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:
@@ -16,6 +16,7 @@ import type {
|
||||
import type { Pos, Position } from "../util/location";
|
||||
import { isStrictBindReservedWord } from "../util/identifier";
|
||||
import { NodeUtils } from "./node";
|
||||
import { type BindingTypes, BIND_NONE, BIND_OUTSIDE } from "../util/scopeflags";
|
||||
|
||||
export default class LValParser extends NodeUtils {
|
||||
// Forward-declaration: defined in expression.js
|
||||
@@ -262,7 +263,7 @@ export default class LValParser extends NodeUtils {
|
||||
elts.push(this.parseAssignableListItemTypes(this.parseRest()));
|
||||
this.checkCommaAfterRest(
|
||||
close,
|
||||
this.state.inFunction && this.state.inParameters
|
||||
this.scope.inFunction && this.state.inParameters
|
||||
? "parameter"
|
||||
: "element",
|
||||
);
|
||||
@@ -325,7 +326,7 @@ export default class LValParser extends NodeUtils {
|
||||
|
||||
checkLVal(
|
||||
expr: Expression,
|
||||
isBinding: ?boolean,
|
||||
bindingType: ?BindingTypes = BIND_NONE,
|
||||
checkClashes: ?{ [key: string]: boolean },
|
||||
contextDescription: string,
|
||||
): void {
|
||||
@@ -337,7 +338,7 @@ export default class LValParser extends NodeUtils {
|
||||
) {
|
||||
this.raise(
|
||||
expr.start,
|
||||
`${isBinding ? "Binding" : "Assigning to"} '${
|
||||
`${bindingType === BIND_NONE ? "Assigning to" : "Binding"} '${
|
||||
expr.name
|
||||
}' in strict mode`,
|
||||
);
|
||||
@@ -358,15 +359,20 @@ export default class LValParser extends NodeUtils {
|
||||
const key = `_${expr.name}`;
|
||||
|
||||
if (checkClashes[key]) {
|
||||
this.raise(expr.start, "Argument name clash in strict mode");
|
||||
this.raise(expr.start, "Argument name clash");
|
||||
} else {
|
||||
checkClashes[key] = true;
|
||||
}
|
||||
}
|
||||
if (bindingType !== BIND_NONE && bindingType !== BIND_OUTSIDE) {
|
||||
this.scope.declareName(expr.name, bindingType, expr.start);
|
||||
}
|
||||
break;
|
||||
|
||||
case "MemberExpression":
|
||||
if (isBinding) this.raise(expr.start, "Binding member expression");
|
||||
if (bindingType !== BIND_NONE) {
|
||||
this.raise(expr.start, "Binding member expression");
|
||||
}
|
||||
break;
|
||||
|
||||
case "ObjectPattern":
|
||||
@@ -374,7 +380,7 @@ export default class LValParser extends NodeUtils {
|
||||
if (prop.type === "ObjectProperty") prop = prop.value;
|
||||
this.checkLVal(
|
||||
prop,
|
||||
isBinding,
|
||||
bindingType,
|
||||
checkClashes,
|
||||
"object destructuring pattern",
|
||||
);
|
||||
@@ -386,7 +392,7 @@ export default class LValParser extends NodeUtils {
|
||||
if (elem) {
|
||||
this.checkLVal(
|
||||
elem,
|
||||
isBinding,
|
||||
bindingType,
|
||||
checkClashes,
|
||||
"array destructuring pattern",
|
||||
);
|
||||
@@ -397,21 +403,26 @@ export default class LValParser extends NodeUtils {
|
||||
case "AssignmentPattern":
|
||||
this.checkLVal(
|
||||
expr.left,
|
||||
isBinding,
|
||||
bindingType,
|
||||
checkClashes,
|
||||
"assignment pattern",
|
||||
);
|
||||
break;
|
||||
|
||||
case "RestElement":
|
||||
this.checkLVal(expr.argument, isBinding, checkClashes, "rest element");
|
||||
this.checkLVal(
|
||||
expr.argument,
|
||||
bindingType,
|
||||
checkClashes,
|
||||
"rest element",
|
||||
);
|
||||
break;
|
||||
|
||||
default: {
|
||||
const message =
|
||||
(isBinding
|
||||
? /* istanbul ignore next */ "Binding invalid"
|
||||
: "Invalid") +
|
||||
(bindingType === BIND_NONE
|
||||
? "Invalid"
|
||||
: /* istanbul ignore next */ "Binding invalid") +
|
||||
" left-hand side" +
|
||||
(contextDescription
|
||||
? " in " + contextDescription
|
||||
|
||||
Reference in New Issue
Block a user