Skip to content

Commit 7100712

Browse files
committed
fix(linter/constructor-super): clarify duplicate super diagnostics (#22035)
Clarifies duplicate `super()` diagnostics by labeling the repeated call and the first `super()` call on that path.
1 parent fce5b7c commit 7100712

2 files changed

Lines changed: 31 additions & 26 deletions

File tree

crates/oxc_linter/src/rules/eslint/constructor_super.rs

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,13 @@ fn missing_super_some(span: Span) -> OxcDiagnostic {
2929
.with_label(span)
3030
}
3131

32-
fn duplicate_super(span: Span) -> OxcDiagnostic {
32+
fn duplicate_super(span: Span, first_super_span: Span) -> OxcDiagnostic {
3333
OxcDiagnostic::warn("Unexpected duplicate `super()`.")
3434
.with_help("Remove the duplicate `super()` call")
35-
.with_label(span)
35+
.with_labels([
36+
span.primary_label("This path may call `super()` after it was already called."),
37+
first_super_span.label("`super()` was first called here."),
38+
])
3639
}
3740

3841
#[derive(Clone, Copy)]
@@ -273,9 +276,10 @@ impl Rule for ConstructorSuper {
273276
if super_call_spans.len() > 1 {
274277
let mut sorted_spans = super_call_spans;
275278
sorted_spans.sort_by_key(|s| s.start);
279+
let first_super_span = sorted_spans[0];
276280

277281
for &span in sorted_spans.iter().skip(1) {
278-
ctx.diagnostic(duplicate_super(span));
282+
ctx.diagnostic(duplicate_super(span, first_super_span));
279283
}
280284
}
281285
} else {
@@ -307,9 +311,10 @@ impl Rule for ConstructorSuper {
307311
if has_duplicate && super_call_spans.len() > 1 {
308312
let mut sorted_spans = super_call_spans;
309313
sorted_spans.sort_by_key(|s| s.start);
314+
let first_super_span = sorted_spans[0];
310315

311316
for &span in sorted_spans.iter().skip(1) {
312-
ctx.diagnostic(duplicate_super(span));
317+
ctx.diagnostic(duplicate_super(span, first_super_span));
313318
}
314319
}
315320
}
@@ -432,13 +437,13 @@ impl ConstructorSuper {
432437
record_super(span);
433438
}
434439
}
435-
// Special case: `super() || super()` - report both as duplicate.
440+
// Special case: `super() || super()` - report the RHS as duplicate.
436441
//
437442
// Technically, `super()` returns the constructed instance (truthy),
438443
// so the RHS of `||` won't execute at runtime. However:
439-
// 1. ESLint reports this as duplicate for compatibility
444+
// 1. ESLint reports the RHS as a duplicate for compatibility
440445
// 2. This code pattern is almost certainly a mistake
441-
// 3. The CFG doesn't catch this because it sees only LHS as reachable
446+
// 3. The CFG records the reachable LHS, but not the short-circuited RHS
442447
//
443448
// We intentionally match ESLint's behavior here.
444449
Expression::LogicalExpression(logical)
@@ -457,14 +462,13 @@ impl ConstructorSuper {
457462
let left_span = check_super(&logical.left);
458463
let right_span = check_super(&logical.right);
459464

460-
// Report both super() calls as duplicates if both exist
461-
if left_span.is_some() && right_span.is_some() {
462-
if let Some(span) = left_span {
463-
record_super(span);
464-
}
465-
if let Some(span) = right_span {
466-
record_super(span);
467-
}
465+
// The CFG records the reachable left `super()` call. Add the
466+
// right call so `super() || super()` still matches ESLint's
467+
// duplicate-super behavior.
468+
if left_span.is_some()
469+
&& let Some(span) = right_span
470+
{
471+
record_super(span);
468472
}
469473
}
470474
_ => {}

crates/oxc_linter/src/snapshots/eslint_constructor_super.snap

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -241,35 +241,36 @@ source: crates/oxc_linter/src/tester.rs
241241
eslint(constructor-super): Unexpected duplicate `super()`.
242242
╭─[constructor_super.tsx:1:46]
243243
1class A extends B { constructor() { super(); super(); } }
244-
· ───────
245-
╰────
246-
help: Remove the duplicate `super()` call
247-
248-
eslint(constructor-super): Unexpected duplicate `super()`.
249-
╭─[constructor_super.tsx:1:37]
250-
1class A extends B { constructor() { super() || super(); } }
251-
· ───────
244+
· ───┬─── ───┬───
245+
· │ ╰── This path may call `super()` after it was already called.
246+
· ╰── `super()` was first called here.
252247
╰────
253248
help: Remove the duplicate `super()` call
254249

255250
eslint(constructor-super): Unexpected duplicate `super()`.
256251
╭─[constructor_super.tsx:1:48]
257252
1class A extends B { constructor() { super() || super(); } }
258-
· ───────
253+
· ───┬─── ───┬───
254+
· │ ╰── This path may call `super()` after it was already called.
255+
· ╰── `super()` was first called here.
259256
╰────
260257
help: Remove the duplicate `super()` call
261258

262259
eslint(constructor-super): Unexpected duplicate `super()`.
263260
╭─[constructor_super.tsx:1:53]
264261
1class A extends B { constructor() { if (a) super(); super(); } }
265-
· ───────
262+
· ───┬─── ───┬───
263+
· │ ╰── This path may call `super()` after it was already called.
264+
· ╰── `super()` was first called here.
266265
╰────
267266
help: Remove the duplicate `super()` call
268267

269268
eslint(constructor-super): Unexpected duplicate `super()`.
270269
╭─[constructor_super.tsx:1:76]
271270
1class A extends B { constructor() { switch (a) { case 0: super(); default: super(); } } }
272-
· ───────
271+
· ───┬─── ───┬───
272+
· │ ╰── This path may call `super()` after it was already called.
273+
· ╰── `super()` was first called here.
273274
╰────
274275
help: Remove the duplicate `super()` call
275276

0 commit comments

Comments
 (0)