Skip to content

Commit f2d1531

Browse files
committed
Fix location for duplicate function implementation errors
Use only the relevant declarations (by collecting them in the for loop), and use `declaration` if `getNameOfDeclaration` didn't work (useful for `export default` with anonymous functions). Fixes #39804. Also, use `nodeIsPresent` once, and a random `?.`.
1 parent 03b6580 commit f2d1531

6 files changed

+56
-6
lines changed

src/compiler/checker.ts

+9-6
Original file line numberDiff line numberDiff line change
@@ -31280,6 +31280,7 @@ namespace ts {
3128031280
let duplicateFunctionDeclaration = false;
3128131281
let multipleConstructorImplementation = false;
3128231282
let hasNonAmbientClass = false;
31283+
const functionDeclarations = [] as Declaration[];
3128331284
for (const current of declarations) {
3128431285
const node = <SignatureDeclaration | ClassDeclaration | ClassExpression>current;
3128531286
const inAmbientContext = node.flags & NodeFlags.Ambient;
@@ -31300,25 +31301,27 @@ namespace ts {
3130031301
}
3130131302

3130231303
if (node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.MethodSignature || node.kind === SyntaxKind.Constructor) {
31304+
functionDeclarations.push(node);
3130331305
const currentNodeFlags = getEffectiveDeclarationFlags(node, flagsToCheck);
3130431306
someNodeFlags |= currentNodeFlags;
3130531307
allNodeFlags &= currentNodeFlags;
3130631308
someHaveQuestionToken = someHaveQuestionToken || hasQuestionToken(node);
3130731309
allHaveQuestionToken = allHaveQuestionToken && hasQuestionToken(node);
31310+
const bodyIsPresent = nodeIsPresent((node as FunctionLikeDeclaration).body);
3130831311

31309-
if (nodeIsPresent((node as FunctionLikeDeclaration).body) && bodyDeclaration) {
31312+
if (bodyIsPresent && bodyDeclaration) {
3131031313
if (isConstructor) {
3131131314
multipleConstructorImplementation = true;
3131231315
}
3131331316
else {
3131431317
duplicateFunctionDeclaration = true;
3131531318
}
3131631319
}
31317-
else if (previousDeclaration && previousDeclaration.parent === node.parent && previousDeclaration.end !== node.pos) {
31320+
else if (previousDeclaration?.parent === node.parent && previousDeclaration.end !== node.pos) {
3131831321
reportImplementationExpectedError(previousDeclaration);
3131931322
}
3132031323

31321-
if (nodeIsPresent((node as FunctionLikeDeclaration).body)) {
31324+
if (bodyIsPresent) {
3132231325
if (!bodyDeclaration) {
3132331326
bodyDeclaration = node as FunctionLikeDeclaration;
3132431327
}
@@ -31336,14 +31339,14 @@ namespace ts {
3133631339
}
3133731340

3133831341
if (multipleConstructorImplementation) {
31339-
forEach(declarations, declaration => {
31342+
forEach(functionDeclarations, declaration => {
3134031343
error(declaration, Diagnostics.Multiple_constructor_implementations_are_not_allowed);
3134131344
});
3134231345
}
3134331346

3134431347
if (duplicateFunctionDeclaration) {
31345-
forEach(declarations, declaration => {
31346-
error(getNameOfDeclaration(declaration), Diagnostics.Duplicate_function_implementation);
31348+
forEach(functionDeclarations, declaration => {
31349+
error(getNameOfDeclaration(declaration) || declaration, Diagnostics.Duplicate_function_implementation);
3134731350
});
3134831351
}
3134931352

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
tests/cases/compiler/exportDefaultInterfaceAndTwoFunctions.ts(2,1): error TS2393: Duplicate function implementation.
2+
tests/cases/compiler/exportDefaultInterfaceAndTwoFunctions.ts(3,1): error TS2393: Duplicate function implementation.
3+
4+
5+
==== tests/cases/compiler/exportDefaultInterfaceAndTwoFunctions.ts (2 errors) ====
6+
export default interface A { a: string; }
7+
export default function() { return 1; }
8+
~~~~~~
9+
!!! error TS2393: Duplicate function implementation.
10+
export default function() { return 2; }
11+
~~~~~~
12+
!!! error TS2393: Duplicate function implementation.
13+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//// [exportDefaultInterfaceAndTwoFunctions.ts]
2+
export default interface A { a: string; }
3+
export default function() { return 1; }
4+
export default function() { return 2; }
5+
6+
7+
//// [exportDefaultInterfaceAndTwoFunctions.js]
8+
"use strict";
9+
exports.__esModule = true;
10+
function default_1() { return 1; }
11+
exports["default"] = default_1;
12+
function default_2() { return 2; }
13+
exports["default"] = default_2;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
=== tests/cases/compiler/exportDefaultInterfaceAndTwoFunctions.ts ===
2+
export default interface A { a: string; }
3+
>A : Symbol(A, Decl(exportDefaultInterfaceAndTwoFunctions.ts, 0, 41), Decl(exportDefaultInterfaceAndTwoFunctions.ts, 1, 39), Decl(exportDefaultInterfaceAndTwoFunctions.ts, 0, 0))
4+
>a : Symbol(A.a, Decl(exportDefaultInterfaceAndTwoFunctions.ts, 0, 28))
5+
6+
export default function() { return 1; }
7+
export default function() { return 2; }
8+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
=== tests/cases/compiler/exportDefaultInterfaceAndTwoFunctions.ts ===
2+
export default interface A { a: string; }
3+
>a : string
4+
5+
export default function() { return 1; }
6+
>1 : 1
7+
8+
export default function() { return 2; }
9+
>2 : 2
10+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default interface A { a: string; }
2+
export default function() { return 1; }
3+
export default function() { return 2; }

0 commit comments

Comments
 (0)