From 1c90ef5aad15e9b09a3d8be0d87868347566dc37 Mon Sep 17 00:00:00 2001 From: Kanchalai Tanglertsampan Date: Wed, 15 Mar 2017 19:16:37 -0700 Subject: [PATCH 1/3] wip-fix resolveEntityName with parenthesizedExpression --- src/compiler/checker.ts | 12 +++++++++--- src/compiler/types.ts | 2 +- ...DeclarationWithPropertyAccessInHeritageClause1.ts | 12 ++++++++++++ 3 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 tests/cases/conformance/es6/classDeclaration/emitClassDeclarationWithPropertyAccessInHeritageClause1.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 00498191f2..ab35f15b60 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1511,6 +1511,9 @@ namespace ts { return undefined; } } + else if (name.kind === SyntaxKind.ParenthesizedExpression) { + return getSymbolOfNode(name.expression); + } else { Debug.fail("Unknown entity name kind."); } @@ -21069,7 +21072,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; @@ -21084,9 +21086,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 86895f3db6..0f183c6b1f 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1395,7 +1395,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/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 From 4980fa4d5d7145499f3ebe95b3c06cfe61f451cc Mon Sep 17 00:00:00 2001 From: Kanchalai Tanglertsampan Date: Thu, 16 Mar 2017 13:39:17 -0700 Subject: [PATCH 2/3] Update resolveEntityName to check for non entity name in property access expression --- src/compiler/checker.ts | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ab35f15b60..e90560f9ce 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1493,9 +1493,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; @@ -1512,7 +1526,13 @@ namespace ts { } } else if (name.kind === SyntaxKind.ParenthesizedExpression) { - return getSymbolOfNode(name.expression); + // 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."); @@ -21091,7 +21111,7 @@ namespace ts { return entityNameSymbol; } } - + if (isPartOfExpression(entityName)) { if (nodeIsMissing(entityName)) { // Missing entity name. From aace2c358fc3aac041f19aaac9e9e26a666d68ee Mon Sep 17 00:00:00 2001 From: Kanchalai Tanglertsampan Date: Thu, 16 Mar 2017 13:39:30 -0700 Subject: [PATCH 3/3] Update baselines and add fourslash --- ...tionWithPropertyAccessInHeritageClause1.js | 41 +++++++++++++++++++ ...ithPropertyAccessInHeritageClause1.symbols | 36 ++++++++++++++++ ...nWithPropertyAccessInHeritageClause1.types | 39 ++++++++++++++++++ .../reference/reexportClassDefinition.symbols | 2 + ...rPropertyAccessExpressionHeritageClause.ts | 20 +++++++++ ...nPropertyAccessExpressionHeritageClause.ts | 17 ++++++++ ...ePropertyAccessExpressionHeritageClause.ts | 17 ++++++++ 7 files changed, 172 insertions(+) create mode 100644 tests/baselines/reference/emitClassDeclarationWithPropertyAccessInHeritageClause1.js create mode 100644 tests/baselines/reference/emitClassDeclarationWithPropertyAccessInHeritageClause1.symbols create mode 100644 tests/baselines/reference/emitClassDeclarationWithPropertyAccessInHeritageClause1.types create mode 100644 tests/cases/fourslash/findAllReferPropertyAccessExpressionHeritageClause.ts create mode 100644 tests/cases/fourslash/gotoDefinitionPropertyAccessExpressionHeritageClause.ts create mode 100644 tests/cases/fourslash/renamePropertyAccessExpressionHeritageClause.ts 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/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