[clang-format] BlockIndent misindents nested break inside cast expressions
- 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
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