Skip to content

bug - formatter rewrites tuple-target list comprehensions into invalid syntax #615

@dannymeijer

Description

@dannymeijer

Area

  • Compiler (frontend/backend/codegen)
  • Tooling (CLI/formatter/test runner)

Summary

Expected: incan fmt must not rewrite valid source into syntax that incan build rejects. Tuple-unpack targets in list comprehensions should either remain in a parser-accepted form or the parser should accept the formatter-normalized form.

Actual: on incan 0.3.0-rc1, an unformatted tuple-target list comprehension builds successfully, but incan fmt rewrites for idx, value in ... to for (idx, value) in .... The formatted output is then rejected by incan build with Expected identifier, found Punctuation(LParen).

This is related to closed issue #483, but it is a new current behavior shape. #483 reported rejection of the original for idx, value in ... comprehension form. The current RC1 compiler accepts that original form; the defect is now formatter/parser disagreement after formatting.

Reproduction steps

  1. Create <scratch-path>:
def labels(values: list[str]) -> list[str]:
    """Build indexed labels from one string list."""
    return [f"{idx}:{value}" for idx, value in enumerate(values)]


def main() -> None:
    """Exercise tuple-unpack targets inside a list comprehension."""
    result = labels(["a", "b"])
    assert result[0] == "0:a"
    assert result[1] == "1:b"
  1. Verify the original source builds:
incan build <scratch-path> <scratch-path>
  1. Format the file:
incan fmt <scratch-path>

The formatter rewrites the comprehension to:

return [f"{idx}:{value}" for (idx, value) in enumerate(values)]
  1. Build the formatted file:
incan build <scratch-path> <scratch-path>

Output / logs

Original source build succeeds:

Generated Rust project in: <scratch-path>
Building...
✓ Build successful!
Binary: <scratch-path>

Formatter output:

Formatted: <scratch-path>

✓ 1 file(s) formatted, 0 error(s)

Build after formatting fails:

syntax error: Expected identifier, found Punctuation(LParen)
  --> <scratch-path>
  |
3 |     return [f"{idx}:{value}" for (idx, value) in enumerate(values)]
  |                                  ^

Original InQL trigger was src/prism/output_columns.incn on InQL PR dannys-code-corner/InQL#29. The InQL branch uses an explicit loop as a non-blocking workaround so the PR can continue without relying on formatter-invalid output.

Environment

OS: macOS / zsh local development environment
Rust: rustc 1.96.0-nightly (362211dc2 2026-03-24)
Incan: incan 0.3.0-rc1
Incan commit: 939ff58e0013fb342c256205ad83383f21626d44
Commands:

incan --version
incan fmt <scratch-path>
incan build <scratch-path> <scratch-path>

Blocking / workaround status

Non-blocking for InQL PR #29 because the comprehension can be written as an explicit loop without changing semantics:

mut output_columns: list[str] = []
for idx, expr in enumerate(group_columns):
    output_columns.append(scalar_expr_output_name(expr, f"group_{idx}"))
return output_columns

The workaround should not replace this bug report. The formatter and parser need to agree on tuple-unpack targets in comprehension clauses.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingincan compilerSuggestions, features, or bugs related to the Compiler (frontend/backend/codegen)toolingSuggestions, features, or bugs related to the Tooling (CLI/formatter/test runner)

    Type

    No fields configured for Bug.

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions