Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(language-service): improve non-callable error message #35271

Closed
wants to merge 5 commits into from

Conversation

ayazhafiz
Copy link
Member

This commit improves the context of a non-callable function error
message by providing the affected call target and its non-callable type.

PR Checklist

Please check if your PR fulfills the following requirements:

PR Type

What kind of change does this PR introduce?

  • Feature

Does this PR introduce a breaking change?

  • Yes
  • No

@ayazhafiz ayazhafiz added feature Issue that requests a new feature area: language-service Issues related to Angular's VS Code language service target: major This PR is targeted for the next major release labels Feb 9, 2020
@ayazhafiz ayazhafiz requested a review from kyliau February 9, 2020 20:31
@ngbot ngbot bot added this to the needsTriage milestone Feb 9, 2020
@ayazhafiz
Copy link
Member Author

One thing we should start evaluating a design for is isolating error messages in a separate (json) file with unique IDs, descriptions, and even examples. Many modern language tools like tsserver, Rust's compiler, and Shellcheck follow this model. It will also provide a natural way for us to support enabling/disabling particular error messages as users have requested.

@kyliau
Copy link
Contributor

kyliau commented Feb 11, 2020

One thing we should start evaluating a design for is isolating error messages in a separate (json) file with unique IDs, descriptions, and even examples. Many modern language tools like tsserver, Rust's compiler, and Shellcheck follow this model. It will also provide a natural way for us to support enabling/disabling particular error messages as users have requested.

In Ivy, each error already has a unique ID, but description is still generated on the fly.
It's good idea to pull them out into JSON.

https://github.com/angular/angular/blob/master/packages/compiler-cli/src/ngtsc/diagnostics/src/error_code.ts

packages/language-service/test/diagnostics_spec.ts Outdated Show resolved Hide resolved
@@ -197,15 +197,18 @@ export class AstType implements AstVisitor {
const args = ast.args.map(arg => this.getType(arg));
const target = this.getType(ast.target !);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ast.target is of type AST here, but really it's a MethodCall AST, of which the receiver is an ImplicitReceiver.
The MethodCall AST contains the name of the function call, so it looks like a better fix here is to make a safe cast to retrieve the name.

Alternatively, we could fix the typings upstream. Right now, many parameters are just typed AST. They should have been more specific.

If we go with the cast, we'll have to account for the other case which is a PropertyRead receiver. Please see next comment =)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, you're right. I'll see if we can type the ast specially.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem with pulling out the name is it only contains the name for the most local AST. So for something like title.toUpperCase()(), the property read AST name would only give us toUpperCase, and we would have to continue backing up and reconstruct what we think the source code looks like to get title.toUpperCase(). For this reason, I think it's better to parse out the source code from the AST span.

@ayazhafiz
Copy link
Member Author

ptal @kyliau

const analyzer = new AstType(scope, this.info.query, {event});
private diagnoseExpression(ast: AST, offset: number, inEvent: boolean) {
const scope = this.getExpressionScope(this.path, inEvent);
const analyzer = new AstType(scope, this.info.query, {inEvent}, this.info.source);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't have to pass in the entire template, because the AST we got from the TemplateAst is actually ASTWithSource, so we already have the expression string here. This info combined with the span of the expression node should be sufficient for us to retrieve the string of interest.
Before we do that though, it might be better to update the type for all ASTs in template_ast.ts from AST to ASTWithSource. Please let me know if you need more clarification, thank you!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this will work, because AstType may visit child nodes of the AST, which may not be ASTWithSources. We could pass the source of just the expression, but I am weary of this it would require using more relative spans. What do you think?

ayazhafiz pushed a commit to ayazhafiz/angular that referenced this pull request Mar 6, 2020
TemplateAst values are currently typed as the base class AST, but they
are actually constructed with ASTWithSource. Type them as such, because
ASTWithSource gives more information about the consumed expression AST
to downstream customers (namely, the expression AST source).

Unblocks angular#35271
matsko pushed a commit that referenced this pull request Mar 9, 2020
TemplateAst values are currently typed as the base class AST, but they
are actually constructed with ASTWithSource. Type them as such, because
ASTWithSource gives more information about the consumed expression AST
to downstream customers (namely, the expression AST source).

Unblocks #35271

PR Close #35892
@ayazhafiz ayazhafiz requested a review from kyliau March 11, 2020 01:30
@@ -360,6 +362,8 @@ export class AstType implements AstVisitor {
return this.resolvePropertyRead(this.query.getNonNullableType(this.getType(ast.receiver)), ast);
}

private sourceOf(ast: AST): string { return this.source.substring(ast.span.start, ast.span.end); }
Copy link
Contributor

@kyliau kyliau Mar 11, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You brought up a very good point in https://github.com/angular/angular/pull/35271/files#r390700195
Could you please double check that ast.span here is relative to the whole template?
I think it might be relative to the expression in the template.
In the test case that you wrote in packages/language-service/test/diagnostics_spec.ts the expression is the template itself so this works. Could you please test this with a template that has multiple expressions?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call. Fixed to use absolute spans.

@ayazhafiz ayazhafiz added the action: merge The PR is ready for merge by the caretaker label Mar 17, 2020
@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Apr 17, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
action: merge The PR is ready for merge by the caretaker area: language-service Issues related to Angular's VS Code language service cla: yes feature Issue that requests a new feature target: major This PR is targeted for the next major release
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants