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
This commit is contained in:
parent
ab0bcee144
commit
1d3aae6f92
|
@ -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.
|
||||
|
|
|
@ -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, `
|
||||
<ng-template let-greet="myClick">
|
||||
<span (click)="greet()"></span>
|
||||
</ng-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);
|
||||
|
|
Loading…
Reference in New Issue