[clang] [Clang] Fix inverted diagnostic location for duplicate default labels (PR #180447)

Youssed Mohamed Abdul-Sataar via cfe-commits cfe-commits at lists.llvm.org
Thu Feb 12 17:00:44 PST 2026


youssef47048 wrote:

> I meant, in our parsing implementation, where did you find the logic that caused this?

After doing investigation, The root cause was in how the `ParseCaseStatement` and
`ParseDefaultStatement` register themselves on the switch case list.

`ParseCaseStatement` calls `ActOnCaseStmt` (which calls `addSwitchCase`)
**before** parsing its sub-statement, while `ParseDefaultStatement`
calls `ActOnDefaultStmt` (which calls `addSwitchCase`) **after** parsing
its sub-statement.

because `addSwitchCase` is (LIFO), this produce different linked list orderings depending on
whether labels are separated by break(the standard switch case) or fallthrough.

consider this example:
```

void test2(int z) {
  switch(z) {
    default:   // expected-note {{previous case defined here}}
    case 1:
    case 2:
    default: break; // expected-error {{multiple default labels in one switch}}

  }
}

```
Each statement finishes completely beforethe next one starts,
so all three addSwitchCase calls happen in
top-to-bottom order, and the LIFO prepend reverses them.
 The loop in `ActOnFinishSwitchStmt` then sees the second default first and treats
it as `TheDefaultStmt`, emitting the error on the first one which is wrong.

Consider this example also:

```
void f3(int z) {
    switch(z) {
      default: // expected-note {{previous case defined here}}
      case 1:
      default:  // expected-error {{multiple default labels in one switch}}
        break;
    }
}

```
In this case, the parsing is recursive: the outer default's
sub-statement is the next label. `ParseCaseStatement` registers itself
before recursing, and the inner `ParseDefaultStatement` registers after
its sub-statement. The outer default registers last. This produces a
list order that happens to be correct.

The proposed solution is to split `ActOnDefaultStmt` the same way [ `ActOnCaseStmt` /
`ActOnCaseStmtBody`] are already split: create the `DefaultStmt` and add
it to the switch case list immediately after parsing the 'default: `
tokens, then attach the sub-statement afterward via a new
`ActOnDefaultStmtBody`. This makes the registration order consistent
with `ParseCaseStatement` and gives `addSwitchCase` a uniform prepend
order regardless of breaks or fallthroughs.

https://github.com/llvm/llvm-project/pull/180447


More information about the cfe-commits mailing list