[BugFix] : OptionalChaining Bug fixes (#7288)

* Added optionalExpression types to babylon and babel-types

* OptionalChain transforms bug fix

* Added OptionalExpressions to babel-generator. Fixed OptionalChain Bugs

* Removed 'optionalChain' from newExpression and added test cases

* Added test cases for optionalChain

* Update index.js
This commit is contained in:
Naveen jain 2018-02-08 06:12:14 +05:30 committed by Justin Ridgewell
parent dd0337cc85
commit a3ad518ce1
35 changed files with 1276 additions and 616 deletions

View File

@ -95,7 +95,34 @@ export function Decorator(node: Object) {
this.newline(); this.newline();
} }
export function CallExpression(node: Object) { export function OptionalMemberExpression(node: Object) {
this.print(node.object, node);
if (!node.computed && t.isMemberExpression(node.property)) {
throw new TypeError("Got a MemberExpression for MemberExpression property");
}
let computed = node.computed;
if (t.isLiteral(node.property) && typeof node.property.value === "number") {
computed = true;
}
if (node.optional) {
this.token("?.");
}
if (computed) {
this.token("[");
this.print(node.property, node);
this.token("]");
} else {
if (!node.optional) {
this.token(".");
}
this.print(node.property, node);
}
}
export function OptionalCallExpression(node: Object) {
this.print(node.callee, node); this.print(node.callee, node);
this.print(node.typeParameters, node); // TS this.print(node.typeParameters, node); // TS
@ -108,6 +135,15 @@ export function CallExpression(node: Object) {
this.token(")"); this.token(")");
} }
export function CallExpression(node: Object) {
this.print(node.callee, node);
this.print(node.typeParameters, node); // TS
this.token("(");
this.printList(node.arguments, node);
this.token(")");
}
export function Import() { export function Import() {
this.word("import"); this.word("import");
} }
@ -203,17 +239,12 @@ export function MemberExpression(node: Object) {
computed = true; computed = true;
} }
if (node.optional) {
this.token("?.");
}
if (computed) { if (computed) {
this.token("["); this.token("[");
this.print(node.property, node); this.print(node.property, node);
this.token("]"); this.token("]");
} else { } else {
if (!node.optional) { this.token(".");
this.token(".");
}
this.print(node.property, node); this.print(node.property, node);
} }
} }

View File

@ -1,39 +0,0 @@
new foo?.();
new foo?.("foo");
new foo?.("foo", "bar");
new foo?.(bar());
new foo?.(bar("test"));
foo(new bar?.());
foo(new bar?.("test"));
new a.foo?.();
new a.foo?.("foo");
new a.foo?.("foo", "bar");
new a.foo?.(bar());
new a.foo?.(bar("test"));
a.foo(new bar?.());
a.foo(new bar?.("test"));
new a?.foo?.();
new a?.foo?.("foo");
new a?.foo?.("foo", "bar");
new a?.foo?.(bar());
new a?.foo?.(bar("test"));
a?.foo(new bar?.());
a?.foo(new bar?.("test"));
new a.foo?.().baz;
new a.foo?.("foo").baz;
new a.foo?.("foo", "bar").baz;
new a.foo?.(bar()).baz;
new a.foo?.(bar("test")).baz;
a.foo(new bar?.()).baz;
a.foo(new bar?.("test")).baz;
new a.foo?.()?.baz;
new a.foo?.("foo")?.baz;
new a.foo?.("foo", "bar")?.baz;
new a.foo?.(bar())?.baz;
new a.foo?.(bar("test"))?.baz;
a.foo(new bar?.())?.baz;
a.foo(new bar?.("test"))?.baz;

View File

@ -1 +0,0 @@
{ "plugins": ["optionalChaining"] }

View File

@ -1,35 +0,0 @@
new foo?.();
new foo?.("foo");
new foo?.("foo", "bar");
new foo?.(bar());
new foo?.(bar("test"));
foo(new bar?.());
foo(new bar?.("test"));
new a.foo?.();
new a.foo?.("foo");
new a.foo?.("foo", "bar");
new a.foo?.(bar());
new a.foo?.(bar("test"));
a.foo(new bar?.());
a.foo(new bar?.("test"));
new a?.foo?.();
new a?.foo?.("foo");
new a?.foo?.("foo", "bar");
new a?.foo?.(bar());
new a?.foo?.(bar("test"));
a?.foo(new bar?.());
a?.foo(new bar?.("test"));
new a.foo?.().baz;
new a.foo?.("foo").baz;
new a.foo?.("foo", "bar").baz;
new a.foo?.(bar()).baz;
new a.foo?.(bar("test")).baz;
a.foo(new bar?.()).baz;
a.foo(new bar?.("test")).baz;
new a.foo?.()?.baz;
new a.foo?.("foo")?.baz;
new a.foo?.("foo", "bar")?.baz;
new a.foo?.(bar())?.baz;
new a.foo?.(bar("test"))?.baz;
a.foo(new bar?.())?.baz;
a.foo(new bar?.("test"))?.baz;

View File

