Skip to content

Commit

Permalink
Disallow mixing labeled and unlabeled elements
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolo-ribaudo committed Jun 28, 2020
1 parent 18bcb42 commit ce28128
Show file tree
Hide file tree
Showing 10 changed files with 32 additions and 79 deletions.
41 changes: 27 additions & 14 deletions packages/babel-parser/src/plugins/typescript/index.js
Expand Up @@ -76,8 +76,8 @@ const TSErrors = Object.freeze({
IndexSignatureHasStatic: "Index signatures cannot have the 'static' modifier",
OptionalTypeBeforeRequired:
"A required element cannot follow an optional element.",
LabeledElementBeforeUnlabeled:
"An unlabeled element cannot follow a labeled element.",
MixedLabeledAndUnlabeledElements:
"Tuple members must all have names or all not have names.",
PatternIsOptional:
"A binding pattern parameter cannot be optional in an implementation signature.",
PrivateElementHasAbstract:
Expand Down Expand Up @@ -636,7 +636,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
// No mandatory elements may follow optional elements
// If there's a rest element, it must be at the end of the tuple
let seenOptionalElement = false;
let seenLabeledElement = false;
let labeledElements = null;
node.elementTypes.forEach(elementNode => {
const { type } = elementNode;

Expand All @@ -649,19 +649,32 @@ export default (superClass: Class<Parser>): Class<Parser> =>
this.raise(elementNode.start, TSErrors.OptionalTypeBeforeRequired);
}

if (
seenLabeledElement &&
!(type === "TSTupleElementType" && elementNode.label)
) {
this.raise(elementNode.start, TSErrors.LabeledElementBeforeUnlabeled);
}
// Flow doesn't support ||=
seenOptionalElement =
seenOptionalElement ||
(type === "TSTupleElementType" && elementNode.optional) ||
type === "TSOptionalType";

// Don't check labels on spread elements
if (type === "TSRestType") return;

const isLabeled = type === "TSTupleElementType" && !!elementNode.label;

if (type === "TSTupleElementType") {
if (elementNode.label) seenLabeledElement = true;
if (elementNode.optional) seenOptionalElement = true;
} else if (type === "TSOptionalType") {
seenOptionalElement = true;
if (labeledElements === false && isLabeled) {
this.raise(
elementNode.start,
TSErrors.MixedLabeledAndUnlabeledElements,
);
} else if (labeledElements === true && !isLabeled) {
this.raise(
elementNode.start,
TSErrors.MixedLabeledAndUnlabeledElements,
);
}

labeledElements =
labeledElements ??
(type === "TSTupleElementType" && !!elementNode.label);
});

return this.finishNode(node, "TSTupleType");
Expand Down
@@ -0,0 +1 @@
type T = [...B, x: A];
@@ -1,6 +1,9 @@
{
"type": "File",
"start":0,"end":19,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":19}},
"errors": [
"SyntaxError: Tuple members must all have names or all not have names. (1:13)"
],
"program": {
"type": "Program",
"start":0,"end":19,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":19}},
Expand Down
Expand Up @@ -2,7 +2,7 @@
"type": "File",
"start":0,"end":19,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":19}},
"errors": [
"SyntaxError: An unlabeled element cannot follow a labeled element. (1:16)"
"SyntaxError: Tuple members must all have names or all not have names. (1:16)"
],
"program": {
"type": "Program",
Expand Down

This file was deleted.

This file was deleted.

@@ -1,9 +1,6 @@
{
"type": "File",
"start":0,"end":22,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":22}},
"errors": [
"SyntaxError: An unlabeled element cannot follow a labeled element. (1:16)"
],
"program": {
"type": "Program",
"start":0,"end":22,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":22}},
Expand Down

0 comments on commit ce28128

Please sign in to comment.