diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 73ec48c78b..45c5fe0afa 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1507,9 +1507,23 @@ namespace ts { } } else if (name.kind === SyntaxKind.QualifiedName || name.kind === SyntaxKind.PropertyAccessExpression) { - const left = name.kind === SyntaxKind.QualifiedName ? (name).left : (name).expression; - const right = name.kind === SyntaxKind.QualifiedName ? (name).right : (name).name; + let left: EntityNameOrEntityNameExpression; + if (name.kind === SyntaxKind.QualifiedName) { + left = (name).left; + } + else if (name.kind === SyntaxKind.PropertyAccessExpression && + (name.expression.kind === SyntaxKind.ParenthesizedExpression || isEntityNameExpression(name.expression))) { + left = name.expression; + } + else { + // 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. + // This is the case when we are trying to do any language service operation in heritage clauses. By return undefined, the getSymbolOfEntityNameOrPropertyAccessExpression + // will attempt to checkPropertyAccessExpression to resolve symbol. + // i.e class C extends foo()./*do language service operation here*/B {} + return undefined; + } + const right = name.kind === SyntaxKind.QualifiedName ? name.right : name.name; const namespace = resolveEntityName(left, SymbolFlags.Namespace, ignoreErrors, /*dontResolveAlias*/ false, location); if (!namespace || nodeIsMissing(right)) { return undefined; @@ -1525,6 +1539,15 @@ namespace ts { return undefined; } } + else if (name.kind === SyntaxKind.ParenthesizedExpression) { + // 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. + // This is the case when we are trying to do any language service operation in heritage clauses. By return undefined, the getSymbolOfEntityNameOrPropertyAccessExpression + // will attempt to checkPropertyAccessExpression to resolve symbol. + // i.e class C extends foo()./*do language service operation here*/B {} + return isEntityNameExpression(name.expression) ? + resolveEntityName(name.expression as EntityNameOrEntityNameExpression, meaning, ignoreErrors, dontResolveAlias, location) : + undefined; + } else { Debug.fail("Unknown entity name kind."); } @@ -21689,7 +21712,6 @@ namespace ts { if (isHeritageClauseElementIdentifier(entityName)) { let meaning = SymbolFlags.None; - // In an interface or class, we're definitely interested in a type. if (entityName.parent.kind === SyntaxKind.ExpressionWithTypeArguments) { meaning = SymbolFlags.Type; @@ -21704,9 +21726,13 @@ namespace ts { } meaning |= SymbolFlags.Alias; - return resolveEntityName(entityName, meaning); + const entityNameSymbol = resolveEntityName(entityName, meaning); + if (entityNameSymbol) { + return entityNameSymbol; + } } - else if (isPartOfExpression(entityName)) { + + if (isPartOfExpression(entityName)) { if (nodeIsMissing(entityName)) { // Missing entity name. return undefined; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index d945af89a2..10bfcfdb40 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1402,7 +1402,7 @@ namespace ts { multiLine?: boolean; } - export type EntityNameExpression = Identifier | PropertyAccessEntityNameExpression; + export type EntityNameExpression = Identifier | PropertyAccessEntityNameExpression | ParenthesizedExpression; export type EntityNameOrEntityNameExpression = EntityName | EntityNameExpression; export interface PropertyAccessExpression extends MemberExpression, Declaration { diff --git a/tests/baselines/reference/emitClassDeclarationWithPropertyAccessInHeritageClause1.js b/tests/baselines/reference/emitClassDeclarationWithPropertyAccessInHeritageClause1.js new file mode 100644 index 0000000000..0c2f79bec0 --- /dev/null +++ b/tests/baselines/reference/emitClassDeclarationWithPropertyAccessInHeritageClause1.js @@ -0,0 +1,41 @@ +//// [emitClassDeclarationWithPropertyAccessInHeritageClause1.ts] +interface I {} +interface CTor { + new (hour: number, minute: number): I +} +var x: { + B : CTor +}; +class B {} +function foo() { + return {B: B}; +} +class C extends (foo()).B {} + +//// [emitClassDeclarationWithPropertyAccessInHeritageClause1.js] +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +var x; +var B = (function () { + function B() { + } + return B; +}()); +function foo() { + return { B: B }; +} +var C = (function (_super) { + __extends(C, _super); + function C() { + return _super !== null && _super.apply(this, arguments) || this; + } + return C; +}((foo()).B)); diff --git a/tests/baselines/reference/emitClassDeclarationWithPropertyAccessInHeritageClause1.symbols b/tests/baselines/reference/emitClassDeclarationWithPropertyAccessInHeritageClause1.symbols new file mode 100644 index 0000000000..f5f4f6ff87 --- /dev/null +++ b/tests/baselines/reference/emitClassDeclarationWithPropertyAccessInHeritageClause1.symbols @@ -0,0 +1,36 @@ +=== tests/cases/conformance/es6/classDeclaration/emitClassDeclarationWithPropertyAccessInHeritageClause1.ts === +interface I {} +>I : Symbol(I, Decl(emitClassDeclarationWithPropertyAccessInHeritageClause1.ts, 0, 0)) + +interface CTor { +>CTor : Symbol(CTor, Decl(emitClassDeclarationWithPropertyAccessInHeritageClause1.ts, 0, 14)) + + new (hour: number, minute: number): I +>hour : Symbol(hour, Decl(emitClassDeclarationWithPropertyAccessInHeritageClause1.ts, 2, 9)) +>minute : Symbol(minute, Decl(emitClassDeclarationWithPropertyAccessInHeritageClause1.ts, 2, 22)) +>I : Symbol(I, Decl(emitClassDeclarationWithPropertyAccessInHeritageClause1.ts, 0, 0)) +} +var x: { +>x : Symbol(x, Decl(emitClassDeclarationWithPropertyAccessInHeritageClause1.ts, 4, 3)) + + B : CTor +>B : Symbol(B, Decl(emitClassDeclarationWithPropertyAccessInHeritageClause1.ts, 4, 8)) +>CTor : Symbol(CTor, Decl(emitClassDeclarationWithPropertyAccessInHeritageClause1.ts, 0, 14)) + +}; +class B {} +>B : Symbol(B, Decl(emitClassDeclarationWithPropertyAccessInHeritageClause1.ts, 6, 2)) + +function foo() { +>foo : Symbol(foo, Decl(emitClassDeclarationWithPropertyAccessInHeritageClause1.ts, 7, 10)) + + return {B: B}; +>B : Symbol(B, Decl(emitClassDeclarationWithPropertyAccessInHeritageClause1.ts, 9, 12)) +>B : Symbol(B, Decl(emitClassDeclarationWithPropertyAccessInHeritageClause1.ts, 6, 2)) +} +class C extends (foo()).B {} +>C : Symbol(C, Decl(emitClassDeclarationWithPropertyAccessInHeritageClause1.ts, 10, 1)) +>(foo()).B : Symbol(B, Decl(emitClassDeclarationWithPropertyAccessInHeritageClause1.ts, 9, 12)) +>foo : Symbol(foo, Decl(emitClassDeclarationWithPropertyAccessInHeritageClause1.ts, 7, 10)) +>B : Symbol(B, Decl(emitClassDeclarationWithPropertyAccessInHeritageClause1.ts, 9, 12)) + diff --git a/tests/baselines/reference/emitClassDeclarationWithPropertyAccessInHeritageClause1.types b/tests/baselines/reference/emitClassDeclarationWithPropertyAccessInHeritageClause1.types new file mode 100644 index 0000000000..0747772ef6 --- /dev/null +++ b/tests/baselines/reference/emitClassDeclarationWithPropertyAccessInHeritageClause1.types @@ -0,0 +1,39 @@ +=== tests/cases/conformance/es6/classDeclaration/emitClassDeclarationWithPropertyAccessInHeritageClause1.ts === +interface I {} +>I : I + +interface CTor { +>CTor : CTor + + new (hour: number, minute: number): I +>hour : number +>minute : number +>I : I +} +var x: { +>x : { B: CTor; } + + B : CTor +>B : CTor +>CTor : CTor + +}; +class B {} +>B : B + +function foo() { +>foo : () => { B: typeof B; } + + return {B: B}; +>{B: B} : { B: typeof B; } +>B : typeof B +>B : typeof B +} +class C extends (foo()).B {} +>C : C +>(foo()).B : B +>(foo()) : { B: typeof B; } +>foo() : { B: typeof B; } +>foo : () => { B: typeof B; } +>B : typeof B + diff --git a/tests/baselines/reference/reexportClassDefinition.symbols b/tests/baselines/reference/reexportClassDefinition.symbols index 5213365798..ba1ead2fbc 100644 --- a/tests/baselines/reference/reexportClassDefinition.symbols +++ b/tests/baselines/reference/reexportClassDefinition.symbols @@ -4,7 +4,9 @@ import foo2 = require('./foo2') class x extends foo2.x {} >x : Symbol(x, Decl(foo3.ts, 0, 31)) +>foo2.x : Symbol(x, Decl(foo2.ts, 2, 10)) >foo2 : Symbol(foo2, Decl(foo3.ts, 0, 0)) +>x : Symbol(x, Decl(foo2.ts, 2, 10)) === tests/cases/conformance/externalModules/foo1.ts === diff --git a/tests/cases/conformance/es6/classDeclaration/emitClassDeclarationWithPropertyAccessInHeritageClause1.ts b/tests/cases/conformance/es6/classDeclaration/emitClassDeclarationWithPropertyAccessInHeritageClause1.ts new file mode 100644 index 0000000000..e22bd2351c --- /dev/null +++ b/tests/cases/conformance/es6/classDeclaration/emitClassDeclarationWithPropertyAccessInHeritageClause1.ts @@ -0,0 +1,12 @@ +interface I {} +interface CTor { + new (hour: number, minute: number): I +} +var x: { + B : CTor +}; +class B {} +function foo() { + return {B: B}; +} +class C extends (foo()).B {} \ No newline at end of file diff --git a/tests/cases/fourslash/findAllReferPropertyAccessExpressionHeritageClause.ts b/tests/cases/fourslash/findAllReferPropertyAccessExpressionHeritageClause.ts new file mode 100644 index 0000000000..57fa235479 --- /dev/null +++ b/tests/cases/fourslash/findAllReferPropertyAccessExpressionHeritageClause.ts @@ -0,0 +1,20 @@ +/// + +//// interface I {} +//// interface CTor { +//// new (hour: number, minute: number): I +//// } +//// var x: { +//// B : CTor +//// }; +//// class B {} +//// function foo() { +//// return {[|B|]: B}; +//// } +//// class C extends (foo()).[|B|] {} +//// class C1 extends foo().[|B|] {} + +const [def, ref1, ref2] = test.ranges(); +verify.referencesOf(ref1, [def, ref1, ref2]); +verify.referencesOf(ref2, [def, ref1, ref2]); +verify.referencesOf(def, [def, ref1, ref2]); \ No newline at end of file diff --git a/tests/cases/fourslash/gotoDefinitionPropertyAccessExpressionHeritageClause.ts b/tests/cases/fourslash/gotoDefinitionPropertyAccessExpressionHeritageClause.ts new file mode 100644 index 0000000000..d998c545fb --- /dev/null +++ b/tests/cases/fourslash/gotoDefinitionPropertyAccessExpressionHeritageClause.ts @@ -0,0 +1,17 @@ +/// + +//// interface I {} +//// interface CTor { +//// new (hour: number, minute: number): I +//// } +//// var x: { +//// B : CTor +//// }; +//// class B {} +//// function foo() { +//// return {/*refB*/B: B}; +//// } +//// class C extends (foo())./*B*/B {} +//// class C1 extends foo()./*B1*/B {} + +verify.goToDefinition([["B", "refB"], ["B1", "refB"]]); \ No newline at end of file diff --git a/tests/cases/fourslash/renamePropertyAccessExpressionHeritageClause.ts b/tests/cases/fourslash/renamePropertyAccessExpressionHeritageClause.ts new file mode 100644 index 0000000000..477008e7a3 --- /dev/null +++ b/tests/cases/fourslash/renamePropertyAccessExpressionHeritageClause.ts @@ -0,0 +1,17 @@ +/// + +//// interface I {} +//// interface CTor { +//// new (hour: number, minute: number): I +//// } +//// var x: { +//// B : CTor +//// }; +//// class B {} +//// function foo() { +//// return {[|B|]: B}; +//// } +//// class C extends (foo()).[|B|] {} +//// class C1 extends foo().[|B|] {} + +verify.rangesAreRenameLocations(); \ No newline at end of file