Skip to content

Commit

Permalink
usage: spec compliant name lookup of uses in parameter initializer
Browse files Browse the repository at this point in the history
Also fixes lookup of types that are shadowed inside the function body.

Fixes: #40
  • Loading branch information
ajafff committed Apr 1, 2018
1 parent 0f408a7 commit a3550cd
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 62 deletions.
10 changes: 10 additions & 0 deletions test/rules/usage/class.ts.lint
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,13 @@ type T = any;
class Foo<T, U> {}
~~~ [Unused]
~ [Unused]

class WithTypeParameter<T> {
~~~~~~~~~~~~~~~~~ [Unused]
method<U = T>(one: U, two: T) {
~~~ [Unused]
~~~ [Unused]
type T = any;
~ [Unused]
}
}
29 changes: 28 additions & 1 deletion test/rules/usage/function-expression.ts.lint
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
export {};

(function foo(bar: typeof foo){
~~~ [Unused]
var foo: number;
~~~ [Unused]
return bar;
})(1);

Expand Down Expand Up @@ -49,3 +49,30 @@ let bar = 2;
~~~ [Unused]
return bar;
})();

{
let bas = 1;
(function({foo = bas}) {
var bas = 2;
~~~ [Unused]
return foo;
})({});
}

{
let bas = 1;
(foo = bas) => {
var bas = 1;
~~~ [Unused]
return foo;
}
}

{
let bas = 1;
(foo = () => bas) => {
var bas = 1;
~~~ [Unused]
return foo;
}
}
22 changes: 18 additions & 4 deletions test/rules/usage/typeof.ts.lint
Original file line number Diff line number Diff line change
@@ -1,23 +1,37 @@
let bar, baz, bas, p1, p2, p3;
~~~ [Unused]
~~~ [Unused]
~~ [Unused]
~~ [Unused]
~~ [Unused]
function fn(foo: typeof bar, bar: typeof baz, baz: typeof bas, p1 = p2, p2 = 2, p3 = 3, p4 = p3, p5: typeof quux) {
~~~ [Unused]
~~ [Unused]
~~ [Unused]
~~ [Unused]
~~ [Unused]
let bas: boolean;
~~~ [Unused]
var quux: string;
~~~~ [Unused]
}

function fn2(foo: typeof bar) {
function fn2(foo: typeof bar): typeof bar {
~~~ [Unused]
let bas: number;
var bar: typeof bas;
~~~ [Unused]
}

let foo: number;
~~~ [Unused]
function fn3({foo}: {foo: string}): typeof foo {
}

type T = string;

function fn4(param: T) {
type T = number;
~ [Unused]
return param;
}

export {fn, fn2};
export {fn, fn2, fn3, fn4};
68 changes: 11 additions & 57 deletions util/usage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ abstract class AbstractScope implements Scope {
for (const use of this._uses)
if (!this._applyUse(use))
this._addUseToParent(use);
this._uses = [];
}

protected _applyUse(use: VariableUse, variables = this._variables): boolean {
Expand Down Expand Up @@ -348,7 +349,6 @@ class NonRootScope extends AbstractScope {
class EnumScope extends NonRootScope {
public end() {
this._applyUses();
this._uses = [];
}
}

Expand All @@ -373,51 +373,9 @@ class ConditionalTypeScope extends NonRootScope {
}
}

const enum FunctionScopeState {
Initial,
Parameter,
ReturnType,
Body,
}

class FunctionScope extends NonRootScope {
private _innerScope = new NonRootScope(this);
private _state = FunctionScopeState.Initial;

public end(cb: VariableCallback) {
this._innerScope.end(cb);
super.end(cb);
}

public updateState(newState: FunctionScopeState) {
this._state = newState;
}

public addUse(use: VariableUse, source?: Scope) {
if (source === this._innerScope)
return void this._uses.push(use);
switch (this._state) {
case FunctionScopeState.Parameter:
if ((use.domain & UsageDomain.Value) === 0 || use.domain & UsageDomain.TypeQuery)
return void this._uses.push(use);
if (this._applyUse(use) || this._applyUse(use, this._innerScope.getVariables()))
return;
break;
case FunctionScopeState.ReturnType:
if (this._applyUse(use))
return;
break;
case FunctionScopeState.Body:
return this._innerScope.addUse(use);
case FunctionScopeState.Initial:
return void this._uses.push(use);

}
return this._parent.addUse(use, this);
}

protected _getDestinationScope(blockScoped: boolean): Scope {
return blockScoped ? this._innerScope : this;
public beginBody() {
this._applyUses();
}
}

Expand Down Expand Up @@ -469,8 +427,8 @@ class FunctionExpressionScope extends AbstractNamedExpressionScope<FunctionScope
super(name, DeclarationDomain.Value, parent);
}

public updateState(newState: FunctionScopeState) {
return this._innerScope.updateState(newState);
public beginBody() {
return this._innerScope.beginBody();
}
}

Expand Down Expand Up @@ -545,7 +503,6 @@ class NamespaceScope extends NonRootScope {
});
this._applyUses();
this._innerScope = new NonRootScope(this);
this._uses = [];
}

