Skip to content

Commit 0cb490e

Browse files
authored
Merge pull request #14692 from Microsoft/master-fixResolveEntityName
[Master] Fix resolve entity name to not dive inside property access expression when the expression is not entity name
2 parents 75e8ba7 + aace2c3 commit 0cb490e

10 files changed

+216
-6
lines changed

src/compiler/checker.ts

+31-5
Original file line numberDiff line numberDiff line change
@@ -1507,9 +1507,23 @@ namespace ts {
15071507
}
15081508
}
15091509
else if (name.kind === SyntaxKind.QualifiedName || name.kind === SyntaxKind.PropertyAccessExpression) {
1510-
const left = name.kind === SyntaxKind.QualifiedName ? (<QualifiedName>name).left : (<PropertyAccessEntityNameExpression>name).expression;
1511-
const right = name.kind === SyntaxKind.QualifiedName ? (<QualifiedName>name).right : (<PropertyAccessExpression>name).name;
1510+
let left: EntityNameOrEntityNameExpression;
15121511

1512+
if (name.kind === SyntaxKind.QualifiedName) {
1513+
left = (<QualifiedName>name).left;
1514+
}
1515+
else if (name.kind === SyntaxKind.PropertyAccessExpression &&
1516+
(name.expression.kind === SyntaxKind.ParenthesizedExpression || isEntityNameExpression(name.expression))) {
1517+
left = name.expression;
1518+
}
1519+
else {
1520+
// If the expression in property-access expression is not entity-name or parenthsizedExpression (e.g. it is a call expression), it won't be able to successfully resolve the name.
1521+
// This is the case when we are trying to do any language service operation in heritage clauses. By return undefined, the getSymbolOfEntityNameOrPropertyAccessExpression
1522+
// will attempt to checkPropertyAccessExpression to resolve symbol.
1523+
// i.e class C extends foo()./*do language service operation here*/B {}
1524+
return undefined;
1525+
}
1526+
const right = name.kind === SyntaxKind.QualifiedName ? name.right : name.name;
15131527
const namespace = resolveEntityName(left, SymbolFlags.Namespace, ignoreErrors, /*dontResolveAlias*/ false, location);
15141528
if (!namespace || nodeIsMissing(right)) {
15151529
return undefined;
@@ -1525,6 +1539,15 @@ namespace ts {
15251539
return undefined;
15261540
}
15271541
}
1542+
else if (name.kind === SyntaxKind.ParenthesizedExpression) {
1543+
// If the expression in parenthsizedExpression is not an entity-name (e.g. it is a call expression), it won't be able to successfully resolve the name.
1544+
// This is the case when we are trying to do any language service operation in heritage clauses. By return undefined, the getSymbolOfEntityNameOrPropertyAccessExpression
1545+
// will attempt to checkPropertyAccessExpression to resolve symbol.
1546+
// i.e class C extends foo()./*do language service operation here*/B {}
1547+
return isEntityNameExpression(name.expression) ?
1548+
resolveEntityName(name.expression as EntityNameOrEntityNameExpression, meaning, ignoreErrors, dontResolveAlias, location) :
1549+
undefined;
1550+
}
15281551
else {
15291552
Debug.fail("Unknown entity name kind.");
15301553
}
@@ -21689,7 +21712,6 @@ namespace ts {
2168921712

2169021713
if (isHeritageClauseElementIdentifier(<EntityName>entityName)) {
2169121714
let meaning = SymbolFlags.None;
21692-
2169321715
// In an interface or class, we're definitely interested in a type.
2169421716
if (entityName.parent.kind === SyntaxKind.ExpressionWithTypeArguments) {
2169521717
meaning = SymbolFlags.Type;
@@ -21704,9 +21726,13 @@ namespace ts {
2170421726
}
2170521727

2170621728
meaning |= SymbolFlags.Alias;
21707-
return resolveEntityName(<EntityName>entityName, meaning);
21729+
const entityNameSymbol = resolveEntityName(<EntityName>entityName, meaning);
21730+
if (entityNameSymbol) {
21731+
return entityNameSymbol;
21732+
}
2170821733
}
21709-
else if (isPartOfExpression(entityName)) {
21734+
21735+
if (isPartOfExpression(entityName)) {
2171021736
if (nodeIsMissing(entityName)) {
2171121737
// Missing entity name.
2171221738
return undefined;

src/compiler/types.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1402,7 +1402,7 @@ namespace ts {
14021402
multiLine?: boolean;
14031403
}
14041404

1405-
export type EntityNameExpression = Identifier | PropertyAccessEntityNameExpression;
1405+
export type EntityNameExpression = Identifier | PropertyAccessEntityNameExpression | ParenthesizedExpression;
14061406
export type EntityNameOrEntityNameExpression = EntityName | EntityNameExpression;
14071407

14081408
export interface PropertyAccessExpression extends MemberExpression, Declaration {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//// [emitClassDeclarationWithPropertyAccessInHeritageClause1.ts]
2+
interface I {}
3+
interface CTor {
4+
new (hour: number, minute: number): I
5+
}
6+
var x: {
7+
B : CTor
8+
};
9+
class B {}
10+
function foo() {
11+
return {B: B};
12+
}
13+
class C extends (foo()).B {}
14+
15+
//// [emitClassDeclarationWithPropertyAccessInHeritageClause1.js]
16+
var __extends = (this && this.__extends) || (function () {
17+
var extendStatics = Object.setPrototypeOf ||
18+
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
19+
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
20+
return function (d, b) {
21+
extendStatics(d, b);
22+
function __() { this.constructor = d; }
23+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
24+
};
25+
})();
26+
var x;
27+
var B = (function () {
28+
function B() {
29+
}
30+
return B;
31+
}());
32+
function foo() {
33+
return { B: B };
34+
}
35+
var C = (function (_super) {
36+
__extends(C, _super);
37+
function C() {
38+
return _super !== null && _super.apply(this, arguments) || this;
39+
}
40+
return C;
41+
}((foo()).B));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
=== tests/cases/conformance/es6/classDeclaration/emitClassDeclarationWithPropertyAccessInHeritageClause1.ts ===
2+
interface I {}
3+
>I : Symbol(I, Decl(emitClassDeclarationWithPropertyAccessInHeritageClause1.ts, 0, 0))
4+
5+
interface CTor {
6+
>CTor : Symbol(CTor, Decl(emitClassDeclarationWithPropertyAccessInHeritageClause1.ts, 0, 14))
7+
8+
new (hour: number, minute: number): I
9+
>hour : Symbol(hour, Decl(emitClassDeclarationWithPropertyAccessInHeritageClause1.ts, 2, 9))
10+
>minute : Symbol(minute, Decl(emitClassDeclarationWithPropertyAccessInHeritageClause1.ts, 2, 22))
11+
>I : Symbol(I, Decl(emitClassDeclarationWithPropertyAccessInHeritageClause1.ts, 0, 0))
12+
}
13+
var x: {
14+
>x : Symbol(x, Decl(emitClassDeclarationWithPropertyAccessInHeritageClause1.ts, 4, 3))
15+
16+
B : CTor
17+
>B : Symbol(B, Decl(emitClassDeclarationWithPropertyAccessInHeritageClause1.ts, 4, 8))
18+
>CTor : Symbol(CTor, Decl(emitClassDeclarationWithPropertyAccessInHeritageClause1.ts, 0, 14))
19+
20+
};
21+
class B {}
22+
>B : Symbol(B, Decl(emitClassDeclarationWithPropertyAccessInHeritageClause1.ts, 6, 2))
23+
24+
function foo() {
25+
>foo : Symbol(foo, Decl(emitClassDeclarationWithPropertyAccessInHeritageClause1.ts, 7, 10))
26+
27+
return {B: B};
28+
>B : Symbol(B, Decl(emitClassDeclarationWithPropertyAccessInHeritageClause1.ts, 9, 12))
29+
>B : Symbol(B, Decl(emitClassDeclarationWithPropertyAccessInHeritageClause1.ts, 6, 2))
30+
}
31+
class C extends (foo()).B {}
32+
>C : Symbol(C, Decl(emitClassDeclarationWithPropertyAccessInHeritageClause1.ts, 10, 1))
33+
>(foo()).B : Symbol(B, Decl(emitClassDeclarationWithPropertyAccessInHeritageClause1.ts, 9, 12))
34+
>foo : Symbol(foo, Decl(emitClassDeclarationWithPropertyAccessInHeritageClause1.ts, 7, 10))
35+
>B : Symbol(B, Decl(emitClassDeclarationWithPropertyAccessInHeritageClause1.ts, 9, 12))
36+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
=== tests/cases/conformance/es6/classDeclaration/emitClassDeclarationWithPropertyAccessInHeritageClause1.ts ===
2+
interface I {}
3+
>I : I
4+
5+
interface CTor {
6+
>CTor : CTor
7+
8+
new (hour: number, minute: number): I
9+
>hour : number
10+
>minute : number
11+
>I : I
12+
}
13+
var x: {
14+
>x : { B: CTor; }
15+
16+
B : CTor
17+
>B : CTor
18+
>CTor : CTor
19+
20+
};
21+
class B {}
22+
>B : B
23+
24+
function foo() {
25+
>foo : () => { B: typeof B; }
26+
27+
return {B: B};
28+
>{B: B} : { B: typeof B; }
29+
>B : typeof B
30+
>B : typeof B
31+
}
32+
class C extends (foo()).B {}
33+
>C : C
34+
>(foo()).B : B
35+
>(foo()) : { B: typeof B; }
36+
>foo() : { B: typeof B; }
37+
>foo : () => { B: typeof B; }
38+
>B : typeof B
39+

tests/baselines/reference/reexportClassDefinition.symbols

+2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ import foo2 = require('./foo2')
44

55
class x extends foo2.x {}
66
>x : Symbol(x, Decl(foo3.ts, 0, 31))
7+
>foo2.x : Symbol(x, Decl(foo2.ts, 2, 10))
78
>foo2 : Symbol(foo2, Decl(foo3.ts, 0, 0))
9+
>x : Symbol(x, Decl(foo2.ts, 2, 10))
810

911

1012
=== tests/cases/conformance/externalModules/foo1.ts ===
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
interface I {}
2+
interface CTor {
3+
new (hour: number, minute: number): I
4+
}
5+
var x: {
6+
B : CTor
7+
};
8+
class B {}
9+
function foo() {
10+
return {B: B};
11+
}
12+
class C extends (foo()).B {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
//// interface I {}
4+
//// interface CTor {
5+
//// new (hour: number, minute: number): I
6+
//// }
7+
//// var x: {
8+
//// B : CTor
9+
//// };
10+
//// class B {}
11+
//// function foo() {
12+
//// return {[|B|]: B};
13+
//// }
14+
//// class C extends (foo()).[|B|] {}
15+
//// class C1 extends foo().[|B|] {}
16+
17+
const [def, ref1, ref2] = test.ranges();
18+
verify.referencesOf(ref1, [def, ref1, ref2]);
19+
verify.referencesOf(ref2, [def, ref1, ref2]);
20+
verify.referencesOf(def, [def, ref1, ref2]);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
//// interface I {}
4+
//// interface CTor {
5+
//// new (hour: number, minute: number): I
6+
//// }
7+
//// var x: {
8+
//// B : CTor
9+
//// };
10+
//// class B {}
11+
//// function foo() {
12+
//// return {/*refB*/B: B};
13+
//// }
14+
//// class C extends (foo())./*B*/B {}
15+
//// class C1 extends foo()./*B1*/B {}
16+
17+
verify.goToDefinition([["B", "refB"], ["B1", "refB"]]);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
//// interface I {}
4+
//// interface CTor {
5+
//// new (hour: number, minute: number): I
6+
//// }
7+
//// var x: {
8+
//// B : CTor
9+
//// };
10+
//// class B {}
11+
//// function foo() {
12+
//// return {[|B|]: B};
13+
//// }
14+
//// class C extends (foo()).[|B|] {}
15+
//// class C1 extends foo().[|B|] {}
16+
17+
verify.rangesAreRenameLocations();

0 commit comments

Comments
 (0)