Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ts] Fix restrictions for optional parameters #15414

Merged
merged 5 commits into from Feb 9, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
37 changes: 30 additions & 7 deletions packages/babel-parser/src/parser/lval.ts
Expand Up @@ -366,7 +366,7 @@ export default abstract class LValParser extends NodeUtils {
node.elements = this.parseBindingList(
tt.bracketR,
charCodes.rightSquareBracket,
true,
{ allowEmpty: true },
);
return this.finishNode(node, "ArrayPattern");
}
Expand All @@ -384,8 +384,15 @@ export default abstract class LValParser extends NodeUtils {
this: Parser,
close: TokenType,
closeCharCode: typeof charCodes[keyof typeof charCodes],
allowEmpty?: boolean,
allowModifiers?: boolean,
{
allowEmpty = false,
allowModifiers = false,
isFunctionParams = false,
}: {
allowEmpty?: boolean;
allowModifiers?: boolean;
isFunctionParams?: boolean;
} = {},
): Array<Pattern | TSParameterProperty> {
const elts: Array<Pattern | TSParameterProperty> = [];
let first = true;
Expand All @@ -400,7 +407,12 @@ export default abstract class LValParser extends NodeUtils {
} else if (this.eat(close)) {
break;
} else if (this.match(tt.ellipsis)) {
elts.push(this.parseAssignableListItemTypes(this.parseRestBinding()));
elts.push(
this.parseAssignableListItemTypes(
this.parseRestBinding(),
isFunctionParams,
),
);
if (!this.checkCommaAfterRest(closeCharCode)) {
this.expect(close);
break;
Expand All @@ -416,7 +428,13 @@ export default abstract class LValParser extends NodeUtils {
while (this.match(tt.at)) {
decorators.push(this.parseDecorator());
}
elts.push(this.parseAssignableListItem(allowModifiers, decorators));
elts.push(
this.parseAssignableListItem(
allowModifiers,
decorators,
isFunctionParams,
),
);
}
}
return elts;
Expand Down Expand Up @@ -462,9 +480,10 @@ export default abstract class LValParser extends NodeUtils {
this: Parser,
allowModifiers: boolean | undefined | null,
decorators: Decorator[],
isFunctionParam: boolean = false,
): Pattern | TSParameterProperty {
const left = this.parseMaybeDefault();
this.parseAssignableListItemTypes(left);
this.parseAssignableListItemTypes(left, isFunctionParam);
const elt = this.parseMaybeDefault(left.loc.start, left);
if (decorators.length) {
left.decorators = decorators;
Expand All @@ -473,7 +492,11 @@ export default abstract class LValParser extends NodeUtils {
}

// Used by flow/typescript plugin to add type annotations to binding elements
parseAssignableListItemTypes(param: Pattern): Pattern {
parseAssignableListItemTypes(
param: Pattern,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
isFunctionParam: boolean,
): Pattern {
return param;
}

Expand Down
8 changes: 3 additions & 5 deletions packages/babel-parser/src/parser/statement.ts
Expand Up @@ -1606,12 +1606,10 @@ export default abstract class StatementParser extends ExpressionParser {
): void {
this.expect(tt.parenL);
this.expressionScope.enter(newParameterDeclarationScope());
node.params = this.parseBindingList(
tt.parenR,
charCodes.rightParenthesis,
/* allowEmpty */ false,
node.params = this.parseBindingList(tt.parenR, charCodes.rightParenthesis, {
allowModifiers,
);
isFunctionParams: true,
});

this.expressionScope.exit();
}
Expand Down
49 changes: 33 additions & 16 deletions packages/babel-parser/src/plugins/typescript/index.ts
Expand Up @@ -755,7 +755,9 @@ export default (superClass: ClassWithMixin<typeof Parser, IJSXParserMixin>) =>
N.Identifier | N.RestElement | N.ObjectPattern | N.ArrayPattern
> {
return super
.parseBindingList(tt.parenR, charCodes.rightParenthesis)
.parseBindingList(tt.parenR, charCodes.rightParenthesis, {
isFunctionParams: true,
})
.map(pattern => {
if (
pattern.type !== "Identifier" &&
Expand Down Expand Up @@ -1419,11 +1421,9 @@ export default (superClass: ClassWithMixin<typeof Parser, IJSXParserMixin>) =>
const { errors } = this.state;
const previousErrorCount = errors.length;
try {
super.parseBindingList(
tt.bracketR,
charCodes.rightSquareBracket,
true,
);
super.parseBindingList(tt.bracketR, charCodes.rightSquareBracket, {
allowEmpty: true,
});
return errors.length === previousErrorCount;
} catch {
return false;
Expand Down Expand Up @@ -2251,6 +2251,7 @@ export default (superClass: ClassWithMixin<typeof Parser, IJSXParserMixin>) =>
parseAssignableListItem(
allowModifiers: boolean | undefined | null,
decorators: N.Decorator[],
isFunctionParam: boolean,
): N.Pattern | N.TSParameterProperty {
// Store original location to include modifiers in range
const startLoc = this.state.startLoc;
Expand Down Expand Up @@ -2282,7 +2283,7 @@ export default (superClass: ClassWithMixin<typeof Parser, IJSXParserMixin>) =>
}

const left = this.parseMaybeDefault();
this.parseAssignableListItemTypes(left);
this.parseAssignableListItemTypes(left, isFunctionParam);
const elt = this.parseMaybeDefault(left.loc.start, left);
if (accessibility || readonly || override) {
const pp = this.startNodeAt<N.TSParameterProperty>(startLoc);
Expand Down Expand Up @@ -2314,6 +2315,27 @@ export default (superClass: ClassWithMixin<typeof Parser, IJSXParserMixin>) =>
);
}

tsDisallowOptionalPattern(node: Undone<N.Function>) {
for (const param of node.params) {
if (
param.type !== "Identifier" &&
(param as any).optional &&
!this.state.isAmbientContext
) {
this.raise(TSErrors.PatternIsOptional, { at: param });
}
}
}

setArrowFunctionParameters(
node: Undone<N.ArrowFunctionExpression>,
params: N.Expression[],
trailingCommaLoc?: Position | null,
): void {
super.setArrowFunctionParameters(node, params, trailingCommaLoc);
this.tsDisallowOptionalPattern(node);
}

parseFunctionBodyAndFinish<
T extends
| N.Function
Expand All @@ -2340,6 +2362,7 @@ export default (superClass: ClassWithMixin<typeof Parser, IJSXParserMixin>) =>
return super.parseFunctionBodyAndFinish(node, bodilessType, isMethod);
}
}
this.tsDisallowOptionalPattern(node);

return super.parseFunctionBodyAndFinish(node, type, isMethod);
}
Expand Down Expand Up @@ -3504,16 +3527,10 @@ export default (superClass: ClassWithMixin<typeof Parser, IJSXParserMixin>) =>
}

// Allow type annotations inside of a parameter list.
parseAssignableListItemTypes(param: N.Pattern) {
if (this.eat(tt.question)) {
if (
param.type !== "Identifier" &&
!this.state.isAmbientContext &&
!this.state.inType
) {
this.raise(TSErrors.PatternIsOptional, { at: param });
}
parseAssignableListItemTypes(param: N.Pattern, isFunctionParam: boolean) {
if (!isFunctionParam) return param;

if (this.eat(tt.question)) {
(param as any as N.Identifier).optional = true;
}
const type = this.tsTryParseTypeAnnotation();
Expand Down
@@ -1,6 +1,9 @@
{
"type": "File",
"start":0,"end":34,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":34,"index":34}},
"errors": [
"SyntaxError: A binding pattern parameter cannot be optional in an implementation signature. (1:6)"
],
"program": {
"type": "Program",
"start":0,"end":34,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":34,"index":34}},
Expand Down
@@ -0,0 +1 @@
([]?, {}) => {}
@@ -0,0 +1,46 @@
{
"type": "File",
"start":0,"end":15,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":15,"index":15}},
"errors": [
"SyntaxError: A binding pattern parameter cannot be optional in an implementation signature. (1:1)"
],
"program": {
"type": "Program",
"start":0,"end":15,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":15,"index":15}},
"sourceType": "module",
"interpreter": null,
"body": [
{
"type": "ExpressionStatement",
"start":0,"end":15,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":15,"index":15}},
"expression": {
"type": "ArrowFunctionExpression",
"start":0,"end":15,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":15,"index":15}},
"id": null,
"generator": false,
"async": false,
"params": [
{
"type": "ArrayPattern",
"start":1,"end":4,"loc":{"start":{"line":1,"column":1,"index":1},"end":{"line":1,"column":4,"index":4}},
"elements": [],
"optional": true
},
{
"type": "ObjectPattern",
"start":6,"end":8,"loc":{"start":{"line":1,"column":6,"index":6},"end":{"line":1,"column":8,"index":8}},
"properties": []
}
],
"body": {
"type": "BlockStatement",
"start":13,"end":15,"loc":{"start":{"line":1,"column":13,"index":13},"end":{"line":1,"column":15,"index":15}},
"body": [],
"directives": []
}
}
}
],
"directives": []
}
}
@@ -0,0 +1,4 @@
function f({}?): void;
function f() {}

export { f };
@@ -0,0 +1,82 @@
{
"type": "File",
"start":0,"end":53,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":4,"column":13,"index":53}},
"program": {
"type": "Program",
"start":0,"end":53,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":4,"column":13,"index":53}},
"sourceType": "module",
"interpreter": null,
"body": [
{
"type": "TSDeclareFunction",
"start":0,"end":22,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":22,"index":22}},
"id": {
"type": "Identifier",
"start":9,"end":10,"loc":{"start":{"line":1,"column":9,"index":9},"end":{"line":1,"column":10,"index":10},"identifierName":"f"},
"name": "f"
},
"generator": false,
"async": false,
"params": [
{
"type": "ObjectPattern",
"start":11,"end":14,"loc":{"start":{"line":1,"column":11,"index":11},"end":{"line":1,"column":14,"index":14}},
"properties": [],
"optional": true
}
],
"returnType": {
"type": "TSTypeAnnotation",
"start":15,"end":21,"loc":{"start":{"line":1,"column":15,"index":15},"end":{"line":1,"column":21,"index":21}},
"typeAnnotation": {
"type": "TSVoidKeyword",
"start":17,"end":21,"loc":{"start":{"line":1,"column":17,"index":17},"end":{"line":1,"column":21,"index":21}}
}
}
},
{
"type": "FunctionDeclaration",
"start":23,"end":38,"loc":{"start":{"line":2,"column":0,"index":23},"end":{"line":2,"column":15,"index":38}},
"id": {
"type": "Identifier",
"start":32,"end":33,"loc":{"start":{"line":2,"column":9,"index":32},"end":{"line":2,"column":10,"index":33},"identifierName":"f"},
"name": "f"
},
"generator": false,
"async": false,
"params": [],
"body": {
"type": "BlockStatement",
"start":36,"end":38,"loc":{"start":{"line":2,"column":13,"index":36},"end":{"line":2,"column":15,"index":38}},
"body": [],
"directives": []
}
},
{
"type": "ExportNamedDeclaration",
"start":40,"end":53,"loc":{"start":{"line":4,"column":0,"index":40},"end":{"line":4,"column":13,"index":53}},
"exportKind": "value",
"specifiers": [
{
"type": "ExportSpecifier",
"start":49,"end":50,"loc":{"start":{"line":4,"column":9,"index":49},"end":{"line":4,"column":10,"index":50}},
"local": {
"type": "Identifier",
"start":49,"end":50,"loc":{"start":{"line":4,"column":9,"index":49},"end":{"line":4,"column":10,"index":50},"identifierName":"f"},
"name": "f"
},
"exportKind": "value",
"exported": {
"type": "Identifier",
"start":49,"end":50,"loc":{"start":{"line":4,"column":9,"index":49},"end":{"line":4,"column":10,"index":50},"identifierName":"f"},
"name": "f"
}
}
],
"source": null,
"declaration": null
}
],
"directives": []
}
}
@@ -0,0 +1 @@
let [a: number] = b;
@@ -0,0 +1,3 @@
{
"throws": "Unexpected token, expected \",\" (1:6)"
}
@@ -0,0 +1 @@
function f([a: number]) {}
@@ -0,0 +1,3 @@
{
"throws": "Unexpected token, expected \",\" (1:13)"
}
@@ -0,0 +1 @@
let [a: number] = b;
@@ -0,0 +1,3 @@
{
"throws": "Unexpected token, expected \",\" (1:6)"
}