Skip to content

Commit

Permalink
Mark static block as FunctionParent (#13832)
Browse files Browse the repository at this point in the history
* refactor: move StaticBlock definition to core

* update generated types

* fix: mark StaticBlock as FunctionParent

* revise testcase

* update babel-types docs
  • Loading branch information
JLHwung committed Oct 11, 2021
1 parent 00f8ee3 commit 49a0d65
Show file tree
Hide file tree
Showing 11 changed files with 251 additions and 83 deletions.
44 changes: 22 additions & 22 deletions packages/babel-traverse/src/path/generated/validators.ts
Expand Up @@ -418,38 +418,38 @@ export interface NodePathValidators {
isWhileStatement(opts?: object): this is NodePath<t.WhileStatement>;
isWithStatement(opts?: object): this is NodePath<t.WithStatement>;
isYieldExpression(opts?: object): this is NodePath<t.YieldExpression>;
isReferencedIdentifier(
opts?: object,
): this is NodePath<VirtualTypeAliases["ReferencedIdentifier"]>;
isReferencedMemberExpression(
opts?: object,
): this is NodePath<VirtualTypeAliases["ReferencedMemberExpression"]>;
isBindingIdentifier(
opts?: object,
): this is NodePath<VirtualTypeAliases["BindingIdentifier"]>;
isStatement(opts?: object): this is NodePath<t.Statement>;
isExpression(opts?: object): this is NodePath<t.Expression>;
isScope(opts?: object): this is NodePath<VirtualTypeAliases["Scope"]>;
isReferenced(opts?: object): boolean;
isBlockScoped(opts?: object): boolean;
isVar(opts?: object): this is NodePath<VirtualTypeAliases["Var"]>;
isUser(opts?: object): boolean;
isExistentialTypeParam(
opts?: object,
): this is NodePath<VirtualTypeAliases["ExistentialTypeParam"]>;
isExpression(opts?: object): this is NodePath<t.Expression>;
isFlow(opts?: object): this is NodePath<t.Flow>;
isForAwaitStatement(
opts?: object,
): this is NodePath<VirtualTypeAliases["ForAwaitStatement"]>;
isGenerated(opts?: object): boolean;
isNumericLiteralTypeAnnotation(
opts?: object,
): this is NodePath<VirtualTypeAliases["NumericLiteralTypeAnnotation"]>;
isPure(opts?: object): boolean;
isFlow(opts?: object): this is NodePath<t.Flow>;
isReferenced(opts?: object): boolean;
isReferencedIdentifier(
opts?: object,
): this is NodePath<VirtualTypeAliases["ReferencedIdentifier"]>;
isReferencedMemberExpression(
opts?: object,
): this is NodePath<VirtualTypeAliases["ReferencedMemberExpression"]>;
isRestProperty(
opts?: object,
): this is NodePath<VirtualTypeAliases["RestProperty"]>;
isScope(opts?: object): this is NodePath<VirtualTypeAliases["Scope"]>;
isSpreadProperty(
opts?: object,
): this is NodePath<VirtualTypeAliases["SpreadProperty"]>;
isExistentialTypeParam(
opts?: object,
): this is NodePath<VirtualTypeAliases["ExistentialTypeParam"]>;
isNumericLiteralTypeAnnotation(
opts?: object,
): this is NodePath<VirtualTypeAliases["NumericLiteralTypeAnnotation"]>;
isForAwaitStatement(
opts?: object,
): this is NodePath<VirtualTypeAliases["ForAwaitStatement"]>;
isStatement(opts?: object): this is NodePath<t.Statement>;
isUser(opts?: object): boolean;
isVar(opts?: object): this is NodePath<VirtualTypeAliases["Var"]>;
}
24 changes: 12 additions & 12 deletions packages/babel-traverse/src/path/generated/virtual-types.ts
Expand Up @@ -5,22 +5,22 @@
import * as t from "@babel/types";

export interface VirtualTypeAliases {
ReferencedIdentifier: t.Identifier | t.JSXIdentifier;
ReferencedMemberExpression: t.MemberExpression;
BindingIdentifier: t.Identifier;
Statement: t.Statement;
Expression: t.Expression;
Scope: t.Scopable | t.Pattern;
Referenced: t.Node;
BlockScoped: t.Node;
Var: t.VariableDeclaration;
User: t.Node;
ExistentialTypeParam: t.ExistsTypeAnnotation;
Expression: t.Expression;
Flow: t.Flow | t.ImportDeclaration | t.ExportDeclaration | t.ImportSpecifier;
ForAwaitStatement: t.ForOfStatement;
Generated: t.Node;
NumericLiteralTypeAnnotation: t.NumberLiteralTypeAnnotation;
Pure: t.Node;
Flow: t.Flow | t.ImportDeclaration | t.ExportDeclaration | t.ImportSpecifier;
Referenced: t.Node;
ReferencedIdentifier: t.Identifier | t.JSXIdentifier;
ReferencedMemberExpression: t.MemberExpression;
RestProperty: t.RestElement;
Scope: t.Scopable | t.Pattern;
SpreadProperty: t.RestElement;
ExistentialTypeParam: t.ExistsTypeAnnotation;
NumericLiteralTypeAnnotation: t.NumberLiteralTypeAnnotation;
ForAwaitStatement: t.ForOfStatement;
Statement: t.Statement;
User: t.Node;
Var: t.VariableDeclaration;
}
167 changes: 167 additions & 0 deletions packages/babel-traverse/test/scope.js
Expand Up @@ -702,4 +702,171 @@ describe("scope", () => {
}
});
});

