From 1d3aae6f9257ff34eb38100c3d4ed96fa65d9a96 Mon Sep 17 00:00:00 2001 From: Keen Yee Liau Date: Tue, 12 Nov 2019 17:40:56 -0800 Subject: [PATCH] fix(language-service): Function alias should be callable (#33782) This commit fixes a long standing bug whereby a template variable that gets initialized to a class method gets resolved to the Any type, thus when it is called the language service produces error "Member X is not callable". PR closes https://github.com/angular/angular/issues/16643 PR closes https://github.com/angular/vscode-ng-language-service/issues/234 PR Close #33782 --- .../src/expression_diagnostics.ts | 28 ++----------------- .../language-service/test/diagnostics_spec.ts | 10 +++++++ 2 files changed, 12 insertions(+), 26 deletions(-) diff --git a/packages/language-service/src/expression_diagnostics.ts b/packages/language-service/src/expression_diagnostics.ts index 36c7a34600..8acc2db926 100644 --- a/packages/language-service/src/expression_diagnostics.ts +++ b/packages/language-service/src/expression_diagnostics.ts @@ -90,29 +90,6 @@ function getDefinitionOf(info: DiagnosticTemplateInfo, ast: TemplateAst): Defini } } -/** - * Resolve the specified `variable` from the `directives` list and return the - * corresponding symbol. If resolution fails, return the `any` type. - * @param variable template variable to resolve - * @param directives template context - * @param query - */ -function findSymbolForVariableInDirectives( - variable: VariableAst, directives: DirectiveAst[], query: SymbolQuery): Symbol { - for (const d of directives) { - // Get the symbol table for the directive's StaticSymbol - const table = query.getTemplateContext(d.directive.type.reference); - if (!table) { - continue; - } - const symbol = table.get(variable.value); - if (symbol) { - return symbol; - } - } - return query.getBuiltinType(BuiltinType.Any); -} - /** * Resolve all variable declarations in a template by traversing the specified * `path`. @@ -126,9 +103,8 @@ function getVarDeclarations( if (!(current instanceof EmbeddedTemplateAst)) { continue; } - const {directives, variables} = current; - for (const variable of variables) { - let symbol = findSymbolForVariableInDirectives(variable, directives, info.query); + for (const variable of current.variables) { + let symbol = info.members.get(variable.value) || info.query.getBuiltinType(BuiltinType.Any); const kind = info.query.getTypeKind(symbol); if (kind === BuiltinType.Any || kind === BuiltinType.Unbound) { // For special cases such as ngFor and ngIf, the any type is not very useful. diff --git a/packages/language-service/test/diagnostics_spec.ts b/packages/language-service/test/diagnostics_spec.ts index 3b4f6d26a0..edd0ec103a 100644 --- a/packages/language-service/test/diagnostics_spec.ts +++ b/packages/language-service/test/diagnostics_spec.ts @@ -109,6 +109,16 @@ describe('diagnostics', () => { .toBe(`Identifier 'age' is not defined. 'Hero' does not contain such a member`); }); + it('should not report error for variable initialized as class method', () => { + mockHost.override(TEST_TEMPLATE, ` + + + + `); + const diagnostics = ngLS.getDiagnostics(TEST_TEMPLATE); + expect(diagnostics).toEqual([]); + }); + describe('in expression-cases.ts', () => { it('should report access to an unknown field', () => { const diags = ngLS.getDiagnostics(EXPRESSION_CASES).map(d => d.messageText);