{"record":{"id":"21a791c772adde2f","repo":"astral-sh/ruff","slug":"expected-indented-block-to-have-at-least-one-state","errorCode":null,"errorMessage":"Expected indented block to have at least one statement","messagePattern":"Expected indented block to have at least one statement","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"info","filePath":"crates/ruff_linter/src/rules/flake8_simplify/rules/collapsible_if.rs","lineNumber":355,"sourceCode":"        Cow::Owned(format!(\n            \"def f():{}{module_text}\",\n            stylist.line_ending().as_str()\n        ))\n    };\n\n    // Parse the CST.\n    let mut tree = match_statement(&module_text)?;\n\n    let statement = if outer_indent.is_empty() {\n        &mut tree\n    } else {\n        let embedding = match_function_def(&mut tree)?;\n\n        let indented_block = match_indented_block(&mut embedding.body)?;\n        indented_block.indent = Some(outer_indent);\n\n        let Some(statement) = indented_block.body.first_mut() else {\n            bail!(\"Expected indented block to have at least one statement\")\n        };\n        statement\n    };\n\n    let outer_if = match_if(statement)?;\n\n    let libcst_native::If {\n        body: libcst_native::Suite::IndentedBlock(outer_body),\n        orelse: None,\n        ..\n    } = outer_if\n    else {\n        bail!(\"Expected outer if to have indented body and no else\")\n    };\n\n    let [\n        libcst_native::Statement::Compound(libcst_native::CompoundStatement::If(\n            inner_if @ libcst_native::If { orelse: None, .. },","sourceCodeStart":337,"sourceCodeEnd":373,"githubUrl":"https://github.com/astral-sh/ruff/blob/15f3fe6b15a5f00172f34b0f542f8ea277f5a586/crates/ruff_linter/src/rules/flake8_simplify/rules/collapsible_if.rs#L337-L373","documentation":"During the SIM102 collapse, the outer body is temporarily embedded in a function definition so libcst exposes an `IndentedBlock`; if that block has no statements (`body.first_mut()` is `None`) the fix bails, since a merged `if` needs at least one statement to move under it.","triggerScenarios":"A nested `if a: if b:` where the innermost body is empty or unparseable into a first statement — practically `if` blocks containing only comments or pass-less bodies produced by partial edits.","commonSituations":"Work-in-progress code with empty blocks, code stripped by other tools leaving only comments, or stub files.","solutions":["Ensure the innermost `if` body contains at least one statement (e.g. add `pass` or the real body) and rerun the fix","Collapse the conditions manually into a single `if a and b:`","Suppress SIM102 with `# noqa` until the body is filled in"],"exampleFix":"// before\nif a:\n    if b:\n        pass  # only statement\n// after\nif a and b:\n    pass","handlingStrategy":"validation","validationCode":"python - <<'EOF'\nimport ast, sys\ntree = ast.parse(open(sys.argv[1]).read())\nfor n in ast.walk(tree):\n    if isinstance(n, ast.If) and len(n.body) == 1 and isinstance(n.body[0], ast.If):\n        inner = n.body[0]\n        if len(inner.body) == 0:\n            print('empty nested-if body at line', inner.lineno, '- not SIM102-fixable')\nEOF","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never commit empty `if` bodies; use `pass` explicitly or fill in the real body first","Check for blocks containing only comments before running SIM102 autofixes","Run autofixes after tests pass so partial/broken bodies don't reach the linter"],"tags":["ruff","flake8-simplify","sim102","autofix"],"backgroundTag":"linter-autofix-skipped","analyzedSha":"15f3fe6b15a5f00172f34b0f542f8ea277f5a586","analyzedAt":"2026-09-05T10:32:37.492Z","contentChangedAt":"2026-09-05T10:32:37.492Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}