public createOrReuseNamespaceScope(name: string, exported: boolean, ambient: boolean, hasExportStatement: boolean): NamespaceScope {
Expand Down Expand Up @@ -650,7 +607,7 @@ class UsageWalker {
if (node.parent!.kind !== ts.SyntaxKind.IndexSignature &&
((<ts.ParameterDeclaration>node).name.kind !== ts.SyntaxKind.Identifier ||
(<ts.Identifier>(<ts.NamedDeclaration>node).name).originalKeywordKind !== ts.SyntaxKind.ThisKeyword))
this._handleBindingName(<ts.Identifier>(<ts.NamedDeclaration>node).name, false, false, true);
this._handleBindingName(<ts.Identifier>(<ts.NamedDeclaration>node).name, false, false);
break;
case ts.SyntaxKind.EnumMember:
this._scope.addVariable(
Expand All @@ -666,7 +623,7 @@ class UsageWalker {
case ts.SyntaxKind.TypeParameter:
this._scope.addVariable(
getIdentifierText((<ts.TypeParameterDeclaration>node).name),
(<ts.TypeParameterDeclaration>node).name, false,
(<ts.TypeParameterDeclaration>node).name, true,
false,
DeclarationDomain.Type,
);
Expand Down Expand Up @@ -738,14 +695,11 @@ class UsageWalker {
cb(node.name);
if (node.typeParameters !== undefined)
node.typeParameters.forEach(cb);
scope.updateState(FunctionScopeState.Parameter);
node.parameters.forEach(cb);
if (node.type !== undefined) {
scope.updateState(FunctionScopeState.ReturnType);
if (node.type !== undefined)
cb(node.type);
}
if (node.body !== undefined) {
scope.updateState(FunctionScopeState.Body);
scope.beginBody();
cb(node.body);
}
scope.end(varCb);
Expand Down Expand Up @@ -796,12 +750,12 @@ class UsageWalker {
hasModifier(node.modifiers, ts.SyntaxKind.ExportKeyword), domain);
}

private _handleBindingName(name: ts.BindingName, blockScoped: boolean, exported: boolean, isParameter?: boolean) {
private _handleBindingName(name: ts.BindingName, blockScoped: boolean, exported: boolean) {
if (name.kind === ts.SyntaxKind.Identifier)
return this._scope.addVariable(getIdentifierText(name), name, blockScoped, exported, DeclarationDomain.Value);
forEachDestructuringIdentifier(name, (declaration) => {
this._scope.addVariable(
getIdentifierText(declaration.name), declaration.name, isParameter || blockScoped, exported, DeclarationDomain.Value,
getIdentifierText(declaration.name), declaration.name, blockScoped, exported, DeclarationDomain.Value,
);
});
}
Expand Down

0 comments on commit a3550cd

Please sign in to comment.