llvm / llvm/llvm-project

[clang-format] BlockIndent misindents nested break inside cast expressions

Open
#220,327 1 comment 0 reactions 0 assignees View on GitHub
clang-format
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

## Summary

With `AlignAfterOpenBracket: BlockIndent`, a line break that lands inside a cast expression's argument gets its continuation indent computed from the cast's opening-paren column (the `Align` formula), while the closing paren follows the `BlockIndent` rule — two inconsistent rules within one expression.

## Repro

clang-format 23.1.0 (also reproduces on 19.1.7), minimal config:

```yaml
BasedOnStyle: LLVM
ColumnLimit: 90
IndentWidth: 2
ContinuationIndentWidth: 2
AlignAfterOpenBracket: BlockIndent
BreakBeforeBinaryOperators: NonAssignment
```

Input:

```cpp
void f() {
auto const nowMs = static_cast(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count());
(void)nowMs;
}
```

Actual output:

```cpp
void f() {
auto const nowMs =
static_cast(std::chrono::duration_cast(
std::chrono::system_clock::now().time_since_epoch()
)
.count());
(void)nowMs;
}
```

The argument is indented to column 33 = cast open-paren column (31) + `ContinuationIndentWidth` (2), i.e. the `Align` formula; the closing paren sits at indent 4, i.e. the `BlockIndent` rule. Expected under BlockIndent: either break after the cast's own paren, or indent the nested argument by continuation levels (6 = 2 + 2 + 2).

## Scope

- Reproduces with `static_cast`, `reinterpret_cast` and C-style casts (`(T)(...)`).
- Identical nesting with a plain function call (`wrap(...)`) or a functional cast (`std::uint64_t(...)`) formats correctly — the cast code path is what differs.
- Not caused by any single option: removing `PenaltyBreakOpenParenthesis`, `BinPackArguments: false`, `AllowAllArgumentsOnNextLine: false` or `AlignOperands: DontAlign` individually leaves the output unchanged.
- Not a recent regression: 19.1.7 and 23.1.0 behave the same.

Contributor guide

Open the contributing guide

Research direction

Start by running the provided clang-format 23.1.0 reproducer with the minimal YAML configuration and compare the cast-expression path with the equivalent plain function-call path. Investigate the clang-format handling of static_cast, reinterpret_cast, and C-style cast arguments. Done means nested cast arguments and closing parentheses follow consistent BlockIndent continuation levels without regressing the listed cast forms.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
65/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.