@ -9,15 +9,20 @@ export default function(api, options) {
const optionals = []; const optionals = [];
let objectPath = path; let objectPath = path;
while (objectPath.isMemberExpression() || objectPath.isCallExpression()) { while (
objectPath.isOptionalMemberExpression() ||
objectPath.isOptionalCallExpression()
) {
const { node } = objectPath; const { node } = objectPath;
if (node.optional) { if (node.optional) {
optionals.push(node); optionals.push(node);
} }
if (objectPath.isMemberExpression()) { if (objectPath.isOptionalMemberExpression()) {
objectPath.node.type = "MemberExpression";
objectPath = objectPath.get("object"); objectPath = objectPath.get("object");
} else { } else {
objectPath.node.type = "CallExpression";
objectPath = objectPath.get("callee"); objectPath = objectPath.get("callee");
} }
} }
@ -101,21 +106,10 @@ export default function(api, options) {
return path.find(path => { return path.find(path => {
const { parentPath } = path; const { parentPath } = path;
if (path.key == "left" && parentPath.isAssignmentExpression()) { if (path.key == "object" && parentPath.isOptionalMemberExpression()) {
throw path.buildCodeFrameError(
"Illegal optional chain in assignment expression",
);
}
if (path.key == "argument" && parentPath.isUpdateExpression()) {
throw path.buildCodeFrameError(
"Illegal optional chain in update expression",
);
}
if (path.key == "object" && parentPath.isMemberExpression()) {
return false; return false;
} }
if (path.key == "callee" && parentPath.isCallExpression()) { if (path.key == "callee" && parentPath.isOptionalCallExpression()) {
return false; return false;
} }
if ( if (
@ -133,7 +127,7 @@ export default function(api, options) {
inherits: syntaxOptionalChaining, inherits: syntaxOptionalChaining,
visitor: { visitor: {
"MemberExpression|CallExpression"(path) { "OptionalCallExpression|OptionalMemberExpression"(path) {
if (!path.node.optional) { if (!path.node.optional) {
return; return;
} }

View File

@ -1,3 +1,3 @@
{ {
"throws": "Illegal optional chain in assignment expression" "throws": "Invalid left-hand side in assignment expression"
} }

View File

@ -1,3 +1,3 @@
{ {
"throws": "Illegal optional chain in assignment expression" "throws": "Invalid left-hand side in assignment expression"
} }

View File

@ -1,3 +1,3 @@
{ {
"throws": "Illegal optional chain in update expression" "throws": "Invalid left-hand side in postfix operation"
} }

View File

@ -1590,6 +1590,38 @@ Aliases: `Flow`, `FlowDeclaration`, `Statement`, `Declaration`
--- ---
### optionalCallExpression
```javascript
t.optionalCallExpression(callee, arguments, optional)
```
See also `t.isOptionalCallExpression(node, opts)` and `t.assertOptionalCallExpression(node, opts)`.
Aliases: `Expression`
- `callee`: `Expression` (required)
- `arguments`: `Array<Expression | SpreadElement | JSXNamespacedName>` (required)
- `optional`: `boolean` (required)
- `typeParameters`: `TypeParameterInstantiation | TSTypeParameterInstantiation` (default: `null`)
---
### optionalMemberExpression
```javascript
t.optionalMemberExpression(object, property, computed, optional)
```
See also `t.isOptionalMemberExpression(node, opts)` and `t.assertOptionalMemberExpression(node, opts)`.
Aliases: `Expression`
- `object`: `Expression` (required)
- `property`: `any` (required)
- `computed`: `boolean` (default: `false`)
- `optional`: `boolean` (required)
---
### parenthesizedExpression ### parenthesizedExpression
```javascript ```javascript
t.parenthesizedExpression(expression) t.parenthesizedExpression(expression)

View File

@ -651,6 +651,18 @@ export function assertBindExpression(node: Object, opts?: Object = {}): void {
export function assertClassProperty(node: Object, opts?: Object = {}): void { export function assertClassProperty(node: Object, opts?: Object = {}): void {
assert("ClassProperty", node, opts); assert("ClassProperty", node, opts);
} }
export function assertOptionalMemberExpression(
node: Object,
opts?: Object = {},
): void {
assert("OptionalMemberExpression", node, opts);
}
export function assertOptionalCallExpression(
node: Object,
opts?: Object = {},
): void {
assert("OptionalCallExpression", node, opts);
}
export function assertImport(node: Object, opts?: Object = {}): void { export function assertImport(node: Object, opts?: Object = {}): void {
assert("Import", node, opts); assert("Import", node, opts);
} }

View File

@ -596,6 +596,14 @@ export function ClassProperty(...args: Array<any>): Object {
return builder("ClassProperty", ...args); return builder("ClassProperty", ...args);
} }
export { ClassProperty as classProperty }; export { ClassProperty as classProperty };
export function OptionalMemberExpression(...args: Array<any>): Object {
return builder("OptionalMemberExpression", ...args);
}
export { OptionalMemberExpression as optionalMemberExpression };
export function OptionalCallExpression(...args: Array<any>): Object {
return builder("OptionalCallExpression", ...args);
}
export { OptionalCallExpression as optionalCallExpression };
export function Import(...args: Array<any>): Object { export function Import(...args: Array<any>): Object {
return builder("Import", ...args); return builder("Import", ...args);
} }

View File

@ -54,6 +54,63 @@ defineType("ClassProperty", {
}, },
}); });
defineType("OptionalMemberExpression", {
builder: ["object", "property", "computed", "optional"],
visitor: ["object", "property"],
aliases: ["Expression"],
fields: {
object: {
validate: assertNodeType("Expression"),
},
property: {
validate: (function() {
const normal = assertNodeType("Identifier");
const computed = assertNodeType("Expression");
return function(node, key, val) {
const validator = node.computed ? computed : normal;
validator(node, key, val);
};
})(),
},
computed: {
default: false,
},
optional: {
validate: assertValueType("boolean"),
},
},
});
defineType("OptionalCallExpression", {
visitor: ["callee", "arguments", "typeParameters"],
builder: ["callee", "arguments", "optional"],
aliases: ["Expression"],
fields: {
callee: {
validate: assertNodeType("Expression"),
},
arguments: {
validate: chain(
assertValueType("array"),
assertEach(
assertNodeType("Expression", "SpreadElement", "JSXNamespacedName"),
),
),
},
optional: {
validate: assertValueType("boolean"),
},
typeParameters: {
validate: assertNodeType(
"TypeParameterInstantiation",
"TSTypeParameterInstantiation",
),
optional: true,
},
},
});
defineType("Import", { defineType("Import", {
aliases: ["Expression"], aliases: ["Expression"],
}); });

View File

@ -485,6 +485,15 @@ export function isBindExpression(node: Object, opts?: Object): boolean {
export function isClassProperty(node: Object, opts?: Object): boolean { export function isClassProperty(node: Object, opts?: Object): boolean {
return is("ClassProperty", node, opts); return is("ClassProperty", node, opts);
} }
export function isOptionalMemberExpression(
node: Object,
opts?: Object,
): boolean {
return is("OptionalMemberExpression", node, opts);
}
export function isOptionalCallExpression(node: Object, opts?: Object): boolean {
return is("OptionalCallExpression", node, opts);
}
export function isImport(node: Object, opts?: Object): boolean { export function isImport(node: Object, opts?: Object): boolean {
return is("Import", node, opts); return is("Import", node, opts);
} }

View File

@ -433,13 +433,13 @@ export default class ExpressionParser extends LValParser {
return base; return base;
} }
/** @param state Set 'state.stop = true' to indicate that we should stop parsing subscripts. */ /** @param state Set 'state.stop = true' to indicate that we should stop parsing subscripts. 'state.optionalChainMember to indicate that the member is currently in OptionalChain'*/
parseSubscript( parseSubscript(
base: N.Expression, base: N.Expression,
startPos: number, startPos: number,
startLoc: Position, startLoc: Position,
noCalls: ?boolean, noCalls: ?boolean,
state: { stop: boolean }, state: { stop: boolean, optionalChainMember?: boolean },
): N.Expression { ): N.Expression {
if (!noCalls && this.eat(tt.doubleColon)) { if (!noCalls && this.eat(tt.doubleColon)) {
const node = this.startNodeAt(startPos, startLoc); const node = this.startNodeAt(startPos, startLoc);
@ -454,7 +454,7 @@ export default class ExpressionParser extends LValParser {
); );
} else if (this.match(tt.questionDot)) { } else if (this.match(tt.questionDot)) {
this.expectPlugin("optionalChaining"); this.expectPlugin("optionalChaining");
state.optionalChainMember = true;
if (noCalls && this.lookahead().type == tt.parenL) { if (noCalls && this.lookahead().type == tt.parenL) {
state.stop = true; state.stop = true;
return base; return base;
@ -469,7 +469,7 @@ export default class ExpressionParser extends LValParser {
node.computed = true; node.computed = true;
node.optional = true; node.optional = true;
this.expect(tt.bracketR); this.expect(tt.bracketR);
return this.finishNode(node, "MemberExpression"); return this.finishNode(node, "OptionalMemberExpression");
} else if (this.eat(tt.parenL)) { } else if (this.eat(tt.parenL)) {
const possibleAsync = this.atPossibleAsync(base); const possibleAsync = this.atPossibleAsync(base);
@ -480,19 +480,23 @@ export default class ExpressionParser extends LValParser {
); );
node.optional = true; node.optional = true;
return this.finishNode(node, "CallExpression"); return this.finishNode(node, "OptionalCallExpression");
} else { } else {
node.object = base; node.object = base;
node.property = this.parseIdentifier(true); node.property = this.parseIdentifier(true);
node.computed = false; node.computed = false;
node.optional = true; node.optional = true;
return this.finishNode(node, "MemberExpression"); return this.finishNode(node, "OptionalMemberExpression");
} }
} else if (this.eat(tt.dot)) { } else if (this.eat(tt.dot)) {
const node = this.startNodeAt(startPos, startLoc); const node = this.startNodeAt(startPos, startLoc);
node.object = base; node.object = base;
node.property = this.parseMaybePrivateName(); node.property = this.parseMaybePrivateName();
node.computed = false; node.computed = false;
if (state.optionalChainMember) {
node.optional = false;
return this.finishNode(node, "OptionalMemberExpression");
}
return this.finishNode(node, "MemberExpression"); return this.finishNode(node, "MemberExpression");
} else if (this.eat(tt.bracketL)) { } else if (this.eat(tt.bracketL)) {
const node = this.startNodeAt(startPos, startLoc); const node = this.startNodeAt(startPos, startLoc);
@ -500,6 +504,10 @@ export default class ExpressionParser extends LValParser {
node.property = this.parseExpression(); node.property = this.parseExpression();
node.computed = true; node.computed = true;
this.expect(tt.bracketR); this.expect(tt.bracketR);
if (state.optionalChainMember) {
node.optional = false;
return this.finishNode(node, "OptionalMemberExpression");
}
return this.finishNode(node, "MemberExpression"); return this.finishNode(node, "MemberExpression");
} else if (!noCalls && this.match(tt.parenL)) { } else if (!noCalls && this.match(tt.parenL)) {
const possibleAsync = this.atPossibleAsync(base); const possibleAsync = this.atPossibleAsync(base);
@ -518,7 +526,11 @@ export default class ExpressionParser extends LValParser {
possibleAsync, possibleAsync,
refTrailingCommaPos, refTrailingCommaPos,
); );
this.finishCallExpression(node); if (!state.optionalChainMember) {
this.finishCallExpression(node);
} else {
this.finishOptionalCallExpression(node);
}
if (possibleAsync && this.shouldParseAsyncArrow()) { if (possibleAsync && this.shouldParseAsyncArrow()) {
state.stop = true; state.stop = true;
@ -542,7 +554,14 @@ export default class ExpressionParser extends LValParser {
const node = this.startNodeAt(startPos, startLoc); const node = this.startNodeAt(startPos, startLoc);
node.tag = base; node.tag = base;
node.quasi = this.parseTemplate(true); node.quasi = this.parseTemplate(true);
return this.finishNode(node, "TaggedTemplateExpression"); if (!state.optionalChainMember) {
return this.finishNode(node, "TaggedTemplateExpression");
} else {
this.raise(
startPos,
"Tagged Template Literals are not allowed in optionalChain",
);
}
} else { } else {
state.stop = true; state.stop = true;
return base; return base;
@ -572,6 +591,20 @@ export default class ExpressionParser extends LValParser {
return this.finishNode(node, "CallExpression"); return this.finishNode(node, "CallExpression");
} }
finishOptionalCallExpression(node: N.CallExpression): N.CallExpression {
if (node.callee.type === "Import") {
if (node.arguments.length !== 1) {
this.raise(node.start, "import() requires exactly one argument");
}
const importArg = node.arguments[0];
if (importArg && importArg.type === "SpreadElement") {
this.raise(importArg.start, "... is not allowed in import()");
}
}
return this.finishNode(node, "OptionalCallExpression");
}
parseCallExpressionArguments( parseCallExpressionArguments(
close: TokenType, close: TokenType,
possibleAsyncArrow: boolean, possibleAsyncArrow: boolean,
@ -1103,7 +1136,21 @@ export default class ExpressionParser extends LValParser {
} }
node.callee = this.parseNoCallExpr(); node.callee = this.parseNoCallExpr();
if (this.eat(tt.questionDot)) node.optional = true; if (
node.callee.type === "OptionalMemberExpression" ||
node.callee.type === "OptionalCallExpression"
) {
this.raise(
this.state.lastTokEnd,
"constructors in/after an Optional Chain are not allowed",
);
}
if (this.eat(tt.questionDot)) {
this.raise(
this.state.start,
"constructors in/after an Optional Chain are not allowed",
);
}
this.parseNewArguments(node); this.parseNewArguments(node);
return this.finishNode(node, "NewExpression"); return this.finishNode(node, "NewExpression");
} }

View File

@ -509,6 +509,18 @@ export type MemberExpression = NodeBase & {
computed: boolean, computed: boolean,
}; };
export type OptionalMemberExpression = NodeBase & {
type: "OptionalMemberExpression",
object: Expression | Super,
property: Expression,
computed: boolean,
optional: boolean,
};
export type OptionalCallExpression = CallOrNewBase & {
type: "OptionalCallExpression",
optional: boolean,
};
export type BindExpression = NodeBase & { export type BindExpression = NodeBase & {
type: "BindExpression", type: "BindExpression",
object: $ReadOnlyArray<?Expression>, object: $ReadOnlyArray<?Expression>,

View File

@ -1,9 +1 @@
new C?.() new C?.b.d()
new C?.(a, b)
new B?.C?.()
new B?.C?.(a, b)
new B?.C

View File

@ -1,3 +1,4 @@
{ {
"plugins": ["optionalChaining"] "plugins": ["optionalChaining"],
"throws": "constructors in/after an Optional Chain are not allowed (1:10)"
} }

View File

@ -1,450 +0,0 @@
{
"type": "File",
"start": 0,
"end": 66,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 9,
"column": 8
}
},
"program": {
"type": "Program",
"start": 0,
"end": 66,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 9,
"column": 8
}
},
"sourceType": "script",
"body": [
{
"type": "ExpressionStatement",
"start": 0,
"end": 9,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 9
}
},
"expression": {
"type": "NewExpression",
"start": 0,
"end": 9,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 9
}
},
"callee": {
"type": "Identifier",
"start": 4,
"end": 5,
"loc": {
"start": {
"line": 1,
"column": 4
},
"end": {
"line": 1,
"column": 5
},
"identifierName": "C"
},
"name": "C"
},
"optional": true,
"arguments": []
}
},
{
"type": "ExpressionStatement",
"start": 11,
"end": 24,
"loc": {
"start": {
"line": 3,
"column": 0
},
"end": {
"line": 3,
"column": 13
}
},
"expression": {
"type": "NewExpression",
"start": 11,
"end": 24,
"loc": {
"start": {
"line": 3,
"column": 0
},
"end": {
"line": 3,
"column": 13
}
},
"callee": {
"type": "Identifier",
"start": 15,
"end": 16,
"loc": {
"start": {
"line": 3,
"column": 4
},
"end": {
"line": 3,
"column": 5
},
"identifierName": "C"
},
"name": "C"
},
"optional": true,
"arguments": [
{
"type": "Identifier",
"start": 19,
"end": 20,
"loc": {
"start": {
"line": 3,
"column": 8
},
"end": {
"line": 3,
"column": 9
},
"identifierName": "a"
},
"name": "a"
},
{
"type": "Identifier",
"start": 22,
"end": 23,
"loc": {
"start": {
"line": 3,
"column": 11
},
"end": {
"line": 3,
"column": 12
},
"identifierName": "b"
},
"name": "b"
}
]
}
},
{
"type": "ExpressionStatement",
"start": 26,
"end": 38,
"loc": {
"start": {
"line": 5,
"column": 0
},
"end": {
"line": 5,
"column": 12
}
},
"expression": {
"type": "NewExpression",
"start": 26,
"end": 38,
"loc": {
"start": {
"line": 5,
"column": 0
},
"end": {
"line": 5,
"column": 12
}
},
"callee": {
"type": "MemberExpression",
"start": 30,
"end": 34,
"loc": {
"start": {
"line": 5,
"column": 4
},
"end": {
"line": 5,
"column": 8
}
},
"object": {
"type": "Identifier",
"start": 30,
"end": 31,
"loc": {
"start": {
"line": 5,
"column": 4
},
"end": {
"line": 5,
"column": 5
},
"identifierName": "B"
},
"name": "B"
},
"property": {
"type": "Identifier",
"start": 33,
"end": 34,
"loc": {
"start": {
"line": 5,
"column": 7
},
"end": {
"line": 5,
"column": 8
},
"identifierName": "C"
},
"name": "C"
},
"computed": false,
"optional": true
},
"optional": true,
"arguments": []
}
},
{
"type": "ExpressionStatement",
"start": 40,
"end": 56,
"loc": {
"start": {
"line": 7,
"column": 0
},
"end": {
"line": 7,
"column": 16
}
},
"expression": {
"type": "NewExpression",
"start": 40,
"end": 56,
"loc": {
"start": {
"line": 7,
"column": 0
},
"end": {
"line": 7,
"column": 16
}
},
"callee": {
"type": "MemberExpression",
"start": 44,
"end": 48,
"loc": {
"start": {
"line": 7,
"column": 4
},
"end": {
"line": 7,
"column": 8
}
},
"object": {
"type": "Identifier",
"start": 44,
"end": 45,
"loc": {
"start": {
"line": 7,
"column": 4
},
"end": {
"line": 7,
"column": 5
},
"identifierName": "B"
},
"name": "B"
},
"property": {
"type": "Identifier",
"start": 47,
"end": 48,
"loc": {
"start": {
"line": 7,
"column": 7
},
"end": {
"line": 7,
"column": 8
},
"identifierName": "C"
},
"name": "C"
},
"computed": false,
"optional": true
},
"optional": true,
"arguments": [
{
"type": "Identifier",
"start": 51,
"end": 52,
"loc": {
"start": {
"line": 7,
"column": 11
},
"end": {
"line": 7,
"column": 12
},
"identifierName": "a"
},
"name": "a"
},
{
"type": "Identifier",
"start": 54,
"end": 55,
"loc": {
"start": {
"line": 7,
"column": 14
},
"end": {
"line": 7,
"column": 15
},
"identifierName": "b"
},
"name": "b"
}
]
}
},
{
"type": "ExpressionStatement",
"start": 58,
"end": 66,
"loc": {
"start": {
"line": 9,
"column": 0
},
"end": {
"line": 9,
"column": 8
}
},
"expression": {
"type": "NewExpression",
"start": 58,
"end": 66,
"loc": {
"start": {
"line": 9,
"column": 0
},
"end": {
"line": 9,
"column": 8
}
},
"callee": {
"type": "MemberExpression",
"start": 62,
"end": 66,
"loc": {
"start": {
"line": 9,
"column": 4
},
"end": {
"line": 9,
"column": 8
}
},
"object": {
"type": "Identifier",
"start": 62,
"end": 63,
"loc": {
"start": {
"line": 9,
"column": 4
},
"end": {
"line": 9,
"column": 5
},
"identifierName": "B"
},
"name": "B"
},
"property": {
"type": "Identifier",
"start": 65,
"end": 66,
"loc": {
"start": {
"line": 9,
"column": 7
},
"end": {
"line": 9,
"column": 8
},
"identifierName": "C"
},
"name": "C"
},
"computed": false,
"optional": true
},
"arguments": []
}
}
],
"directives": []
}
}

View File

@ -5,3 +5,5 @@ func?.(a, b)
a?.func?.() a?.func?.()
a?.func?.(a, b) a?.func?.(a, b)
a.func?.()

View File

@ -1,29 +1,29 @@
{ {
"type": "File", "type": "File",
"start": 0, "start": 0,
"end": 52, "end": 64,
"loc": { "loc": {
"start": { "start": {
"line": 1, "line": 1,
"column": 0 "column": 0
}, },
"end": { "end": {
"line": 7, "line": 9,
"column": 15 "column": 10
} }
}, },
"program": { "program": {
"type": "Program", "type": "Program",
"start": 0, "start": 0,
"end": 52, "end": 64,
"loc": { "loc": {
"start": { "start": {
"line": 1, "line": 1,
"column": 0 "column": 0
}, },
"end": { "end": {
"line": 7, "line": 9,
"column": 15 "column": 10
} }
}, },
"sourceType": "script", "sourceType": "script",
@ -43,7 +43,7 @@
} }
}, },
"expression": { "expression": {
"type": "CallExpression", "type": "OptionalCallExpression",
"start": 0, "start": 0,
"end": 8, "end": 8,
"loc": { "loc": {
@ -92,7 +92,7 @@
} }
}, },
"expression": { "expression": {
"type": "CallExpression", "type": "OptionalCallExpression",
"start": 10, "start": 10,
"end": 22, "end": 22,
"loc": { "loc": {
@ -176,7 +176,7 @@
} }
}, },
"expression": { "expression": {
"type": "CallExpression", "type": "OptionalCallExpression",
"start": 24, "start": 24,
"end": 35, "end": 35,
"loc": { "loc": {
@ -190,7 +190,7 @@
} }
}, },
"callee": { "callee": {
"type": "MemberExpression", "type": "OptionalMemberExpression",
"start": 24, "start": 24,
"end": 31, "end": 31,
"loc": { "loc": {
@ -259,7 +259,7 @@
} }
}, },
"expression": { "expression": {
"type": "CallExpression", "type": "OptionalCallExpression",
"start": 37, "start": 37,
"end": 52, "end": 52,
"loc": { "loc": {
@ -273,7 +273,7 @@
} }
}, },
"callee": { "callee": {
"type": "MemberExpression", "type": "OptionalMemberExpression",
"start": 37, "start": 37,
"end": 44, "end": 44,
"loc": { "loc": {
@ -361,6 +361,88 @@
], ],
"optional": true "optional": true
} }
},
{
"type": "ExpressionStatement",
"start": 54,
"end": 64,
"loc": {
"start": {
"line": 9,
"column": 0
},
"end": {
"line": 9,
"column": 10
}
},
"expression": {
"type": "OptionalCallExpression",
"start": 54,
"end": 64,
"loc": {
"start": {
"line": 9,
"column": 0
},
"end": {
"line": 9,
"column": 10
}
},
"callee": {
"type": "MemberExpression",
"start": 54,
"end": 60,
"loc": {
"start": {
"line": 9,
"column": 0
},
"end": {
"line": 9,
"column": 6
}
},
"object": {
"type": "Identifier",
"start": 54,
"end": 55,
"loc": {
"start": {
"line": 9,
"column": 0
},
"end": {
"line": 9,
"column": 1
},
"identifierName": "a"
},
"name": "a"
},
"property": {
"type": "Identifier",
"start": 56,
"end": 60,
"loc": {
"start": {
"line": 9,
"column": 2
},
"end": {
"line": 9,
"column": 6
},
"identifierName": "func"
},
"name": "func"
},
"computed": false
},
"arguments": [],
"optional": true
}
} }
], ],
"directives": [] "directives": []

View File

@ -5,3 +5,7 @@ obj?.[expr]?.[other]
obj?.[true] obj?.[true]
obj?.[true]?.[true] obj?.[true]?.[true]
obj.a?.[expr]
obj.a?.[true]

View File

@ -1,29 +1,29 @@
{ {
"type": "File", "type": "File",
"start": 0, "start": 0,
"end": 67, "end": 97,
"loc": { "loc": {
"start": { "start": {
"line": 1, "line": 1,
"column": 0 "column": 0
}, },
"end": { "end": {
"line": 7, "line": 11,
"column": 19 "column": 13
} }
}, },
"program": { "program": {
"type": "Program", "type": "Program",
"start": 0, "start": 0,
"end": 67, "end": 97,
"loc": { "loc": {
"start": { "start": {
"line": 1, "line": 1,
"column": 0 "column": 0
}, },
"end": { "end": {
"line": 7, "line": 11,
"column": 19 "column": 13
} }
}, },
"sourceType": "script", "sourceType": "script",
@ -43,7 +43,7 @@
} }
}, },
"expression": { "expression": {
"type": "MemberExpression", "type": "OptionalMemberExpression",
"start": 0, "start": 0,
"end": 11, "end": 11,
"loc": { "loc": {
@ -109,7 +109,7 @@
} }
}, },
"expression": { "expression": {
"type": "MemberExpression", "type": "OptionalMemberExpression",
"start": 13, "start": 13,
"end": 33, "end": 33,
"loc": { "loc": {
@ -123,7 +123,7 @@
} }
}, },
"object": { "object": {
"type": "MemberExpression", "type": "OptionalMemberExpression",
"start": 13, "start": 13,
"end": 24, "end": 24,
"loc": { "loc": {
@ -209,7 +209,7 @@
} }
}, },
"expression": { "expression": {
"type": "MemberExpression", "type": "OptionalMemberExpression",
"start": 35, "start": 35,
"end": 46, "end": 46,
"loc": { "loc": {
@ -274,7 +274,7 @@
} }
}, },
"expression": { "expression": {
"type": "MemberExpression", "type": "OptionalMemberExpression",
"start": 48, "start": 48,
"end": 67, "end": 67,
"loc": { "loc": {
@ -288,7 +288,7 @@
} }
}, },
"object": { "object": {
"type": "MemberExpression", "type": "OptionalMemberExpression",
"start": 48, "start": 48,
"end": 59, "end": 59,
"loc": { "loc": {
@ -356,6 +356,203 @@
"computed": true, "computed": true,
"optional": true "optional": true
} }
},
{
"type": "ExpressionStatement",
"start": 69,
"end": 82,
"loc": {
"start": {
"line": 9,
"column": 0
},
"end": {
"line": 9,
"column": 13
}
},
"expression": {
"type": "OptionalMemberExpression",
"start": 69,
"end": 82,
"loc": {
"start": {
"line": 9,
"column": 0
},
"end": {
"line": 9,
"column": 13
}
},
"object": {
"type": "MemberExpression",
"start": 69,
"end": 74,
"loc": {
"start": {
"line": 9,
"column": 0
},
"end": {
"line": 9,
"column": 5
}
},
"object": {
"type": "Identifier",
"start": 69,
"end": 72,
"loc": {
"start": {
"line": 9,
"column": 0
},
"end": {
"line": 9,
"column": 3
},
"identifierName": "obj"
},
"name": "obj"
},
"property": {
"type": "Identifier",
"start": 73,
"end": 74,
"loc": {
"start": {
"line": 9,
"column": 4
},
"end": {
"line": 9,
"column": 5
},
"identifierName": "a"
},
"name": "a"
},
"computed": false
},
"property": {
"type": "Identifier",
"start": 77,
"end": 81,
"loc": {
"start": {
"line": 9,
"column": 8
},
"end": {
"line": 9,
"column": 12
},
"identifierName": "expr"
},
"name": "expr"
},
"computed": true,
"optional": true
}
},
{
"type": "ExpressionStatement",
"start": 84,
"end": 97,
"loc": {
"start": {
"line": 11,
"column": 0
},
"end": {
"line": 11,
"column": 13
}
},
"expression": {
"type": "OptionalMemberExpression",
"start": 84,
"end": 97,
"loc": {
"start": {
"line": 11,
"column": 0
},
"end": {
"line": 11,
"column": 13
}
},
"object": {
"type": "MemberExpression",
"start": 84,
"end": 89,
"loc": {
"start": {
"line": 11,
"column": 0
},
"end": {
"line": 11,
"column": 5
}
},
"object": {
"type": "Identifier",
"start": 84,
"end": 87,
"loc": {
"start": {
"line": 11,
"column": 0
},
"end": {
"line": 11,
"column": 3
},
"identifierName": "obj"
},
"name": "obj"
},
"property": {
"type": "Identifier",
"start": 88,
"end": 89,
"loc": {
"start": {
"line": 11,
"column": 4
},
"end": {
"line": 11,
"column": 5
},
"identifierName": "a"
},
"name": "a"
},
"computed": false
},
"property": {
"type": "BooleanLiteral",
"start": 92,
"end": 96,
"loc": {
"start": {
"line": 11,
"column": 8
},
"end": {
"line": 11,
"column": 12
}
},
"value": true
},
"computed": true,
"optional": true
}
} }
], ],
"directives": [] "directives": []

