Skip to content

Comments

feat(compiler-cli): add extended diagnostics to check for component v…#48658

Closed
robertIsaac wants to merge 2 commits intoangular:mainfrom
robertIsaac:feat/compiler-cli/extended-diagnostics/component-variable-shadow-template-reference/45227
Closed

feat(compiler-cli): add extended diagnostics to check for component v…#48658
robertIsaac wants to merge 2 commits intoangular:mainfrom
robertIsaac:feat/compiler-cli/extended-diagnostics/component-variable-shadow-template-reference/45227

Conversation

@robertIsaac
Copy link
Contributor

@robertIsaac robertIsaac commented Jan 7, 2023

…ariable shadowing template reference

for example

@COMPONENT({
  template: '<div #var1></div>'
})
export class FooComponent {
  var1: string;
}

fixes #45227

right now this goes without error, but it can lead to mistakenly inside the template to use the template reference as if it was the component variable

PR Checklist

Please check if your PR fulfills the following requirements:

PR Type

What kind of change does this PR introduce?

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • CI related changes
  • Documentation content changes
  • angular.io application / infrastructure changes
  • Other... Please describe:

What is the current behavior?

Issue Number: 45227

What is the new behavior?

it will show warning when there is shadowing between component variable and template reference

Does this PR introduce a breaking change?

  • Yes
  • No

I guess it's not, because it's a warning not an error, but I'm leaving it for the Angular team to decide

to fix the warning, simply rename the component variable or the template reference

Other information

this will show the warning even if the component variable is private, which in my opinion is correct
but in case the Angular team disagree, I will make it only show the warning in case of protect or public variables only

@pullapprove pullapprove bot requested a review from AndrewKushnir January 7, 2023 13:40
@angular-robot angular-robot bot added the detected: feature PR contains a feature commit label Jan 7, 2023
@robertIsaac robertIsaac force-pushed the feat/compiler-cli/extended-diagnostics/component-variable-shadow-template-reference/45227 branch from e43432c to 10b7a88 Compare January 7, 2023 14:34
@robertIsaac
Copy link
Contributor Author

@jessicajaniuk @AndrewKushnir
why google-internal-tests is failing? I can't see the details of this

@AndrewKushnir AndrewKushnir requested a review from JoostK January 9, 2023 18:52
@AndrewKushnir AndrewKushnir added action: review The PR is still awaiting reviews from at least one requested reviewer target: minor This PR is targeted for the next minor release area: compiler Issues related to `ngc`, Angular's template compiler compiler: extended diagnostics labels Jan 9, 2023
@ngbot ngbot bot modified the milestone: Backlog Jan 9, 2023
Copy link
Member

@JoostK JoostK left a comment

Choose a reason for hiding this comment

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

There's a few more cases that such check would need to take into account:

  1. Inherited properties will not cause the message to be reported.
  2. Template scopes can shadow ancestor template variables:
<div *ngFor="let local of []">
  <button #local></button>
</div>

Here, #local shadows the let-local declaration of the ng-template element that is created by the structural ngFor directive.

Copy link
Contributor

@AndrewKushnir AndrewKushnir left a comment

Choose a reason for hiding this comment

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

@robertIsaac thanks for the PR 👍

I've left some comments, including additional information about build failures from the internal tests run.

Copy link
Contributor

Choose a reason for hiding this comment

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

The deep link here is the reason for the internal test failures. Could you please update it to avoid the deep link?

Suggested change
import {Reference} from '@angular/compiler/src/render3/r3_ast';
import {TmplAstReference as Reference} from '@angular/compiler';

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I see there is more imports from src/render3/r3_ast, should I fix them as well?

Copy link
Contributor

Choose a reason for hiding this comment

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

This line is also causing some issues, could you please update it as well?

Suggested change
import ts, {Identifier} from 'typescript';
import ts from 'typescript';

and use ts.Identifier in the code below.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

Copy link
Contributor

Choose a reason for hiding this comment

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

There are 3 other build errors, presumably related to the lack of type-narrowing for the node (potentially related to the deep linking issue above):

packages/compiler-cli/src/ngtsc/typecheck/extended/checks/component_variable_shadow_template_reference/index.ts:32:36 - error TS2339: Property 'name' does not exist on type 'Node | AST'.
  Property 'name' does not exist on type 'Node'.