describe("own bindings", () => {
// Var declarations should be declared in the nearest FunctionParent ancestry
describe("var declarations should be registered", () => {
it("in program", () => {
const program = getPath("var foo;");
expect(program.scope.hasOwnBinding("foo")).toBe(true);
});
it("in function declaration", () => {
const functionDeclaration = getPath("function f() { var foo; }").get(
"body.0.expression",
);
expect(functionDeclaration.scope.hasOwnBinding("foo")).toBe(true);
});
it("in function expression", () => {
const functionExpression = getPath("(function () { var foo; })").get(
"body.0.expression",
);
expect(functionExpression.scope.hasOwnBinding("foo")).toBe(true);
});
it("in arrow expression", () => {
const arrowExpression =
getPath("() => { var foo; }").get("body.0.expression");
expect(arrowExpression.scope.hasOwnBinding("foo")).toBe(true);
});
it("in object method", () => {
const objectMethod = getPath("({ method() { var foo; } })").get(
"body.0.expression.properties.0",
);
expect(objectMethod.scope.hasOwnBinding("foo")).toBe(true);
});
it("in class method", () => {
const classMethod = getPath("(class { method() { var foo; } })").get(
"body.0.expression.body.body.0",
);
expect(classMethod.scope.hasOwnBinding("foo")).toBe(true);
});
it("in class private method", () => {
const classMethod = getPath("(class { #method() { var foo; } })").get(
"body.0.expression.body.body.0",
);
expect(classMethod.scope.hasOwnBinding("foo")).toBe(true);
});
it("in static block", () => {
const staticBlock = getPath("(class { static { var foo; } })", {
plugins: ["classStaticBlock"],
}).get("body.0.expression.body.body.0");
expect(staticBlock.scope.hasOwnBinding("foo")).toBe(true);
});
});
describe("var declarations should not be registered", () => {
it("in block statement", () => {
const blockStatement = getPath("{ var foo; }").get("body.0");
expect(blockStatement.scope.hasOwnBinding("foo")).toBe(false);
});
it("in catch clause", () => {
const catchClause = getPath("try {} catch { var foo; }").get(
"body.0.handler",
);
expect(catchClause.scope.hasOwnBinding("foo")).toBe(false);
});
it("in for-init statement", () => {
const forStatement = getPath("for (var foo;;);").get("body.0");
expect(forStatement.scope.hasOwnBinding("foo")).toBe(false);
});
it("in for-in statement", () => {
const forStatement = getPath("for (var foo in x);").get("body.0");
expect(forStatement.scope.hasOwnBinding("foo")).toBe(false);
});
it("in for-of statement", () => {
const forStatement = getPath("for (var foo of x);").get("body.0");
expect(forStatement.scope.hasOwnBinding("foo")).toBe(false);
});
it("in switch statement", () => {
const switchStatement = getPath("switch (0) { case 0: var foo; }").get(
"body.0",
);
expect(switchStatement.scope.hasOwnBinding("foo")).toBe(false);
});
it("in while statement", () => {
const whileStatement = getPath("while (0) \n var foo;").get("body.0");
expect(whileStatement.scope.hasOwnBinding("foo")).toBe(false);
});
it("in do-while statement", () => {
const doWhileStatement = getPath("do \n var foo \n while(0);").get(
"body.0",
);
expect(doWhileStatement.scope.hasOwnBinding("foo")).toBe(false);
});
});
// Lexical declarations should be registered in the nearest BlockParent ancestry
describe("let declarations should be registered", () => {
it("in program", () => {
const program = getPath("let foo;");
expect(program.scope.hasOwnBinding("foo")).toBe(true);
});
it("in function declaration", () => {
const functionDeclaration = getPath("function f() { let foo; }").get(
"body.0.expression",
);
expect(functionDeclaration.scope.hasOwnBinding("foo")).toBe(true);
});
it("in function expression", () => {
const functionExpression = getPath("(function () { let foo; })").get(
"body.0.expression",
);
expect(functionExpression.scope.hasOwnBinding("foo")).toBe(true);
});
it("in arrow expression", () => {
const arrowExpression =
getPath("() => { let foo; }").get("body.0.expression");
expect(arrowExpression.scope.hasOwnBinding("foo")).toBe(true);
});
it("in object method", () => {
const objectMethod = getPath("({ method() { let foo; } })").get(
"body.0.expression.properties.0",
);
expect(objectMethod.scope.hasOwnBinding("foo")).toBe(true);
});
it("in class method", () => {
const classMethod = getPath("(class { method() { let foo; } })").get(
"body.0.expression.body.body.0",
);
expect(classMethod.scope.hasOwnBinding("foo")).toBe(true);
});
it("in class private method", () => {
const classMethod = getPath("(class { #method() { let foo; } })").get(
"body.0.expression.body.body.0",
);
expect(classMethod.scope.hasOwnBinding("foo")).toBe(true);
});
it("in static block", () => {
const staticBlock = getPath("(class { static { let foo; } })", {
plugins: ["classStaticBlock"],
}).get("body.0.expression.body.body.0");
expect(staticBlock.scope.hasOwnBinding("foo")).toBe(true);
});
it("in block statement", () => {
const blockStatement = getPath("{ let foo; }").get("body.0");
expect(blockStatement.scope.hasOwnBinding("foo")).toBe(true);
});
it("in catch clause", () => {
const catchClause = getPath("try {} catch { let foo; }").get(
"body.0.handler",
);
expect(catchClause.scope.hasOwnBinding("foo")).toBe(true);
});
it("in for-init statement", () => {
const forStatement = getPath("for (let foo;;);").get("body.0");
expect(forStatement.scope.hasOwnBinding("foo")).toBe(true);
});
it("in for-in statement", () => {
const forStatement = getPath("for (let foo in x);").get("body.0");
expect(forStatement.scope.hasOwnBinding("foo")).toBe(true);
});
it("in for-of statement", () => {
const forStatement = getPath("for (let foo of x);").get("body.0");
expect(forStatement.scope.hasOwnBinding("foo")).toBe(true);
});
it("in switch statement", () => {
const switchStatement = getPath("switch (0) { case 0: let foo; }").get(
"body.0",
);
expect(switchStatement.scope.hasOwnBinding("foo")).toBe(true);
});
});
});
});
2 changes: 1 addition & 1 deletion packages/babel-types/scripts/generators/docs.js
Expand Up @@ -206,7 +206,7 @@ const aliasDescriptions = {
ForXStatement:
"A cover of [ForInStatements and ForOfStatements](https://tc39.es/ecma262/#sec-for-in-and-for-of-statements).",
Function:
"A cover of functions and [method](#method)s, the must have `body` and `params`. Note: `Function` is different to `FunctionParent`.",
"A cover of functions and [method](#method)s, the must have `body` and `params`. Note: `Function` is different to `FunctionParent`. For example, a `StaticBlock` is a `FunctionParent` but not `Function`.",
FunctionParent:
"A cover of AST nodes that start an execution context with new [VariableEnvironment](https://tc39.es/ecma262/#table-additional-state-components-for-ecmascript-code-execution-contexts). In other words, they define the scope of `var` declarations. FunctionParent did not include `Program` since Babel 7.",
Immutable:
Expand Down
12 changes: 6 additions & 6 deletions packages/babel-types/src/asserts/generated/index.ts
Expand Up @@ -524,6 +524,12 @@ export function assertPrivateName(
): asserts node is t.PrivateName {
assert("PrivateName", node, opts);
}
export function assertStaticBlock(
node: object | null | undefined,
opts?: object | null,
): asserts node is t.StaticBlock {
assert("StaticBlock", node, opts);
}
export function assertAnyTypeAnnotation(
node: object | null | undefined,
opts?: object | null,
Expand Down Expand Up @@ -1076,12 +1082,6 @@ export function assertDecimalLiteral(
): asserts node is t.DecimalLiteral {
assert("DecimalLiteral", node, opts);
}
export function assertStaticBlock(
node: object | null | undefined,
opts?: object | null,
): asserts node is t.StaticBlock {
assert("StaticBlock", node, opts);
}
export function assertModuleExpression(
node: object | null | undefined,
opts?: object | null,
Expand Down
13 changes: 7 additions & 6 deletions packages/babel-types/src/ast-types/generated/index.ts
Expand Up @@ -1073,6 +1073,11 @@ export interface PrivateName extends BaseNode {
id: Identifier;
}

export interface StaticBlock extends BaseNode {
type: "StaticBlock";
body: Array<Statement>;
}

export interface AnyTypeAnnotation extends BaseNode {
type: "AnyTypeAnnotation";
}
Expand Down Expand Up @@ -1633,11 +1638,6 @@ export interface DecimalLiteral extends BaseNode {
value: string;
}

export interface StaticBlock extends BaseNode {
type: "StaticBlock";
body: Array<Statement>;
}

export interface ModuleExpression extends BaseNode {
type: "ModuleExpression";
body: Program;
Expand Down Expand Up @@ -2232,7 +2232,8 @@ export type FunctionParent =
| ObjectMethod
| ArrowFunctionExpression
| ClassMethod
| ClassPrivateMethod;
| ClassPrivateMethod
| StaticBlock;
export type Pureish =
| FunctionDeclaration
| FunctionExpression
Expand Down
6 changes: 3 additions & 3 deletions packages/babel-types/src/builders/generated/index.ts
Expand Up @@ -547,6 +547,9 @@ export function classPrivateMethod(
export function privateName(id: t.Identifier): t.PrivateName {
return builder("PrivateName", ...arguments);
}
export function staticBlock(body: Array<t.Statement>): t.StaticBlock {
return builder("StaticBlock", ...arguments);
}
export function anyTypeAnnotation(): t.AnyTypeAnnotation {
return builder("AnyTypeAnnotation", ...arguments);
}
Expand Down Expand Up @@ -1057,9 +1060,6 @@ export function tupleExpression(
export function decimalLiteral(value: string): t.DecimalLiteral {
return builder("DecimalLiteral", ...arguments);
}
export function staticBlock(body: Array<t.Statement>): t.StaticBlock {
return builder("StaticBlock", ...arguments);
}
export function moduleExpression(body: t.Program): t.ModuleExpression {
return builder("ModuleExpression", ...arguments);
}
Expand Down
2 changes: 1 addition & 1 deletion packages/babel-types/src/builders/generated/uppercase.js
Expand Up @@ -94,6 +94,7 @@ export {
classPrivateProperty as ClassPrivateProperty,
classPrivateMethod as ClassPrivateMethod,
privateName as PrivateName,
staticBlock as StaticBlock,
anyTypeAnnotation as AnyTypeAnnotation,
arrayTypeAnnotation as ArrayTypeAnnotation,
booleanTypeAnnotation as BooleanTypeAnnotation,
Expand Down Expand Up @@ -186,7 +187,6 @@ export {
recordExpression as RecordExpression,
tupleExpression as TupleExpression,
decimalLiteral as DecimalLiteral,
staticBlock as StaticBlock,
moduleExpression as ModuleExpression,
topicReference as TopicReference,
pipelineTopicExpression as PipelineTopicExpression,
Expand Down
13 changes: 13 additions & 0 deletions packages/babel-types/src/definitions/core.ts
Expand Up @@ -2196,3 +2196,16 @@ defineType("PrivateName", {
},
},
});

defineType("StaticBlock", {
visitor: ["body"],
fields: {
body: {
validate: chain(
assertValueType("array"),
assertEach(assertNodeType("Statement")),
),
},
},
aliases: ["Scopable", "BlockParent", "FunctionParent"],
});

0 comments on commit 49a0d65

Please sign in to comment.