Skip to content

Commit

Permalink
Allow function expression parameters to shadow the function id (#5262)
Browse files Browse the repository at this point in the history
* Do not explicitly pass context when creating scopes

* Allow function expression parameters to shadow their id

* Describe dashes in PR template
  • Loading branch information
lukastaegert authored Nov 21, 2023
1 parent ce6f492 commit 2ebab0c
Show file tree
Hide file tree
Showing 23 changed files with 65 additions and 40 deletions.
4 changes: 3 additions & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,12 @@ List any relevant issue numbers:
<!--
If this PR resolves any issues, list them as
resolves #1234
- resolves #1234
where 1234 is the issue number. This will help us with house-keeping as Github will automatically add a note to those issues stating that a potential fix exists. Once the PR is merged, Github will automatically close those issues.
Starting each line with a dash "-" will cause GitHub to display the issue title inline.
If an issue is only solved partially or is relevant in some other way, just list the number without "resolves".
-->

Expand Down
6 changes: 3 additions & 3 deletions src/ast/nodes/ArrowFunctionExpression.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type { HasEffectsContext, InclusionContext } from '../ExecutionContext';
import type { NodeInteraction } from '../NodeInteractions';
import { INTERACTION_CALLED } from '../NodeInteractions';
import type ChildScope from '../scopes/ChildScope';
import ReturnValueScope from '../scopes/ReturnValueScope';
import type Scope from '../scopes/Scope';
import { type ObjectPath } from '../utils/PathTracker';
import type BlockStatement from './BlockStatement';
import Identifier from './Identifier';
Expand All @@ -21,8 +21,8 @@ export default class ArrowFunctionExpression extends FunctionBase {
declare type: NodeType.tArrowFunctionExpression;
protected objectEntity: ObjectEntity | null = null;

createScope(parentScope: Scope): void {
this.scope = new ReturnValueScope(parentScope, this.scope.context, false);
createScope(parentScope: ChildScope): void {
this.scope = new ReturnValueScope(parentScope, false);
}

hasEffects(): boolean {
Expand Down
2 changes: 1 addition & 1 deletion src/ast/nodes/BlockStatement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export default class BlockStatement extends StatementBase {
createScope(parentScope: ChildScope): void {
this.scope = (this.parent as Node).preventChildBlockScope
? (parentScope as ChildScope)
: new BlockScope(parentScope, this.scope.context);
: new BlockScope(parentScope);
}

hasEffects(context: HasEffectsContext): boolean {
Expand Down
2 changes: 1 addition & 1 deletion src/ast/nodes/CatchClause.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export default class CatchClause extends NodeBase {
declare type: NodeType.tCatchClause;

createScope(parentScope: ChildScope): void {
this.scope = new ParameterScope(parentScope, this.scope.context, true);
this.scope = new ParameterScope(parentScope, true);
}

parseNode(esTreeNode: GenericEsTreeNode): void {
Expand Down
2 changes: 1 addition & 1 deletion src/ast/nodes/ClassBody.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export default class ClassBody extends NodeBase {
declare type: NodeType.tClassBody;

createScope(parentScope: ChildScope): void {
this.scope = new ClassBodyScope(parentScope, this.parent as ClassNode, this.scope.context);
this.scope = new ClassBodyScope(parentScope, this.parent as ClassNode);
}

include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void {
Expand Down
2 changes: 1 addition & 1 deletion src/ast/nodes/ForInStatement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export default class ForInStatement extends StatementBase {
declare type: NodeType.tForInStatement;

createScope(parentScope: ChildScope): void {
this.scope = new BlockScope(parentScope, this.scope.context);
this.scope = new BlockScope(parentScope);
}

hasEffects(context: HasEffectsContext): boolean {
Expand Down
2 changes: 1 addition & 1 deletion src/ast/nodes/ForOfStatement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export default class ForOfStatement extends StatementBase {
}

createScope(parentScope: ChildScope): void {
this.scope = new BlockScope(parentScope, this.scope.context);
this.scope = new BlockScope(parentScope);
}

hasEffects(): boolean {
Expand Down
2 changes: 1 addition & 1 deletion src/ast/nodes/ForStatement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export default class ForStatement extends StatementBase {
declare update: ExpressionNode | null;

createScope(parentScope: ChildScope): void {
this.scope = new BlockScope(parentScope, this.scope.context);
this.scope = new BlockScope(parentScope);
}

hasEffects(context: HasEffectsContext): boolean {
Expand Down
16 changes: 16 additions & 0 deletions src/ast/nodes/FunctionExpression.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,27 @@
import type MagicString from 'magic-string';
import { BLANK } from '../../utils/blank';
import type { NodeRenderOptions, RenderOptions } from '../../utils/renderHelpers';
import ChildScope from '../scopes/ChildScope';
import type { IdentifierWithVariable } from './Identifier';
import Identifier from './Identifier';
import * as NodeType from './NodeType';
import FunctionNode from './shared/FunctionNode';
import type { GenericEsTreeNode } from './shared/Node';

export default class FunctionExpression extends FunctionNode {
declare type: NodeType.tFunctionExpression;
declare idScope: ChildScope;

createScope(parentScope: ChildScope) {
super.createScope((this.idScope = new ChildScope(parentScope, parentScope.context)));
}

parseNode(esTreeNode: GenericEsTreeNode) {
if (esTreeNode.id !== null) {
this.id = new Identifier(esTreeNode.id, this, this.idScope) as IdentifierWithVariable;
}
super.parseNode(esTreeNode);
}

render(
code: MagicString,
Expand Down
4 changes: 2 additions & 2 deletions src/ast/nodes/IfStatement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,14 @@ export default class IfStatement extends StatementBase implements DeoptimizableE
}

parseNode(esTreeNode: GenericEsTreeNode): void {
this.consequentScope = new TrackingScope(this.scope, this.scope.context);
this.consequentScope = new TrackingScope(this.scope);
this.consequent = new (this.scope.context.getNodeConstructor(esTreeNode.consequent.type))(
esTreeNode.consequent,
this,
this.consequentScope
);
if (esTreeNode.alternate) {
this.alternateScope = new TrackingScope(this.scope, this.scope.context);
this.alternateScope = new TrackingScope(this.scope);
this.alternate = new (this.scope.context.getNodeConstructor(esTreeNode.alternate.type))(
esTreeNode.alternate,
this,
Expand Down
2 changes: 1 addition & 1 deletion src/ast/nodes/StaticBlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export default class StaticBlock extends StatementBase {
declare type: NodeType.tStaticBlock;

createScope(parentScope: ChildScope): void {
this.scope = new BlockScope(parentScope, this.scope.context);
this.scope = new BlockScope(parentScope);
}

hasEffects(context: HasEffectsContext): boolean {
Expand Down
2 changes: 1 addition & 1 deletion src/ast/nodes/SwitchStatement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export default class SwitchStatement extends StatementBase {

createScope(parentScope: ChildScope): void {
this.parentScope = parentScope;
this.scope = new BlockScope(parentScope, this.scope.context);
this.scope = new BlockScope(parentScope);
}

hasEffects(context: HasEffectsContext): boolean {
Expand Down
2 changes: 1 addition & 1 deletion src/ast/nodes/shared/ClassNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export default class ClassNode extends NodeBase implements DeoptimizableEntity {
private objectEntity: ObjectEntity | null = null;

createScope(parentScope: ChildScope): void {
this.scope = new ChildScope(parentScope, this.scope.context);
this.scope = new ChildScope(parentScope, parentScope.context);
}

deoptimizeArgumentsOnInteractionAtPath(
Expand Down
5 changes: 3 additions & 2 deletions src/ast/nodes/shared/FunctionNode.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { type HasEffectsContext, type InclusionContext } from '../../ExecutionContext';
import type { NodeInteraction } from '../../NodeInteractions';
import { INTERACTION_CALLED } from '../../NodeInteractions';
import type ChildScope from '../../scopes/ChildScope';
import FunctionScope from '../../scopes/FunctionScope';
import type { ObjectPath, PathTracker } from '../../utils/PathTracker';
import type BlockStatement from '../BlockStatement';
Expand All @@ -23,8 +24,8 @@ export default class FunctionNode extends FunctionBase {
protected objectEntity: ObjectEntity | null = null;
private declare constructedEntity: ObjectEntity;

createScope(parentScope: FunctionScope): void {
this.scope = new FunctionScope(parentScope, this.scope.context);
createScope(parentScope: ChildScope): void {
this.scope = new FunctionScope(parentScope);
this.constructedEntity = new ObjectEntity(Object.create(null), OBJECT_PROTOTYPE);
// This makes sure that all deoptimizations of "this" are applied to the
// constructed entity.
Expand Down
4 changes: 4 additions & 0 deletions src/ast/scopes/BlockScope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ import type LocalVariable from '../variables/LocalVariable';
import ChildScope from './ChildScope';

export default class BlockScope extends ChildScope {
constructor(parent: ChildScope) {
super(parent, parent.context);
}

addDeclaration(
identifier: Identifier,
context: AstContext,
Expand Down
7 changes: 2 additions & 5 deletions src/ast/scopes/CatchBodyScope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,8 @@ import ChildScope from './ChildScope';
import type ParameterScope from './ParameterScope';

export default class CatchBodyScope extends ChildScope {
constructor(
readonly parent: ParameterScope,
readonly context: AstContext
) {
super(parent, context);
constructor(readonly parent: ParameterScope) {
super(parent, parent.context);
}

addDeclaration(
Expand Down
5 changes: 2 additions & 3 deletions src/ast/scopes/ClassBodyScope.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import type { AstContext } from '../../Module';
import type ClassNode from '../nodes/shared/ClassNode';
import { VariableKind } from '../nodes/shared/VariableKinds';
import LocalVariable from '../variables/LocalVariable';
import ThisVariable from '../variables/ThisVariable';
import ChildScope from './ChildScope';
import type Scope from './Scope';

export default class ClassBodyScope extends ChildScope {
readonly instanceScope: ChildScope;
readonly thisVariable: LocalVariable;

constructor(parent: Scope, classNode: ClassNode, context: AstContext) {
constructor(parent: ChildScope, classNode: ClassNode) {
const { context } = parent;
super(parent, context);
this.variables.set(
'this',
Expand Down
7 changes: 2 additions & 5 deletions src/ast/scopes/FunctionBodyScope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,8 @@ import ChildScope from './ChildScope';
import type ParameterScope from './ParameterScope';

export default class FunctionBodyScope extends ChildScope {
constructor(
readonly parent: ParameterScope,
readonly context: AstContext
) {
super(parent, context);
constructor(parent: ParameterScope) {
super(parent, parent.context);
}

// There is stuff that is only allowed in function scopes, i.e. functions can
Expand Down
6 changes: 3 additions & 3 deletions src/ast/scopes/FunctionScope.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import type { AstContext } from '../../Module';
import type { InclusionContext } from '../ExecutionContext';
import type SpreadElement from '../nodes/SpreadElement';
import type { ExpressionEntity } from '../nodes/shared/Expression';
Expand All @@ -11,8 +10,9 @@ export default class FunctionScope extends ReturnValueScope {
readonly argumentsVariable: ArgumentsVariable;
readonly thisVariable: ThisVariable;

constructor(parent: ChildScope, context: AstContext) {
super(parent, context, false);
constructor(parent: ChildScope) {
const { context } = parent;
super(parent, false);
this.variables.set('arguments', (this.argumentsVariable = new ArgumentsVariable(context)));
this.variables.set('this', (this.thisVariable = new ThisVariable(context)));
}
Expand Down
10 changes: 3 additions & 7 deletions src/ast/scopes/ParameterScope.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import type { AstContext } from '../../Module';
import { logDuplicateArgumentNameError } from '../../utils/logs';
import type { InclusionContext } from '../ExecutionContext';
import type Identifier from '../nodes/Identifier';
Expand All @@ -8,19 +7,16 @@ import ParameterVariable from '../variables/ParameterVariable';
import CatchBodyScope from './CatchBodyScope';
import ChildScope from './ChildScope';
import FunctionBodyScope from './FunctionBodyScope';
import type Scope from './Scope';

export default class ParameterScope extends ChildScope {
readonly bodyScope: ChildScope;
parameters: readonly ParameterVariable[][] = [];

private hasRest = false;

constructor(parent: Scope, context: AstContext, isCatchScope: boolean) {
super(parent, context);
this.bodyScope = isCatchScope
? new CatchBodyScope(this, context)
: new FunctionBodyScope(this, context);
constructor(parent: ChildScope, isCatchScope: boolean) {
super(parent, parent.context);
this.bodyScope = isCatchScope ? new CatchBodyScope(this) : new FunctionBodyScope(this);
}

/**
Expand Down
3 changes: 3 additions & 0 deletions test/form/samples/function-id-as-parameter/_config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = defineTest({
description: 'allows function expression parameters to shadow their id'
});
5 changes: 5 additions & 0 deletions test/form/samples/function-id-as-parameter/_expected.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const foo = function foo(foo) {
console.log(foo);
};

foo(42);
5 changes: 5 additions & 0 deletions test/form/samples/function-id-as-parameter/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const foo = function foo(foo) {
console.log(foo);
}

foo(42);

0 comments on commit 2ebab0c

Please sign in to comment.