32     const templateReference = node.name;
                                      ~~~~
packages/compiler-cli/src/ngtsc/typecheck/extended/checks/component_variable_shadow_template_reference/index.ts:41:9 - error TS2345: Argument of type 'ParseSourceSpan | AbsoluteSourceSpan' is not assignable to parameter of type 'ParseSourceSpan'.
  Type 'AbsoluteSourceSpan' is missing the following properties from type 'ParseSourceSpan': fullStart, details

41         node.sourceSpan,
           ~~~~~~~~~~~~~~~

and an error related to the implicit any type in the following code:

packages/compiler-cli/src/ngtsc/typecheck/extended/checks/component_variable_shadow_template_reference/index.ts:34:31 - error TS7006: Parameter 'member' implicitly has an 'any' type.

34         component.members.map(member => (member.name as Identifier)?.escapedText.toString());
                                 ~~~~~~

Copy link
Contributor Author

Choose a reason for hiding this comment

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

do I need to explicitly provide type to member or what? because in my IDE it shows correctly member: ts.ClassElement

Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
* @COMPONENT({
* @Component({

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
* A variable in the component shadow the same name as a template reference.
* A template reference shadows a component class property or method.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
COMPONENT_VARIABLE_SHADOW_TEMPLATE_REFERENCE = "ComponentVariableShadowTemplateReference",
COMPONENT_VARIABLE_SHADOWS_TEMPLATE_REFERENCE = "ComponentVariableShadowsTemplateReference",

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I renamed everything to be shadows

@robertIsaac robertIsaac force-pushed the feat/compiler-cli/extended-diagnostics/component-variable-shadow-template-reference/45227 branch 3 times, most recently from 53b6526 to c1e974a Compare January 9, 2023 21:19
@robertIsaac
Copy link
Contributor Author

  1. Inherited properties will not cause the message to be reported.

so members of the component return only direct members? is there anyway to get all the members of a class?

  1. Template scopes can shadow ancestor template variables:
<div *ngFor="let local of []">
  <button #local></button>
</div>

Here, #local shadows the let-local declaration of the ng-template element that is created by the structural ngFor directive.

oh I missed this when I was solving the solution, will see what I can do about it tomorrow

@robertIsaac
Copy link
Contributor Author

@JoostK I found making componentVariables to equal (component.parent as unknown as {identifiers: Map<string, string>}).identifiers.keys(); solves the first problem
but there is two problems

  1. the type of parent is node which shouldn't have identifiers
  2. it will also forbid using the class names of the component and its superclasses
    any suggestions here?

@JoostK
Copy link
Member

JoostK commented Jan 9, 2023

so members of the component return only direct members? is there anyway to get all the members of a class?

Indeed, members is a property of the parse tree so it corresponds with syntactical items of the class. You'll need a ts.TypeChecker to obtain all properties for a given declaration (I'm on mobile atm and don't know how to get to that, not which methods to then use, by heart)

@robertIsaac
Copy link
Contributor Author

You'll need a ts.TypeChecker to obtain all properties for a given declaration (I'm on mobile atm and don't know how to get to that, not which methods to then use, by heart)

@JoostK I don't get what you mean here, TypeChecker is interface
anyway I found another way to do it, can you take a look now

@robertIsaac robertIsaac force-pushed the feat/compiler-cli/extended-diagnostics/component-variable-shadow-template-reference/45227 branch from 7d2df67 to 4a79c47 Compare January 10, 2023 13:34
@robertIsaac
Copy link
Contributor Author

robertIsaac commented Jan 10, 2023

@JoostK I made test cases to check how actually angular will work with edge cases
https://stackblitz.com/edit/angular-ivy-jnn75s

and it seems things are actually more complicated than what I originally thought
the template reference has scope for structural directives tree, but if there is none then it's global for the template

so the acceptance criteria from my opinion is:

  1. any template reference shouldn't shadow any component variable (that's done in this PR)
  2. any two template references can shadow each other as long as they have different scopes - inside different structural directive elements (that's the current behavior and we need to maintain it)
  3. global references shouldn't shallow each others (not done)
  4. scoped references shouldn't shallow global ones (not done)
  5. inside same scope, no two reference should shallow each other (not done)

do you think there is more edge cases I should consider?

@robertIsaac robertIsaac force-pushed the feat/compiler-cli/extended-diagnostics/component-variable-shadow-template-reference/45227 branch from 4a79c47 to c4fa18c Compare January 10, 2023 14:40
@robertIsaac
Copy link
Contributor Author

@AndrewKushnir @JoostK any update about this PR?

@JeanMeche
Copy link
Member

JeanMeche commented Jul 3, 2023

Can you rebase your branch to fix the conflicts ? (Error 8108 is now taken).

@robertIsaac robertIsaac force-pushed the feat/compiler-cli/extended-diagnostics/component-variable-shadow-template-reference/45227 branch 2 times, most recently from fc18891 to 99ab9f4 Compare July 3, 2023 15:10
@robertIsaac
Copy link
Contributor Author

Can you rebase your branch to fix the conflicts ? (Error 8108 is now taken).

done

do you plan to accept it?

@jessicajaniuk
Copy link
Contributor

@robertIsaac looks like you have failing status checks. You'll need to resolve those first. One of them is showing that you need to update goldens now that you've added this check.

@JeanMeche
Copy link
Member

(You'll need to run yarn bazel run //packages/compiler-cli:error_code_api.accept for the tests to pass).

@robertIsaac robertIsaac force-pushed the feat/compiler-cli/extended-diagnostics/component-variable-shadow-template-reference/45227 branch from 99ab9f4 to ab8c6c2 Compare July 19, 2023 19:25
@robertIsaac
Copy link
Contributor Author

(You'll need to run yarn bazel run //packages/compiler-cli:error_code_api.accept for the tests to pass).

done

@robertIsaac robertIsaac force-pushed the feat/compiler-cli/extended-diagnostics/component-variable-shadow-template-reference/45227 branch from ab8c6c2 to ae70936 Compare July 20, 2023 10:17
…ariable shadowing template reference

for example
```
@component({
  template: '<div #var1></div>'
})
export class FooComponent {
  var1: string;
}
```

right now this goes without error, but it can lead to mistakenly inside the template to use the template reference as if it was the component variable

Fixes angular#45227
…ariable shadowing template reference

for example
```
@component({
  template: '<div #var1></div>'
})
export class FooComponent {
  var1: string;
}
```

right now this goes without error, but it can lead to mistakenly inside the template to use the template reference as if it was the component variable

Fixes angular#45227
@robertIsaac robertIsaac force-pushed the feat/compiler-cli/extended-diagnostics/component-variable-shadow-template-reference/45227 branch from ae70936 to fd2b2ec Compare September 28, 2023 17:51
@JoostK
Copy link
Member

JoostK commented Sep 25, 2024

We discussed this just now and decided to close this for now, as the PR in its current state is not ready to land:

  1. it uses internal fields of TS AST nodes, which we'd rather avoid as it's brittle w.r.t TS updates, as well as being incomplete w.r.t. inherited fields.
  2. There's no shadowing checks for nested templates (which may shadow variable declarations of enclosing templates)

@JoostK JoostK closed this Sep 25, 2024
@robertIsaac
Copy link
Contributor Author

hi @JoostK thanks for your feedback
but I'm interested to know what should be the proper way to do it
in a previous comment it was mentioned I should use ts.TypeChecker but I never understood how exactly to use it

@JoostK
Copy link
Member

JoostK commented Sep 25, 2024

ts.TypeChecker provides a method getPropertiesOfType to go from a ts.Type to its properties as ts.Symbol. Those symbols also have names, and that result also includes properties from base classes, which wouldn't be part of the class' AST structure. An extended diagnostic has access to the type checker in ctx.typeChecker (with ctx being TemplateContext that is passed as first parameter of TemplateCheck.run).

You'd also have to go from the ts.ClassDeclaration to its ts.Type using ts.TypeChecker.getTypeAtLocation(), arriving at something like the following:

const properties = ctx.typeChecker.getPropertiesOfType(ctx.typeChecker.getTypeAtLocation(component));

@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 Oct 26, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

action: review The PR is still awaiting reviews from at least one requested reviewer area: compiler Issues related to `ngc`, Angular's template compiler compiler: extended diagnostics detected: feature PR contains a feature commit target: minor This PR is targeted for the next minor release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

extended diagnostics: show warning if component variable is shadowed by template reference

5 participants