View File

@ -1,3 +1,5 @@
foo?.bar foo?.bar
foo?.bar?.baz foo?.bar?.baz
foo.bar?.baz

View File

@ -1,29 +1,29 @@
{ {
"type": "File", "type": "File",
"start": 0, "start": 0,
"end": 23, "end": 37,
"loc": { "loc": {
"start": { "start": {
"line": 1, "line": 1,
"column": 0 "column": 0
}, },
"end": { "end": {
"line": 3, "line": 5,
"column": 13 "column": 12
} }
}, },
"program": { "program": {
"type": "Program", "type": "Program",
"start": 0, "start": 0,
"end": 23, "end": 37,
"loc": { "loc": {
"start": { "start": {
"line": 1, "line": 1,
"column": 0 "column": 0
}, },
"end": { "end": {
"line": 3, "line": 5,
"column": 13 "column": 12
} }
}, },
"sourceType": "script", "sourceType": "script",
@ -43,7 +43,7 @@
} }
}, },
"expression": { "expression": {
"type": "MemberExpression", "type": "OptionalMemberExpression",
"start": 0, "start": 0,
"end": 8, "end": 8,
"loc": { "loc": {
@ -109,7 +109,7 @@
} }
}, },
"expression": { "expression": {
"type": "MemberExpression", "type": "OptionalMemberExpression",
"start": 10, "start": 10,
"end": 23, "end": 23,
"loc": { "loc": {
@ -123,7 +123,7 @@
} }
}, },
"object": { "object": {
"type": "MemberExpression", "type": "OptionalMemberExpression",
"start": 10, "start": 10,
"end": 18, "end": 18,
"loc": { "loc": {
@ -193,6 +193,105 @@
"computed": false, "computed": false,
"optional": true "optional": true
} }
},
{
"type": "ExpressionStatement",
"start": 25,
"end": 37,
"loc": {
"start": {
"line": 5,
"column": 0
},
"end": {
"line": 5,
"column": 12
}
},
"expression": {
"type": "OptionalMemberExpression",
"start": 25,
"end": 37,
"loc": {
"start": {
"line": 5,
"column": 0
},
"end": {
"line": 5,
"column": 12
}
},
"object": {
"type": "MemberExpression",
"start": 25,
"end": 32,
"loc": {
"start": {
"line": 5,
"column": 0
},
"end": {
"line": 5,
"column": 7
}
},
"object": {
"type": "Identifier",
"start": 25,
"end": 28,
"loc": {
"start": {
"line": 5,
"column": 0
},
"end": {
"line": 5,
"column": 3
},
"identifierName": "foo"
},
"name": "foo"
},
"property": {
"type": "Identifier",
"start": 29,
"end": 32,
"loc": {
"start": {
"line": 5,
"column": 4
},
"end": {
"line": 5,
"column": 7
},
"identifierName": "bar"
},
"name": "bar"
},
"computed": false
},
"property": {
"type": "Identifier",
"start": 34,
"end": 37,
"loc": {
"start": {
"line": 5,
"column": 9
},
"end": {
"line": 5,
"column": 12
},
"identifierName": "baz"
},
"name": "baz"
},
"computed": false,
"optional": true
}
} }
], ],
"directives": [] "directives": []

