Introduce scope tracking in the parser (#9493)

* Introduce scope tracking

* Fix tests

* Add new tests

* Remove constructor-super check from transform as it is now in parser

* Correctly handle class properties and class scope

* Fix duplicate name check

* Convert scope identifier storage to array

* Enter a new scope in typescript module blocks

* Add test for duplicate declaration

* Rename error for duplicate exports

* Treat class declarations as lexical declaration

* Update whitelist

* Add tests

* Fix scope tracking for function declarations

* Migrate try-catch duplicate error

* Fix test

* More tests

* One more test

* Make scope a separate class and fix review comments

* Do not allow new.target in top scope arrow function

* Correctly enter new scope for declare module and treat type aliases as lexical declarations

* Tests for typescript scope tracking to not mark type aliases as duplicate

* Fix flow scope tracking

* Remove ident from test names as redundant

* Add test case for var and function

* Improve error messages

* Improve literal regex
This commit is contained in:
Daniel Tschinder
2019-02-25 11:04:52 -08:00
committed by GitHub
parent 918f149a63
commit a7391144b3
284 changed files with 5904 additions and 1842 deletions

View File

@@ -9,6 +9,12 @@ import type State from "../tokenizer/state";
import { types as tc } from "../tokenizer/context";
import * as charCodes from "charcodes";
import { isIteratorStart } from "../util/identifier";
import {
type BindingTypes,
BIND_NONE,
BIND_LEXICAL,
SCOPE_OTHER,
} from "../util/scopeflags";
const reservedTypes = [
"any",
@@ -257,6 +263,8 @@ export default (superClass: Class<Parser>): Class<Parser> =>
flowParseDeclareModule(node: N.FlowDeclareModule): N.FlowDeclareModule {
this.next();
this.scope.enter(SCOPE_OTHER);
if (this.match(tt.string)) {
node.id = this.parseExprAtom();
} else {
@@ -290,6 +298,9 @@ export default (superClass: Class<Parser>): Class<Parser> =>
body.push(bodyNode);
}
this.scope.exit();
this.expect(tt.braceR);
this.finishNode(bodyNode, "BlockStatement");
@@ -518,6 +529,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
flowParseTypeAlias(node: N.FlowTypeAlias): N.FlowTypeAlias {
node.id = this.flowParseRestrictedIdentifier();
this.scope.declareName(node.id.name, BIND_LEXICAL, node.id.start);
if (this.isRelational("<")) {
node.typeParameters = this.flowParseTypeParameterDeclaration();
@@ -537,6 +549,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
): N.FlowOpaqueType {
this.expectContextual("type");
node.id = this.flowParseRestrictedIdentifier(/*liberal*/ true);
this.scope.declareName(node.id.name, BIND_LEXICAL, node.id.start);
if (this.isRelational("<")) {
node.typeParameters = this.flowParseTypeParameterDeclaration();
@@ -1761,7 +1774,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
"arrow function parameters",
);
// Use super's method to force the parameters to be checked
super.checkFunctionNameAndParams(node, true);
super.checkParams(node, false, true);
} else {
arrows.push(node);
}
@@ -1995,14 +2008,14 @@ export default (superClass: Class<Parser>): Class<Parser> =>
checkLVal(
expr: N.Expression,
isBinding: ?boolean,
bindingType: ?BindingTypes = BIND_NONE,
checkClashes: ?{ [key: string]: boolean },
contextDescription: string,
): void {
if (expr.type !== "TypeCastExpression") {
return super.checkLVal(
expr,
isBinding,
bindingType,
checkClashes,
contextDescription,
);
@@ -2047,6 +2060,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
isGenerator: boolean,
isAsync: boolean,
isConstructor: boolean,
allowsDirectSuper: boolean,
): void {
if ((method: $FlowFixMe).variance) {
this.unexpected((method: $FlowFixMe).variance.start);
@@ -2064,6 +2078,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
isGenerator,
isAsync,
isConstructor,
allowsDirectSuper,
);
}
@@ -2217,7 +2232,12 @@ export default (superClass: Class<Parser>): Class<Parser> =>
? this.flowParseRestrictedIdentifier(true)
: this.parseIdentifier();
this.checkLVal(specifier.local, true, undefined, contextDescription);
this.checkLVal(
specifier.local,
BIND_LEXICAL,
undefined,
contextDescription,
);
node.specifiers.push(this.finishNode(specifier, type));
}
@@ -2327,12 +2347,17 @@ export default (superClass: Class<Parser>): Class<Parser> =>
);
}
this.checkLVal(specifier.local, true, undefined, "import specifier");
this.checkLVal(
specifier.local,
BIND_LEXICAL,
undefined,
"import specifier",
);
node.specifiers.push(this.finishNode(specifier, "ImportSpecifier"));
}
// parse function type parameters - function foo<T>() {}
parseFunctionParams(node: N.Function): void {
parseFunctionParams(node: N.Function, allowModifiers?: boolean): void {
// $FlowFixMe
const kind = node.kind;
if (kind !== "get" && kind !== "set" && this.isRelational("<")) {
@@ -2340,7 +2365,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
/* allowDefault */ false,
);
}
super.parseFunctionParams(node);
super.parseFunctionParams(node, allowModifiers);
}
// parse flow type annotations on variable declarator heads - let foo: string = bar
@@ -2519,8 +2544,9 @@ export default (superClass: Class<Parser>): Class<Parser> =>
}
}
checkFunctionNameAndParams(
checkParams(
node: N.Function,
allowDuplicates: boolean,
isArrowFunction: ?boolean,
): void {
if (
@@ -2530,7 +2556,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
return;
}
return super.checkFunctionNameAndParams(node, isArrowFunction);
return super.checkParams(node, allowDuplicates, isArrowFunction);
}
parseParenAndDistinguishExpression(canBeArrow: boolean): N.Expression {