llvm / llvm/llvm-project

[clang-format] Lambda body left unformatted when a sibling field is a nested designated initializer

Open
#201,736 2 comments 0 reactions 0 assignees View on GitHub
clang-format confirmed
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

### Summary

When a brace-initializer list contains **both** (a) a field whose value is a
*nested designated initializer* and (b) a sibling field whose value is a *lambda
with a multi-statement body*, clang-format silently skips formatting the entire
lambda body. The body is left exactly as written and wrong indentation is not
corrected, `==` spacing is not applied, brace wrapping is not applied. The rest
of the file formats normally.

This is order-independent (the lambda may come before or after the nested
designated initializer) and reproduces on well-formed, valid C++ (it is not a
"fixing invalid code" edge case).

### Version

clang-format version 22.1.2.

### `.clang-format` (minimal)

```yaml
BasedOnStyle: WebKit
ColumnLimit: 120
```

`ColumnLimit` must be non-zero. WebKit's default `ColumnLimit: 0` masks the bug
(no line ever wraps). Any non-zero value (80, 120, 200) triggers it.

### Input (well-formed; only the lambda body is mis-indented)

```cpp
struct Layout {
int a;
int b;
};
struct Source {
Layout layout;
int count;
void (*fn)();
};
Source make()
{
return Source{
.layout = Layout{
.a = 1,
.b = 2,
},
.count = 3,
.fn = []() {
int x = 0;
x = x + 1;
},
};
}
```

### Command

```
clang-format --style=file repro.cpp
```

### Actual output (lambda body unchanged: bug)

```cpp
.fn = []() {
int x = 0;
x = x + 1;
},
```

Running clang-format a second time does not fix it: the wrong output is stable
(idempotent), so it is not a convergence/pass-ordering artifact.

### Expected output

```cpp
.fn = []() {
int x = 0;
x = x + 1;
},
```

### Minimal trigger (both elements required)

| Case | Nested designated-init sibling | Lambda sibling | Result |
| ---- | ---------------------------------------- | -------------- | ------- |
| A | `.layout = Layout{.a=1, .b=2}` | yes | **bug** |
| B | none (`.layout = 9`) | yes | ok |
| C | `.layout = Layout{.a=1, .b=2}` | no | ok |
| F | nested **non**-designated `Layout{1, 2}` | yes | ok |
| G | lambda first, nested designated second | yes | **bug** |

Removing either element, or making the nested initializer non-designated, makes
the lambda body format correctly (rows B/C/F are direct controls).

### Tracker search

I searched the issue tracker and found no matching report. The nearest existing
issue is #119688 ("Misaligned formatting of array of objects with designated
initializers"), but that is about column *alignment* of designated initializers,
not about an adjacent lambda body being skipped entirely.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.