View File

@ -0,0 +1,3 @@
{
"plugins": ["optionalChaining"]
}

View File

@ -0,0 +1,149 @@
{
"type": "File",
"start": 0,
"end": 8,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 8
}
},
"program": {
"type": "Program",
"start": 0,
"end": 8,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 8
}
},
"sourceType": "script",
"body": [
{
"type": "ExpressionStatement",
"start": 0,
"end": 8,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 8
}
},
"expression": {
"type": "OptionalCallExpression",
"start": 0,
"end": 8,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 8
}
},
"callee": {
"type": "OptionalMemberExpression",
"start": 0,
"end": 6,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 6
}
},
"object": {
"type": "MemberExpression",
"start": 0,
"end": 3,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 3
}
},
"object": {
"type": "Identifier",
"start": 0,
"end": 1,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 1
},
"identifierName": "a"
},
"name": "a"
},
"property": {
"type": "Identifier",
"start": 2,
"end": 3,
"loc": {
"start": {
"line": 1,
"column": 2
},
"end": {
"line": 1,
"column": 3
},
"identifierName": "b"
},
"name": "b"
},
"computed": false
},
"property": {
"type": "Identifier",
"start": 5,
"end": 6,
"loc": {
"start": {
"line": 1,
"column": 5
},
"end": {
"line": 1,
"column": 6
},
"identifierName": "c"
},
"name": "c"
},
"computed": false,
"optional": true
},
"arguments": []
}
}
],
"directives": []
}
}

