-
Notifications
You must be signed in to change notification settings - Fork 885
no-shadowed-variable: Rewrite and lint for other shadowed names #2598
Conversation
interface I {} | ||
interface I {} | ||
|
||
// Ignore non-mergeable declarations as the compiler will complain about them anyway |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see a compiler error (excepting the unused variable and implicit any) when I use that code. It's perfectly valid to write (function f1() { let f1: any; return f1; })();
; the function f1
is shadowed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good catch. I added some special handling for named function expressions
private addFailureOnIdentifier(ident: ts.Identifier) { | ||
const failureString = Rule.FAILURE_STRING_FACTORY(ident.text); | ||
this.addFailureAtNode(ident, failureString); | ||
function addToList(map: Map<string, ts.Identifier[]>, name: string, identifiers: ts.Identifier[]) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would make this more generic on T
instead of ts.Identifier
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
adding a type parameter doesn't add anything as this function is always called with the same types
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function might need to be used elsewhere (multimaps show up often), but with a different type of values.
~ [err % ('C')] | ||
namespace C { | ||
~ [err % ('C')] | ||
interface C<C> {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
now that we're linting for more than just shadowed variables, I'm tempted to change the rule name in the next major release to no-shadowed-name
...
for now, can we at least distinguish the failure messages? users might get confused if they see "shadowed variable" for an interface name or a type parameter. let's use Shadowed variable: x
for variables and Shadowed name: y
for everything else
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@adidahiya I changed the error message to always show Shadowed name: x
.
Showing a different error message for variables is not really possible:
- We're not tracking if an identifier is a variable
- Variables could merge with classes, interfaces, namespaces ... are they still considered a variable
- And even if we could distinguish the kind of identifiers, it's not clear when to use which message: if a variable shadows a type? if a type shadows a variable? only if a variable shadows another variable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah good points 👍
Added handling of imports to address #2921 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
overall lgtm
@@ -198,8 +211,10 @@ function constructorsWithArgsParamConst( | |||
derivedCtor: new (...args: any[]) => any, | |||
baseCtors: Array<new (...args: any[]) => any>, | |||
args: any[]): void { | |||
const args = []; | |||
~~~~ [Shadowed variable: 'args'] | |||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why the extra block here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
without the block there would be a compile error because the block scoped variable args
cannot merge with the function scoped parameter args
.
The block is needed here because the rule only handles shadowed names only in different scopes
~ [err % ('C')] | ||
namespace C { | ||
~ [err % ('C')] | ||
interface C<C> {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah good points 👍
So why is this suddenly marking all my classnames as Shadowed Name? Shadowed name: 'SomeClassName' (no-shadowed-variable)
37 | * Some comment.
38 | */
> 39 | export class SomeClassName implements SomeClassToImplement {
| ^
40 | /**
41 | * Some more comments:
C:/<path>/<file>.ts
Shadowed name: 'SomeOtherClassName' (no-shadowed-variable)
43 | * Some comment.
44 | */
> 45 | export class SomeOtherClassName implements SomeClassToImplement {
| ^
46 | /**
47 | * Some more comments:
C:/<path>/<file>.ts
Shadowed name: 'AnotherClassName' (no-shadowed-variable)
39 | * Some comment.
40 | */
> 41 | export class AnotherClassName implements SomeClassToImplement {
| ^
42 | /**
43 | * Some more comments: And if you look at how the classes are setup, it is like this:
I don't see how that would be faulty or shadowed |
@Martinspire For now you can just |
@ajafff but that doesn't work if you have multiple classes and interfaces to export. There can only be one default Also I just found out that we're using it for Typedoc to separate by namespace so its easier to browse (though I also found a plugin to perhaps do that but, but I don't feel like changing so many files because this rule doesn't allow exclusions) |
So for example if we want to keep using namespaces for typedoc, we use something like this:
Inside other files I mostly only need |
PR checklist
import { Foo } from
statement #2921Overview of change:
[enhancement]
no-shadowed-variable
: added checks for other shadowing declarations, e.g. interfaces, classes, type parameters, imports, etc.Fixes: #2921
[bugfix]
no-shadowed-variable
: exemptthis
parameterFixes: #2214
[rule-change]
no-shadowed-variable
no longer fails for declarations in the same scope, e.g.var foo; var foo;
. Use the ruleno-duplicate-variable
to find such errors.Refs: #2597
Is there anything you'd like reviewers to focus on?
The compiler already handles declarations in the same scope pretty well. I don't think we should duplicate its logic if declarations are mergeable (or its error messages if they are not). If the user doesn't want declarations to merge, there's still
no-duplicate-variable
This is my attempt to simplify #2339.
Closes: #2339
/cc @andy-hanson