astral-sh/ruff · warning
Compound statement cannot be inlined
Error message
Compound statement cannot be inlined
What it means
Fix-generation failure in the useless-else-on-loop (PLW0120) fix for multi-statement `else` bodies. When the `else` body contains a compound/indented block, the fixer must dedent the body to the loop's own indentation level; it obtains that level from the enclosing `for`/`while` statement. If `indentation()` returns None for the loop statement (irregular layout around the loop keyword), the dedent cannot be computed and the fix aborts.
Source
Thrown at crates/ruff_linter/src/rules/pylint/rules/useless_else_on_loop.rs:157
) -> Result<Fix> {
let Some(start) = orelse.first() else {
return Err(anyhow::anyhow!("Empty `else` clause"));
};
let Some(end) = orelse.last() else {
return Err(anyhow::anyhow!("Empty `else` clause"));
};
let start_indentation = indentation(locator.contents(), start);
if start_indentation.is_none() {
// Inline `else` block (e.g., `else: x = 1`).
Ok(Fix::safe_edit(Edit::deletion(
else_range.start(),
start.start(),
)))
} else {
// Identify the indentation of the loop itself (e.g., the `while` or `for`).
let Some(desired_indentation) = indentation(locator.contents(), stmt) else {
return Err(anyhow::anyhow!("Compound statement cannot be inlined"));
};
// Dedent the content from the end of the `else` to the end of the loop.
let indented = adjust_indentation(
TextRange::new(
locator.full_line_end(else_range.start()),
locator.full_line_end(end.end()),
),
desired_indentation,
locator,
indexer,
stylist,
)?;
// Replace the content from the start of the `else` to the end of the loop.
Ok(Fix::safe_edit(Edit::replacement(
indented,
locator.line_start(else_range.start()),View on GitHub (pinned to 26f38c119c)
Solutions
- Normalize indentation of the loop and its `else` block (same style, loop keyword on its own line), then rerun the fix
- Remove the loop `else` manually and move its statements after the loop (or inline the single-statement case)
- Run `ruff format` on the file before applying fixes
Example fix
# before (fix fails: loop indentation undetectable)
for x in xs: # irregular leading whitespace
f()
else:
g()
h()
# after (dedented after removal)
for x in xs:
f()
g()
h() Defensive patterns
Strategy: try-catch
Validate before calling
import re
def loop_indent_detectable(source: str) -> bool:
# loop keyword at line start with plain leading whitespace
return re.search(r"^[ \t]*(for|while)\b", source, re.M) is not None Try / catch
match ruff.fix(path) {
Ok(fixes) => apply(fixes),
Err(e) if e.to_string().contains("Compound statement cannot be inlined") => {
// report PLW0120 without autofix; suggest manual cleanup
}
Err(e) => return Err(e),
} Prevention
- Keep `for`/`while` keywords at consistent indentation
- Avoid multi-statement else bodies; move code after the loop instead
- Run the formatter before autofixing irregular files
When it happens
Trigger: `remove_else` handles an `else` body that is not inline (start_indentation is None path), then fails to detect the indentation of the enclosing `for`/`while` statement — e.g. the loop keyword is not at the start of an indentable line, tabs/spaces are mixed, or the statement layout is non-standard.
Common situations: Autofixing loops with an `else` block containing multiple statements in files with unusual or mixed indentation, or generated code; users see PLW0120 reported but not fixed by `ruff --fix`.
Related errors
- `else` is expected to be on its own line
- indented block to start with indentation
- Empty `else` clause
- Compound statement cannot be inlined
- {value:?} cannot be converted to EmptyStringCmpOp
AI-assisted analysis of astral-sh/ruff@26f38c119c (2026-09-05).
Data as JSON: /api/errors/1aeb23de0846035a.
Report an issue: GitHub.