Skip to content

Commit

Permalink
fix(parser): fixes loc tracking for optional chaining
Browse files Browse the repository at this point in the history
  • Loading branch information
KFlash committed Aug 3, 2019
1 parent 32f347f commit e875e14
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 12 deletions.
24 changes: 13 additions & 11 deletions src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3677,8 +3677,8 @@ export function parseMemberOrUpdateExpression(
nextToken(parser, context | Context.AllowRegExp);

const { tokenPos, linePos, colPos } = parser;

const property = parseExpressions(parser, context, inGroup, 1, tokenPos, linePos, colPos);

consume(parser, context, Token.RightBracket);

parser.assignable = isOptional ? AssignmentKind.CannotAssign : AssignmentKind.Assignable;
Expand Down Expand Up @@ -3780,18 +3780,19 @@ export function parseMemberOrUpdateExpression(
return parseMemberOrUpdateExpression(parser, context, expr, 0, isOptional, optionalChaining, start, line, column);
}

return optionalChaining ? parserOptionalChain(parser, context, expr, start, line, column) : expr;
return optionalChaining ? parserOptionalChain(parser, context, expr) : expr;
}

function parserOptionalChain(
parser: ParserState,
context: Context,
expr: ESTree.Expression,
start: number,
line: number,
column: number
): ESTree.OptionalChain {
return finishNode(parser, context, start, line, column, {
/**
* Parses optional chain
*
* @param parser Parser object
* @param context Context masks
* @param expr ESTree AST node
*/
function parserOptionalChain(parser: ParserState, context: Context, expr: ESTree.Expression): ESTree.OptionalChain {
const { tokenPos, linePos, colPos } = parser;
return finishNode(parser, context, tokenPos, linePos, colPos, {
type: 'OptionalChain',
expression: expr
} as any);
Expand Down Expand Up @@ -8552,6 +8553,7 @@ function parseJSXOpeningFragmentOrSelfCloseElement(
const tagName = parseJSXElementName(parser, context, parser.tokenPos, parser.linePos, parser.colPos);
const attributes = parseJSXAttributes(parser, context);
const selfClosing = parser.token === Token.Divide;

if (parser.token === Token.GreaterThan) {
scanJSXToken(parser);
} else {
Expand Down
5 changes: 5 additions & 0 deletions test/parser/next/nullCoalescing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ describe('Next - Null Coalescing', () => {
`0 ?? 3`,
'(a || b)',
'1 ?? 3',
`foo ||bar;
(x => x)|| bar;
(function a(x){return x;})|| 2;
0||(function(){return alpha;});
a ?? (b || c);`,
'var result = obj??key;',
'arr??[idx]',
'func??(arg)',
Expand Down
77 changes: 76 additions & 1 deletion test/parser/next/optional-chaining.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,20 @@ describe('Next - Optional chaining', () => {
'0?.valueOf()',
'[]?.length',
'undefined?.["valueOf"]()',
'const a = b.map(p => p.d?.e?.f);',
'const a = b.map(p => p.c?.d?.e ?? "(string)");',
'f?.(arg0, arg1)',
'true?.(123)',
`function isInvoked(obj) {
let invoked = false;
obj?.a.b.m(invoked = true);
return invoked;
}`,
'a.b?.c?.d',
'obj ? ["a", "b", "c"].map(x => x+x) : []',
'const a = b?.c?.[d]?.e;',
'const a = b?.c();',
'const { a, b } = c.d?.e;',
'1?.["valueOf"]()',
'() => 0?.()',
'() => 1?.()',
Expand Down Expand Up @@ -125,6 +139,7 @@ describe('Next - Optional chaining', () => {
}

fail('Expressions - Optional chaining (fail)', [
['x?.[y] = foo', Context.OptionsNext],
['0, [{ set y(val) {}}?.y] = [23];', Context.OptionsNext],
['0, { x: y?.z = 42 } = { x: 23 };', Context.OptionsNext],
['0, { x: y?.z = 42 } = { x: 23 };', Context.OptionsNext | Context.OptionsWebCompat],
Expand Down Expand Up @@ -198,7 +213,7 @@ describe('Next - Optional chaining', () => {
['const o = { C: class {} }; new o?.C();', Context.OptionsNext | Context.OptionsWebCompat],
['const o = { C: class {} }; new o?.["C"]();', Context.OptionsNext | Context.OptionsWebCompat],
['class C {} new C?.();', Context.OptionsNext | Context.OptionsWebCompat],
['function tag() {} tag?', Context.OptionsNext | Context.OptionsWebCompat],
['function tag() {} tag?.``', Context.OptionsNext | Context.OptionsWebCompat],
['const o = { tag() {} }; o?.tag``', Context.OptionsNext | Context.OptionsWebCompat],
['import?.("foo")', Context.OptionsNext | Context.OptionsWebCompat],
['new new class {}()?.constructor?.();', Context.OptionsNext | Context.OptionsWebCompat],
Expand All @@ -207,6 +222,66 @@ describe('Next - Optional chaining', () => {
]);

pass('Next - Optional chaining (pass)', [
[
`(a?.b).c();`,
Context.OptionsNext | Context.OptionsRanges | Context.OptionsWebCompat,
{
body: [
{
end: 11,
expression: {
arguments: [],
callee: {
computed: false,
end: 8,
object: {
end: 5,
expression: {
computed: false,
end: 5,
object: {
end: 2,
name: 'a',
start: 1,
type: 'Identifier'
},
optional: true,
property: {
end: 5,
name: 'b',
start: 4,
type: 'Identifier'
},
start: 1,
type: 'MemberExpression'
},
start: 5,
type: 'OptionalChain'
},
property: {
end: 8,
name: 'c',
start: 7,
type: 'Identifier'
},
start: 0,
type: 'MemberExpression'
},
end: 10,
optional: false,
start: 0,
type: 'CallExpression'
},
start: 0,
type: 'ExpressionStatement'
}
],
end: 11,
sourceType: 'script',
start: 0,
type: 'Program'
}
],
[
`a?.(...args);`,
Context.OptionsNext,
Expand Down

0 comments on commit e875e14

Please sign in to comment.