View File

@ -0,0 +1,4 @@
{
"plugins": ["optionalChaining"],
"throws": "constructors in/after an Optional Chain are not allowed (1:7)"
}

View File

@ -0,0 +1,4 @@
{
"plugins" : ["optionalChaining"],
"throws" : "Tagged Template Literals are not allowed in optionalChain (1:0)"
}

View File

@ -0,0 +1,5 @@
(a?.b).c;
(a?.b).c();
(a?.b)?.c.d?.e;

View File

@ -0,0 +1,3 @@
{
"plugins": ["optionalChaining"]
}

View File

@ -0,0 +1,428 @@
{
"type": "File",
"start": 0,
"end": 39,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 5,
"column": 15
}
},
"program": {
"type": "Program",
"start": 0,
"end": 39,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 5,
"column": 15
}
},
"sourceType": "script",
"body": [
{
"type": "ExpressionStatement",
"start": 0,
"end": 9,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 9
}
},
"expression": {
"type": "MemberExpression",
"start": 0,
"end": 8,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 8
}
},
"object": {
"type": "OptionalMemberExpression",
"start": 1,
"end": 5,
"loc": {
"start": {
"line": 1,
"column": 1
},
"end": {
"line": 1,
"column": 5
}
},
"object": {
"type": "Identifier",
"start": 1,
"end": 2,
"loc": {
"start": {
"line": 1,
"column": 1
},
"end": {
"line": 1,
"column": 2
},
"identifierName": "a"
},
"name": "a"
},
"property": {
"type": "Identifier",
"start": 4,
"end": 5,
"loc": {
"start": {
"line": 1,
"column": 4
},
"end": {
"line": 1,
"column": 5
},
"identifierName": "b"
},
"name": "b"
},
"computed": false,
"optional": true,
"extra": {
"parenthesized": true,
"parenStart": 0
}
},
"property": {
"type": "Identifier",
"start": 7,
"end": 8,
"loc": {
"start": {
"line": 1,
"column": 7
},
"end": {
"line": 1,
"column": 8
},
"identifierName": "c"
},
"name": "c"
},
"computed": false
}
},
{
"type": "ExpressionStatement",
"start": 11,
"end": 22,
"loc": {
"start": {
"line": 3,
"column": 0
},
"end": {
"line": 3,
"column": 11
}
},
"expression": {
"type": "CallExpression",
"start": 11,
"end": 21,
"loc": {
"start": {
"line": 3,
"column": 0
},
"end": {
"line": 3,
"column": 10
}
},
"callee": {
"type": "MemberExpression",
"start": 11,
"end": 19,
"loc": {
"start": {
"line": 3,
"column": 0
},
"end": {
"line": 3,
"column": 8
}
},
"object": {
"type": "OptionalMemberExpression",
"start": 12,
"end": 16,
"loc": {
"start": {
"line": 3,
"column": 1
},
"end": {
"line": 3,
"column": 5
}
},
"object": {
"type": "Identifier",
"start": 12,
"end": 13,
"loc": {
"start": {
"line": 3,
"column": 1
},
"end": {
"line": 3,
"column": 2
},
"identifierName": "a"
},
"name": "a"
},
"property": {
"type": "Identifier",
"start": 15,
"end": 16,
"loc": {
"start": {
"line": 3,
"column": 4
},
"end": {
"line": 3,
"column": 5
},
"identifierName": "b"
},
"name": "b"
},
"computed": false,
"optional": true,
"extra": {
"parenthesized": true,
"parenStart": 11
}
},
"property": {
"type": "Identifier",
"start": 18,
"end": 19,
"loc": {
"start": {
"line": 3,
"column": 7
},
"end": {
"line": 3,
"column": 8
},
"identifierName": "c"
},
"name": "c"
},
"computed": false
},
"arguments": []
}
},
{
"type": "ExpressionStatement",
"start": 24,
"end": 39,
"loc": {
"start": {
"line": 5,
"column": 0
},
"end": {
"line": 5,
"column": 15
}
},
"expression": {
"type": "OptionalMemberExpression",
"start": 24,
"end": 38,
"loc": {
"start": {
"line": 5,
"column": 0
},
"end": {
"line": 5,
"column": 14
}
},
"object": {
"type": "OptionalMemberExpression",
"start": 24,
"end": 35,
"loc": {
"start": {
"line": 5,
"column": 0
},
"end": {
"line": 5,
"column": 11
}
},
"object": {
"type": "OptionalMemberExpression",
"start": 24,
"end": 33,
"loc": {
"start": {
"line": 5,
"column": 0
},
"end": {
"line": 5,
"column": 9
}
},
"object": {
"type": "OptionalMemberExpression",
"start": 25,
"end": 29,
"loc": {
"start": {
"line": 5,
"column": 1
},
"end": {
"line": 5,
"column": 5
}
},
"object": {
"type": "Identifier",
"start": 25,
"end": 26,
"loc": {
"start": {
"line": 5,
"column": 1
},
"end": {
"line": 5,
"column": 2
},
"identifierName": "a"
},
"name": "a"
},
"property": {
"type": "Identifier",
"start": 28,
"end": 29,
"loc": {
"start": {
"line": 5,
"column": 4
},
"end": {
"line": 5,
"column": 5
},
"identifierName": "b"
},
"name": "b"
},
"computed": false,
"optional": true,
"extra": {
"parenthesized": true,
"parenStart": 24
}
},
"property": {
"type": "Identifier",
"start": 32,
"end": 33,
"loc": {
"start": {
"line": 5,
"column": 8
},
"end": {
"line": 5,
"column": 9
},
"identifierName": "c"
},
"name": "c"
},
"computed": false,
"optional": true
},
"property": {
"type": "Identifier",
"start": 34,
"end": 35,
"loc": {
"start": {
"line": 5,
"column": 10
},
"end": {
"line": 5,
"column": 11
},
"identifierName": "d"
},
"name": "d"
},
"computed": false,
"optional": false
},
"property": {
"type": "Identifier",
"start": 37,
"end": 38,
"loc": {
"start": {
"line": 5,
"column": 13
},
"end": {
"line": 5,
"column": 14
},
"identifierName": "e"
},
"name": "e"
},
"computed": false,
"optional": true
}
}
],
"directives": []
}
}

