From fae2769f44df291a6f653d69103da7b00cab7efd Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Mon, 16 Nov 2020 11:22:11 -0800 Subject: [PATCH] refactor(compiler-cli): Add additional shim locations to reference and variable symbols (#39715) Both `ReferenceSymbol` and `VariableSymbol` have two locations of interest to an external consumer. 1. The location for the initializers of the local TCB variables allow consumers to query the TypeScript Language Service for information about the initialized type of the variable. 2. The location of the local variable itself (i.e. `_t1`) allows consumers to query the TypeScript LS for references to that variable from within the template. PR Close #39715 --- .../src/ngtsc/typecheck/api/symbols.ts | 32 ++++++++++++++++--- .../typecheck/src/template_symbol_builder.ts | 28 +++++++++++++--- packages/language-service/ivy/definitions.ts | 7 ++-- .../language-service/ivy/display_parts.ts | 4 ++- packages/language-service/ivy/quick_info.ts | 6 ++-- 5 files changed, 62 insertions(+), 15 deletions(-) diff --git a/packages/compiler-cli/src/ngtsc/typecheck/api/symbols.ts b/packages/compiler-cli/src/ngtsc/typecheck/api/symbols.ts index b23cc76b071..af72642041f 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/api/symbols.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/api/symbols.ts @@ -155,8 +155,24 @@ export interface ReferenceSymbol { */ declaration: TmplAstReference; - /** The location in the shim file of a variable that holds the type of the local ref. */ - shimLocation: ShimLocation; + /** + * The location in the shim file of a variable that holds the type of the local ref. + * For example, a reference declaration like the following: + * ``` + * var _t1 = document.createElement('div'); + * var _t2 = _t1; // This is the reference declaration + * ``` + * This `targetLocation` is `[_t1 variable declaration].getStart()`. + */ + targetLocation: ShimLocation; + + /** + * The location in the TCB for the identifier node in the reference variable declaration. + * For example, given a variable declaration statement for a template reference: + * `var _t2 = _t1`, this location is `[_t2 node].getStart()`. This location can + * be used to find references to the variable within the template. + */ + referenceVarLocation: ShimLocation; } /** @@ -185,8 +201,16 @@ export interface VariableSymbol { */ declaration: TmplAstVariable; - /** The location in the shim file of a variable that holds the type of the template variable. */ - shimLocation: ShimLocation; + /** + * The location in the shim file for the identifier that was declared for the template variable. + */ + localVarLocation: ShimLocation; + + /** + * The location in the shim file for the initializer node of the variable that represents the + * template variable. + */ + initializerLocation: ShimLocation; } /** diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/template_symbol_builder.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/template_symbol_builder.ts index 91cc032ae41..df08a4016f5 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/template_symbol_builder.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/template_symbol_builder.ts @@ -13,7 +13,7 @@ import {AbsoluteFsPath} from '../../file_system'; import {ClassDeclaration} from '../../reflection'; import {ComponentScopeReader} from '../../scope'; import {isAssignment} from '../../util/src/typescript'; -import {DirectiveSymbol, DomBindingSymbol, ElementSymbol, ExpressionSymbol, InputBindingSymbol, OutputBindingSymbol, ReferenceSymbol, Symbol, SymbolKind, TemplateSymbol, TsNodeSymbolInfo, TypeCheckableDirectiveMeta, VariableSymbol} from '../api'; +import {DirectiveSymbol, DomBindingSymbol, ElementSymbol, ExpressionSymbol, InputBindingSymbol, OutputBindingSymbol, ReferenceSymbol, ShimLocation, Symbol, SymbolKind, TemplateSymbol, TsNodeSymbolInfo, TypeCheckableDirectiveMeta, VariableSymbol} from '../api'; import {ExpressionIdentifier, findAllMatchingNodes, findFirstMatchingNode, hasExpressionIdentifier} from './comments'; import {TemplateData} from './context'; @@ -303,7 +303,17 @@ export class SymbolBuilder { return null; } - return {...expressionSymbol, kind: SymbolKind.Variable, declaration: variable}; + return { + tsType: expressionSymbol.tsType, + tsSymbol: expressionSymbol.tsSymbol, + initializerLocation: expressionSymbol.shimLocation, + kind: SymbolKind.Variable, + declaration: variable, + localVarLocation: { + shimPath: this.shimPath, + positionInShimFile: this.getShimPositionForNode(node.name), + } + }; } private getSymbolOfReference(ref: TmplAstReference): ReferenceSymbol|null { @@ -331,13 +341,19 @@ export class SymbolBuilder { return null; } + const referenceVarShimLocation: ShimLocation = { + shimPath: this.shimPath, + positionInShimFile: this.getShimPositionForNode(node), + }; if (target instanceof TmplAstTemplate || target instanceof TmplAstElement) { return { - ...symbol, - tsSymbol: symbol.tsSymbol, kind: SymbolKind.Reference, + tsSymbol: symbol.tsSymbol, + tsType: symbol.tsType, target, declaration: ref, + targetLocation: symbol.shimLocation, + referenceVarLocation: referenceVarShimLocation, }; } else { if (!ts.isClassDeclaration(target.directive.ref.node)) { @@ -345,11 +361,13 @@ export class SymbolBuilder { } return { - ...symbol, kind: SymbolKind.Reference, tsSymbol: symbol.tsSymbol, + tsType: symbol.tsType, declaration: ref, target: target.directive.ref.node, + targetLocation: symbol.shimLocation, + referenceVarLocation: referenceVarShimLocation, }; } } diff --git a/packages/language-service/ivy/definitions.ts b/packages/language-service/ivy/definitions.ts index 5e59d651fe8..de0b8ce78af 100644 --- a/packages/language-service/ivy/definitions.ts +++ b/packages/language-service/ivy/definitions.ts @@ -90,7 +90,8 @@ export class DefinitionBuilder { }); } if (symbol.kind === SymbolKind.Variable) { - definitions.push(...this.getDefinitionsForSymbols(symbol)); + definitions.push( + ...this.getDefinitionsForSymbols({shimLocation: symbol.initializerLocation})); } return definitions; } @@ -135,9 +136,11 @@ export class DefinitionBuilder { return [...bindingDefs, ...directiveDefs]; } case SymbolKind.Reference: + return this.getTypeDefinitionsForSymbols({shimLocation: symbol.targetLocation}); case SymbolKind.Expression: - case SymbolKind.Variable: return this.getTypeDefinitionsForSymbols(symbol); + case SymbolKind.Variable: + return this.getTypeDefinitionsForSymbols({shimLocation: symbol.initializerLocation}); } } diff --git a/packages/language-service/ivy/display_parts.ts b/packages/language-service/ivy/display_parts.ts index aff731fbf62..0844fe6a81f 100644 --- a/packages/language-service/ivy/display_parts.ts +++ b/packages/language-service/ivy/display_parts.ts @@ -56,7 +56,9 @@ export function getDisplayInfo( const displayParts = createDisplayParts( symbol.declaration.name, kind, /* containerName */ undefined, typeChecker.typeToString(symbol.tsType)); - const documentation = getDocumentationFromTypeDefAtLocation(tsLS, symbol.shimLocation); + const documentation = symbol.kind === SymbolKind.Reference ? + getDocumentationFromTypeDefAtLocation(tsLS, symbol.targetLocation) : + getDocumentationFromTypeDefAtLocation(tsLS, symbol.initializerLocation); return { kind, displayParts, diff --git a/packages/language-service/ivy/quick_info.ts b/packages/language-service/ivy/quick_info.ts index 0079bc0e2f2..32d99e65fd5 100644 --- a/packages/language-service/ivy/quick_info.ts +++ b/packages/language-service/ivy/quick_info.ts @@ -80,14 +80,14 @@ export class QuickInfoBuilder { } private getQuickInfoForVariableSymbol(symbol: VariableSymbol): ts.QuickInfo { - const documentation = this.getDocumentationFromTypeDefAtLocation(symbol.shimLocation); + const documentation = this.getDocumentationFromTypeDefAtLocation(symbol.initializerLocation); return createQuickInfo( symbol.declaration.name, DisplayInfoKind.VARIABLE, getTextSpanOfNode(this.node), undefined /* containerName */, this.typeChecker.typeToString(symbol.tsType), documentation); } private getQuickInfoForReferenceSymbol(symbol: ReferenceSymbol): ts.QuickInfo { - const documentation = this.getDocumentationFromTypeDefAtLocation(symbol.shimLocation); + const documentation = this.getDocumentationFromTypeDefAtLocation(symbol.targetLocation); return createQuickInfo( symbol.declaration.name, DisplayInfoKind.REFERENCE, getTextSpanOfNode(this.node), undefined /* containerName */, this.typeChecker.typeToString(symbol.tsType), documentation); @@ -235,4 +235,4 @@ export function createQuickInfo( displayParts, documentation, }; -} \ No newline at end of file +}