View File

@ -43,7 +43,7 @@
} }
}, },
"expression": { "expression": {
"type": "MemberExpression", "type": "OptionalMemberExpression",
"start": 0, "start": 0,
"end": 13, "end": 13,
"loc": { "loc": {
@ -57,7 +57,7 @@
} }
}, },
"object": { "object": {
"type": "MemberExpression", "type": "OptionalMemberExpression",
"start": 0, "start": 0,
"end": 10, "end": 10,
"loc": { "loc": {
@ -71,7 +71,7 @@
} }
}, },
"object": { "object": {
"type": "MemberExpression", "type": "OptionalMemberExpression",
"start": 0, "start": 0,
"end": 8, "end": 8,
"loc": { "loc": {
@ -85,7 +85,7 @@
} }
}, },
"object": { "object": {
"type": "MemberExpression", "type": "OptionalMemberExpression",
"start": 0, "start": 0,
"end": 6, "end": 6,
"loc": { "loc": {
@ -99,7 +99,7 @@
} }
}, },
"object": { "object": {
"type": "MemberExpression", "type": "OptionalMemberExpression",
"start": 0, "start": 0,
"end": 4, "end": 4,
"loc": { "loc": {
@ -166,7 +166,8 @@
}, },
"name": "c" "name": "c"
}, },
"computed": false "computed": false,
"optional": false
}, },
"property": { "property": {
"type": "Identifier", "type": "Identifier",
@ -185,7 +186,8 @@
}, },
"name": "d" "name": "d"
}, },
"computed": false "computed": false,
"optional": false
}, },
"property": { "property": {
"type": "Identifier", "type": "Identifier",
@ -204,7 +206,8 @@
}, },
"name": "e" "name": "e"
}, },
"computed": false "computed": false,
"optional": false
}, },
"property": { "property": {
"type": "Identifier", "type": "Identifier",
@ -242,7 +245,7 @@
} }
}, },
"expression": { "expression": {
"type": "MemberExpression", "type": "OptionalMemberExpression",
"start": 15, "start": 15,
"end": 27, "end": 27,
"loc": { "loc": {
@ -256,7 +259,7 @@
} }
}, },
"object": { "object": {
"type": "MemberExpression", "type": "OptionalMemberExpression",
"start": 15, "start": 15,
"end": 25, "end": 25,
"loc": { "loc": {
@ -270,7 +273,7 @@
} }
}, },
"object": { "object": {
"type": "MemberExpression", "type": "OptionalMemberExpression",
"start": 15, "start": 15,
"end": 23, "end": 23,
"loc": { "loc": {
@ -403,7 +406,8 @@
}, },
"name": "e" "name": "e"
}, },
"computed": false "computed": false,
"optional": false
}, },
"property": { "property": {
"type": "Identifier", "type": "Identifier",
@ -422,7 +426,8 @@
}, },
"name": "f" "name": "f"
}, },
"computed": false "computed": false,
"optional": false
} }
} }
], ],