diff --git a/javascript/extractor/src/com/semmle/jcorn/AngularExpressionParser.java b/javascript/extractor/src/com/semmle/jcorn/AngularExpressionParser.java index 79c1a1e30dbe..f3245d573352 100644 --- a/javascript/extractor/src/com/semmle/jcorn/AngularExpressionParser.java +++ b/javascript/extractor/src/com/semmle/jcorn/AngularExpressionParser.java @@ -30,14 +30,13 @@ protected Expression buildBinary( boolean logical) { // Angular pipe expression: `x|f:a` is desugared to `f(x, a)` if (op.equals("|")) { - DestructuringErrors refDestructuringErrors = new DestructuringErrors(); List arguments = new ArrayList<>(); arguments.add(left); while (this.type == TokenType.colon) { this.next(); int argStartPos = this.pos; Position argStartLocation = this.curPosition(); - Expression arg = parseMaybeUnary(refDestructuringErrors, false); + Expression arg = parseMaybeUnary(false); arguments.add(parseExprOp(arg, argStartPos, argStartLocation, TokenType.plusMin.binop, true)); } SourceLocation loc = new SourceLocation(startLoc); @@ -50,10 +49,10 @@ protected Expression buildBinary( } @Override - protected Expression parseExprAtom(DestructuringErrors refDestructuringErrors) { + protected Expression parseExprAtom() { // Parse postfix "!" operator Position startLoc = this.startLoc; - Expression expr = super.parseExprAtom(refDestructuringErrors); + Expression expr = super.parseExprAtom(); if (this.type == TokenType.prefix && "!".equals(this.value)) { this.next(); // consume "!" token return finishNode(new NonNullAssertion(new SourceLocation(startLoc), expr)); diff --git a/javascript/extractor/src/com/semmle/jcorn/CustomParser.java b/javascript/extractor/src/com/semmle/jcorn/CustomParser.java index 7170aa3e5217..cb963a4cd380 100644 --- a/javascript/extractor/src/com/semmle/jcorn/CustomParser.java +++ b/javascript/extractor/src/com/semmle/jcorn/CustomParser.java @@ -90,7 +90,7 @@ protected CatchClause parseCatchClause(Position startLoc) { if (this.eat(TokenType.parenL)) { param = this.parseBindingAtom(); this.checkLVal(param, true, null); - if (this.eat(TokenType._if)) guard = this.parseExpression(false, null); + if (this.eat(TokenType._if)) guard = this.parseExpression(false); this.expect(TokenType.parenR); } else if (!options.esnext()) { this.unexpected(); @@ -123,7 +123,7 @@ protected Statement parseVarStatement(Position startLoc, String kind) { } @Override - protected Expression parseExprAtom(DestructuringErrors refDestructuringErrors) { + protected Expression parseExprAtom() { Position startLoc = this.startLoc; if (options.mozExtensions() && this.isContextual("let")) { this.next(); @@ -141,9 +141,9 @@ protected Expression parseExprAtom(DestructuringErrors refDestructuringErrors) { if (this.type == TokenType.comma || this.type == TokenType.bracketR || this.type == TokenType.ellipsis) { - elements = this.parseExprList(TokenType.bracketR, true, true, refDestructuringErrors); + elements = this.parseExprList(TokenType.bracketR, true, true); } else { - Expression firstExpr = this.parseMaybeAssign(false, refDestructuringErrors, null); + Expression firstExpr = this.parseMaybeAssign(false, null); // check whether this is a postfix array comprehension if (this.type == TokenType._for || this.type == TokenType._if) { ComprehensionExpression c = this.parseComprehension(startLoc, false, firstExpr); @@ -154,7 +154,7 @@ protected Expression parseExprAtom(DestructuringErrors refDestructuringErrors) { elements = new ArrayList(); elements.add(firstExpr); elements.addAll( - this.parseExprList(TokenType.bracketR, true, true, refDestructuringErrors)); + this.parseExprList(TokenType.bracketR, true, true)); } } return this.finishNode(new ArrayExpression(new SourceLocation(startLoc), elements)); @@ -164,7 +164,7 @@ protected Expression parseExprAtom(DestructuringErrors refDestructuringErrors) { Identifier buildinName = this.parseIdent(true); Identifier name = this.finishNode(new Identifier(new SourceLocation(startLoc), "%" + buildinName.getName())); this.expect(TokenType.parenL); - List args = this.parseExprList(TokenType.parenR, false, false, null); + List args = this.parseExprList(TokenType.parenR, false, false); CallExpression node = new CallExpression( new SourceLocation(startLoc), name, new ArrayList<>(), args, false, false); @@ -184,7 +184,7 @@ protected Expression parseExprAtom(DestructuringErrors refDestructuringErrors) { } return attr; } else { - return super.parseExprAtom(refDestructuringErrors); + return super.parseExprAtom(); } } @@ -197,7 +197,7 @@ protected Node parseLetExpression(Position startLoc, boolean maybeStatement) { if (this.type == TokenType.braceL) { if (!maybeStatement) { // must be the start of an object literal - Expression body = this.parseObj(false, null); + Expression body = this.parseObj(false); return this.finishNode( new LetExpression(new SourceLocation(startLoc), decl.getDeclarations(), body)); } @@ -212,7 +212,7 @@ protected Node parseLetExpression(Position startLoc, boolean maybeStatement) { return this.finishNode( new LetStatement(new SourceLocation(startLoc), decl.getDeclarations(), body)); } else { - Expression body = this.parseExpression(false, null); + Expression body = this.parseExpression(false); return this.finishNode( new LetExpression(new SourceLocation(startLoc), decl.getDeclarations(), body)); } @@ -283,13 +283,12 @@ private boolean eatDoubleColon() { // accept `yield` in non-generator functions @Override - protected Expression parseMaybeAssign( - boolean noIn, DestructuringErrors refDestructuringErrors, AfterLeftParse afterLeftParse) { + protected Expression parseMaybeAssign(boolean noIn, AfterLeftParse afterLeftParse) { if (options.mozExtensions() && isContextual("yield")) { if (!this.inFunction) this.raise(this.startLoc, "Yield not in function"); return this.parseYield(); } - return super.parseMaybeAssign(noIn, refDestructuringErrors, afterLeftParse); + return super.parseMaybeAssign(noIn, afterLeftParse); } // add parsing of comprehensions @@ -309,12 +308,12 @@ protected ComprehensionExpression parseComprehension( } else { this.expect(TokenType._in); } - Expression right = this.parseExpression(false, null); + Expression right = this.parseExpression(false); this.expect(TokenType.parenR); blocks.add(this.finishNode(new ComprehensionBlock(blockStart, (IPattern) left, right, of))); } Expression filter = this.eat(TokenType._if) ? this.parseParenExpression() : null; - if (body == null) body = this.parseExpression(false, null); + if (body == null) body = this.parseExpression(false); return new ComprehensionExpression( new SourceLocation(startLoc), body, blocks, filter, isGenerator); @@ -353,13 +352,11 @@ protected Expression parseParenAndDistinguishExpression(boolean canBeArrow) { @Override protected boolean parseParenthesisedExpression( - DestructuringErrors refDestructuringErrors, boolean allowTrailingComma, ParenthesisedExpressions parenExprs, boolean first) { boolean cont = - super.parseParenthesisedExpression( - refDestructuringErrors, allowTrailingComma, parenExprs, first); + super.parseParenthesisedExpression(allowTrailingComma, parenExprs, first); if (options.mozExtensions() && parenExprs.exprList.size() == 1 && this.type == TokenType._for) { Expression body = parenExprs.exprList.remove(0); ComprehensionExpression c = parseComprehension(body.getLoc().getStart(), true, body); @@ -399,7 +396,7 @@ protected Expression parseNew() { && options.mozExtensions() && !canInsertSemicolon() && this.type == TokenType.braceL) { - ((NewExpression) res).getArguments().add(this.parseObj(false, null)); + ((NewExpression) res).getArguments().add(this.parseObj(false)); res = this.finishNode(res); } return res; @@ -465,7 +462,7 @@ protected Pair parseSubscript( if (options.e4x() && this.eat(TokenType.dot)) { SourceLocation start = new SourceLocation(startLoc); if (this.eat(TokenType.parenL)) { - Expression filter = parseExpression(false, null); + Expression filter = parseExpression(false); this.expect(TokenType.parenR); return Pair.make(this.finishNode(new XMLFilterExpression(start, base, filter)), true); } @@ -515,7 +512,7 @@ protected Expression parsePropertySelector(SourceLocation start) { */ protected Expression parseAttributeIdentifier(SourceLocation start) { if (this.eat(TokenType.bracketL)) { - Expression idx = parseExpression(false, null); + Expression idx = parseExpression(false); this.expect(TokenType.bracketR); return this.finishNode(new XMLAttributeSelector(start, idx, true)); } else { @@ -533,7 +530,7 @@ protected Expression parseDecoratorBody() { // followed by a right bracket, which will later be converted by // `decoratorToAttributeSelector` below List elements = new ArrayList<>(); - elements.add(parseExpression(false, null)); + elements.add(parseExpression(false)); this.expect(TokenType.bracketR); return this.finishNode(new ArrayExpression(start, elements)); } diff --git a/javascript/extractor/src/com/semmle/jcorn/ESNextParser.java b/javascript/extractor/src/com/semmle/jcorn/ESNextParser.java index 1eff68a30385..edc6fb9bf632 100644 --- a/javascript/extractor/src/com/semmle/jcorn/ESNextParser.java +++ b/javascript/extractor/src/com/semmle/jcorn/ESNextParser.java @@ -59,17 +59,14 @@ public ESNextParser(Options options, String input, int startPos) { */ @Override - protected Property parseProperty( - boolean isPattern, - DestructuringErrors refDestructuringErrors, - Map propHash) { + protected Property parseProperty(boolean isPattern, Map propHash) { Position start = this.startLoc; List decorators = parseDecorators(); Property prop = null; if (this.type == TokenType.ellipsis) { - SpreadElement spread = this.parseSpread(null); + SpreadElement spread = this.parseSpread(); Expression val; if (isPattern) val = new RestElement(spread.getLoc(), spread.getArgument()); else val = spread; @@ -79,7 +76,7 @@ protected Property parseProperty( new SourceLocation(start), null, val, Property.Kind.INIT.name(), false, false)); } - if (prop == null) prop = super.parseProperty(isPattern, refDestructuringErrors, propHash); + if (prop == null) prop = super.parseProperty(isPattern, propHash); prop.addDecorators(decorators); @@ -129,7 +126,7 @@ protected FieldDefinition parseFieldDefinition(PropertyInfo pi, boolean isStatic this.next(); boolean oldInFunc = this.inFunction; this.inFunction = true; - value = parseMaybeAssign(false, null, null); + value = parseMaybeAssign(false, null); this.inFunction = oldInFunc; } this.semicolon(); @@ -220,7 +217,7 @@ protected Statement parseStatement(boolean declaration, boolean topLevel, Set decorators = parseDecorators(); ClassExpression ce = (ClassExpression) this.parseClass(startLoc, false); @@ -232,7 +229,7 @@ protected Expression parseExprAtom(DestructuringErrors refDestructuringErrors) { this.next(); int innerStart = this.start; Position innerStartLoc = this.startLoc; - Expression callee = parseSubscripts(parseExprAtom(null), innerStart, innerStartLoc, true); + Expression callee = parseSubscripts(parseExprAtom(), innerStart, innerStartLoc, true); if (!(callee instanceof MemberExpression)) this.raiseRecoverable(callee, "Binding should be performed on a member expression."); return this.finishNode(new BindExpression(startLoc, null, callee)); @@ -246,7 +243,7 @@ protected Expression parseExprAtom(DestructuringErrors refDestructuringErrors) { this.expect(TokenType.parenL); return parseDynamicImport(startLoc); } - return super.parseExprAtom(refDestructuringErrors); + return super.parseExprAtom(); } @Override @@ -373,7 +370,7 @@ private boolean functionBind() { protected Pair parseSubscript( Expression base, Position startLoc, boolean noCalls) { if (!noCalls && this.eat(doubleColon)) { - Expression callee = parseSubscripts(parseExprAtom(null), this.start, this.startLoc, true); + Expression callee = parseSubscripts(parseExprAtom(), this.start, this.startLoc, true); BindExpression bind = new BindExpression(new SourceLocation(startLoc), base, callee); return Pair.make(this.finishNode(bind), true); } @@ -438,11 +435,11 @@ private MetaProperty parseImportMeta(Position loc) { * already been consumed. */ private DynamicImport parseDynamicImport(Position startLoc) { - Expression source = parseMaybeAssign(false, null, null); + Expression source = parseMaybeAssign(false, null); Expression attributes = null; if (this.eat(TokenType.comma)) { if (this.type != TokenType.parenR) { // Skip if the comma was a trailing comma - attributes = this.parseMaybeAssign(false, null, null); + attributes = this.parseMaybeAssign(false, null); this.eat(TokenType.comma); // Allow trailing comma } } diff --git a/javascript/extractor/src/com/semmle/jcorn/Parser.java b/javascript/extractor/src/com/semmle/jcorn/Parser.java index cb9af6c6822c..6b6876b07213 100644 --- a/javascript/extractor/src/com/semmle/jcorn/Parser.java +++ b/javascript/extractor/src/com/semmle/jcorn/Parser.java @@ -1239,32 +1239,6 @@ protected void unexpected() { unexpected((Integer) null); } - public static class DestructuringErrors { - private int shorthandAssign, trailingComma; - - public void reset() { - this.shorthandAssign = 0; - this.trailingComma = 0; - } - } - - protected boolean checkPatternErrors( - DestructuringErrors refDestructuringErrors, boolean andThrow) { - int trailing = refDestructuringErrors != null ? refDestructuringErrors.trailingComma : 0; - if (!andThrow) return trailing != 0; - if (trailing != 0) this.raise(trailing, "Comma is not permitted after the rest element"); - return false; - } - - protected boolean checkExpressionErrors( - DestructuringErrors refDestructuringErrors, boolean andThrow) { - int pos = refDestructuringErrors != null ? refDestructuringErrors.shorthandAssign : 0; - if (!andThrow) return pos != 0; - if (pos != 0) - this.raise(pos, "Shorthand property assignments are valid only in destructuring patterns"); - return false; - } - private void checkYieldAwaitInDefaultParams() { if (this.yieldPos > 0 && (this.awaitPos == 0 || this.yieldPos < this.awaitPos)) this.raise(this.yieldPos, "Yield expression cannot be a default value"); @@ -1367,20 +1341,16 @@ private void checkPropClash(Property prop, Map propHash) { // and, *if* the syntactic construct they handle is present, wrap // the AST node that the inner parser gave them in another node. - // Parse a full expression. The optional arguments are used to - // forbid the `in` operator (in for loops initalization expressions) - // and provide reference for storing '=' operator inside shorthand - // property assignment in contexts where both object expression - // and object pattern might appear (so it's possible to raise - // delayed syntax error at correct position). - protected Expression parseExpression(boolean noIn, DestructuringErrors refDestructuringErrors) { + // Parse a full expression. The argument is used to forbid the `in` operator + // (in for loops initialization expressions). + protected Expression parseExpression(boolean noIn) { Position startLoc = this.startLoc; - Expression expr = this.parseMaybeAssign(noIn, refDestructuringErrors, null); + Expression expr = this.parseMaybeAssign(noIn, null); if (this.type == TokenType.comma) { List expressions = CollectionUtil.makeList(expr); SequenceExpression node = new SequenceExpression(new SourceLocation(startLoc), expressions); while (this.eat(TokenType.comma)) - expressions.add(this.parseMaybeAssign(noIn, refDestructuringErrors, null)); + expressions.add(this.parseMaybeAssign(noIn, null)); return this.finishNode(node); } return expr; @@ -1392,46 +1362,32 @@ public interface AfterLeftParse { // Parse an assignment expression. This includes applications of // operators like `+=`. - protected Expression parseMaybeAssign( - boolean noIn, DestructuringErrors refDestructuringErrors, AfterLeftParse afterLeftParse) { + protected Expression parseMaybeAssign(boolean noIn, AfterLeftParse afterLeftParse) { if (this.inGenerator && this.isContextual("yield")) return this.parseYield(); - boolean ownDestructuringErrors = false; - if (refDestructuringErrors == null) { - refDestructuringErrors = new DestructuringErrors(); - ownDestructuringErrors = true; - } int startPos = this.start; Position startLoc = this.startLoc; if (this.type == TokenType.parenL || this.type == TokenType.name) this.potentialArrowAt = this.start; - Expression left = this.parseMaybeConditional(noIn, refDestructuringErrors); + Expression left = this.parseMaybeConditional(noIn); if (afterLeftParse != null) left = afterLeftParse.call(left, startPos, startLoc); if (this.type.isAssign) { - this.checkPatternErrors(refDestructuringErrors, true); - if (!ownDestructuringErrors) refDestructuringErrors.reset(); Expression l = this.type == TokenType.eq ? (Expression) this.toAssignable(left, false) : left; - refDestructuringErrors.shorthandAssign = - 0; // reset because shorthand default was used correctly String operator = String.valueOf(this.value); this.checkLVal(l, false, null); this.next(); - Expression r = this.parseMaybeAssign(noIn, null, null); + Expression r = this.parseMaybeAssign(noIn, null); AssignmentExpression node = new AssignmentExpression(new SourceLocation(startLoc), operator, l, r); return this.finishNode(node); - } else { - if (ownDestructuringErrors) this.checkExpressionErrors(refDestructuringErrors, true); } return left; } // Parse a ternary conditional (`?:`) operator. - protected Expression parseMaybeConditional( - boolean noIn, DestructuringErrors refDestructuringErrors) { + protected Expression parseMaybeConditional(boolean noIn) { Position startLoc = this.startLoc; - Expression expr = this.parseExprOps(noIn, refDestructuringErrors); - if (this.checkExpressionErrors(refDestructuringErrors, false)) return expr; + Expression expr = this.parseExprOps(noIn); if (this.eat(TokenType.question)) { return parseConditionalRest(noIn, startLoc, expr); } @@ -1439,20 +1395,19 @@ protected Expression parseMaybeConditional( } protected Expression parseConditionalRest(boolean noIn, Position start, Expression test) { - Expression consequent = this.parseMaybeAssign(false, null, null); + Expression consequent = this.parseMaybeAssign(false, null); this.expect(TokenType.colon); - Expression alternate = this.parseMaybeAssign(noIn, null, null); + Expression alternate = this.parseMaybeAssign(noIn, null); ConditionalExpression node = new ConditionalExpression(new SourceLocation(start), test, consequent, alternate); return this.finishNode(node); } // Start the precedence parser. - protected Expression parseExprOps(boolean noIn, DestructuringErrors refDestructuringErrors) { + protected Expression parseExprOps(boolean noIn) { int startPos = this.start; Position startLoc = this.startLoc; - Expression expr = this.parseMaybeUnary(refDestructuringErrors, false); - if (this.checkExpressionErrors(refDestructuringErrors, false)) return expr; + Expression expr = this.parseMaybeUnary(false); return this.parseExprOp(expr, startPos, startLoc, -1, noIn); } @@ -1472,7 +1427,7 @@ protected Expression parseExprOp( int startPos = this.start; Position startLoc = this.startLoc; Expression right = - this.parseExprOp(this.parseMaybeUnary(null, false), startPos, startLoc, prec, noIn); + this.parseExprOp(this.parseMaybeUnary(false), startPos, startLoc, prec, noIn); Expression node = this.buildBinary(leftStartPos, leftStartLoc, left, right, op, logical); return this.parseExprOp(node, leftStartPos, leftStartLoc, minPrec, noIn); } @@ -1496,8 +1451,7 @@ protected Expression buildBinary( } // Parse unary operators, both prefix and postfix. - protected Expression parseMaybeUnary( - DestructuringErrors refDestructuringErrors, boolean sawUnary) { + protected Expression parseMaybeUnary(boolean sawUnary) { int startPos = this.start; Position startLoc = this.startLoc; Expression expr; @@ -1508,21 +1462,19 @@ protected Expression parseMaybeUnary( String operator = String.valueOf(this.value); boolean update = this.type == TokenType.incDec; this.next(); - Expression argument = this.parseMaybeUnary(null, true); + Expression argument = this.parseMaybeUnary(true); SourceLocation loc = new SourceLocation(startLoc); Expression node = update ? new UpdateExpression(loc, operator, argument, true) : new UnaryExpression(loc, operator, argument, true); - this.checkExpressionErrors(refDestructuringErrors, true); if (update) this.checkLVal(argument, false, null); else if (this.strict && operator.equals("delete") && argument instanceof Identifier) this.raiseRecoverable(node, "Deleting local variable in strict mode"); else sawUnary = true; expr = this.finishNode(node); } else { - expr = this.parseExprSubscripts(refDestructuringErrors); - if (this.checkExpressionErrors(refDestructuringErrors, false)) return expr; + expr = this.parseExprSubscripts(); while (this.type.isPostfix && !this.canInsertSemicolon()) { UpdateExpression node = new UpdateExpression( @@ -1535,20 +1487,19 @@ else if (this.strict && operator.equals("delete") && argument instanceof Identif if (!sawUnary && this.eat(TokenType.starstar)) return this.buildBinary( - startPos, startLoc, expr, this.parseMaybeUnary(null, false), "**", false); + startPos, startLoc, expr, this.parseMaybeUnary(false), "**", false); else return expr; } // Parse call, dot, and `[]`-subscript expressions. - protected Expression parseExprSubscripts(DestructuringErrors refDestructuringErrors) { + protected Expression parseExprSubscripts() { int startPos = this.start; Position startLoc = this.startLoc; - Expression expr = this.parseExprAtom(refDestructuringErrors); + Expression expr = this.parseExprAtom(); boolean skipArrowSubscripts = expr instanceof ArrowFunctionExpression && !inputSubstring(this.lastTokStart, this.lastTokEnd).equals(")"); - if (this.checkExpressionErrors(refDestructuringErrors, false) || skipArrowSubscripts) - return expr; + if (skipArrowSubscripts) return expr; return this.parseSubscripts(expr, startPos, startLoc, false); } @@ -1578,28 +1529,24 @@ protected Pair parseSubscript( new MemberExpression( new SourceLocation(startLoc), base, - this.parseExpression(false, null), + this.parseExpression(false), true, optional, Chainable.isOnOptionalChain(optional, base)); this.expect(TokenType.bracketR); return Pair.make(this.finishNode(node), true); } else if (!noCalls && this.eat(TokenType.parenL)) { - DestructuringErrors refDestructuringErrors = new DestructuringErrors(); int oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos; this.yieldPos = 0; this.awaitPos = 0; List exprList = - this.parseExprList( - TokenType.parenR, this.options.ecmaVersion() >= 8, false, refDestructuringErrors); + this.parseExprList(TokenType.parenR, this.options.ecmaVersion() >= 8, false); if (maybeAsyncArrow && shouldParseAsyncArrow()) { - this.checkPatternErrors(refDestructuringErrors, true); this.checkYieldAwaitInDefaultParams(); this.yieldPos = oldYieldPos; this.awaitPos = oldAwaitPos; return Pair.make(this.parseArrowExpression(startLoc, exprList, true), false); } - this.checkExpressionErrors(refDestructuringErrors, true); if (oldYieldPos > 0) this.yieldPos = oldYieldPos; if (oldAwaitPos > 0) this.awaitPos = oldAwaitPos; CallExpression node = @@ -1642,7 +1589,7 @@ protected boolean shouldParseAsyncArrow() { // expression, an expression started by a keyword like `function` or // `new`, or an expression wrapped in punctuation like `()`, `[]`, // or `{}`. - protected Expression parseExprAtom(DestructuringErrors refDestructuringErrors) { + protected Expression parseExprAtom() { Expression node; boolean canBeArrow = this.potentialArrowAt == this.start; if (this.type == TokenType._super) { @@ -1700,12 +1647,11 @@ protected Expression parseExprAtom(DestructuringErrors refDestructuringErrors) { } else if (this.type == TokenType.bracketL) { Position startLoc = this.startLoc; this.next(); - List elements = - this.parseExprList(TokenType.bracketR, true, true, refDestructuringErrors); + List elements = this.parseExprList(TokenType.bracketR, true, true); node = new ArrayExpression(new SourceLocation(startLoc), elements); return this.finishNode(node); } else if (this.type == TokenType.braceL) { - return this.parseObj(false, refDestructuringErrors); + return this.parseObj(false); } else if (this.type == TokenType._function) { Position startLoc = this.startLoc; this.next(); @@ -1734,7 +1680,7 @@ protected Literal parseLiteral(TokenType tokenType, Object value) { protected Expression parseParenExpression() { this.expect(TokenType.parenL); - Expression val = this.parseExpression(false, null); + Expression val = this.parseExpression(false); this.expect(TokenType.parenR); return val; } @@ -1745,12 +1691,10 @@ protected Expression parseParenAndDistinguishExpression(boolean canBeArrow) { if (this.options.ecmaVersion() >= 6) { this.next(); - DestructuringErrors refDestructuringErrors = new DestructuringErrors(); int oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos; - ParenthesisedExpressions parenExprs = parseParenthesisedExpressions(refDestructuringErrors); + ParenthesisedExpressions parenExprs = parseParenthesisedExpressions(); if (canBeArrow && !this.canInsertSemicolon() && this.eat(TokenType.arrow)) { - this.checkPatternErrors(refDestructuringErrors, true); this.checkYieldAwaitInDefaultParams(); if (parenExprs.innerParenStart != 0) this.unexpected(parenExprs.innerParenStart); this.yieldPos = oldYieldPos; @@ -1761,7 +1705,6 @@ protected Expression parseParenAndDistinguishExpression(boolean canBeArrow) { if (parenExprs.exprList.isEmpty() || parenExprs.lastIsComma) this.unexpected(this.lastTokStart); if (parenExprs.spreadStart != 0) this.unexpected(parenExprs.spreadStart); - this.checkExpressionErrors(refDestructuringErrors, true); if (oldYieldPos > 0) this.yieldPos = oldYieldPos; if (oldAwaitPos > 0) this.awaitPos = oldAwaitPos; @@ -1803,8 +1746,7 @@ protected static class ParenthesisedExpressions { boolean lastIsComma; } - protected ParenthesisedExpressions parseParenthesisedExpressions( - DestructuringErrors refDestructuringErrors) { + protected ParenthesisedExpressions parseParenthesisedExpressions() { boolean allowTrailingComma = this.options.ecmaVersion() >= 8; ParenthesisedExpressions parenExprs = new ParenthesisedExpressions(); parenExprs.startLoc = this.startLoc; @@ -1814,8 +1756,7 @@ protected ParenthesisedExpressions parseParenthesisedExpressions( while (this.type != TokenType.parenR) { if (first) first = false; else this.expect(TokenType.comma); - if (!parseParenthesisedExpression( - refDestructuringErrors, allowTrailingComma, parenExprs, first)) break; + if (!parseParenthesisedExpression(allowTrailingComma, parenExprs, first)) break; } parenExprs.endLoc = this.startLoc; this.expect(TokenType.parenR); @@ -1829,7 +1770,6 @@ protected ParenthesisedExpressions parseParenthesisedExpressions( * @return true if more expressions may follow this one, false if it must be the last one */ protected boolean parseParenthesisedExpression( - DestructuringErrors refDestructuringErrors, boolean allowTrailingComma, ParenthesisedExpressions parenExprs, boolean first) { @@ -1838,16 +1778,15 @@ protected boolean parseParenthesisedExpression( return false; } else if (this.type == TokenType.ellipsis) { parenExprs.spreadStart = this.start; - parenExprs.exprList.add(this.parseParenItem(this.parseRest(false), -1, null)); - if (this.type == TokenType.comma) - this.raise(this.startLoc, "Comma is not permitted after the rest element"); + parenExprs.exprList.add(this.parseParenItem(this.parseRest(), -1, null)); + this.eat(TokenType.comma); return false; } else { if (this.type == TokenType.parenL && parenExprs.innerParenStart == 0) { parenExprs.innerParenStart = this.start; } parenExprs.exprList.add( - this.parseMaybeAssign(false, refDestructuringErrors, this::parseParenItem)); + this.parseMaybeAssign(false, this::parseParenItem)); } return true; } @@ -1879,7 +1818,7 @@ protected Expression parseNew() { int innerStartPos = this.start; Position innerStartLoc = this.startLoc; Expression callee = - this.parseSubscripts(this.parseExprAtom(null), innerStartPos, innerStartLoc, true); + this.parseSubscripts(this.parseExprAtom(), innerStartPos, innerStartLoc, true); if (Chainable.isOnOptionalChain(false, callee)) this.raise(callee, "An optional chain may not be used in a `new` expression."); @@ -1891,7 +1830,7 @@ protected Expression parseNewArguments(Position startLoc, Expression callee) { List arguments; if (this.eat(TokenType.parenL)) arguments = - this.parseExprList(TokenType.parenR, this.options.ecmaVersion() >= 8, false, null); + this.parseExprList(TokenType.parenR, this.options.ecmaVersion() >= 8, false); else arguments = new ArrayList(); NewExpression node = new NewExpression(new SourceLocation(startLoc), callee, new ArrayList<>(), arguments); @@ -1926,7 +1865,7 @@ protected TemplateLiteral parseTemplate(boolean isTagged) { List quasis = CollectionUtil.makeList(curElt); while (!curElt.isTail()) { this.expect(TokenType.dollarBraceL); - expressions.add(this.parseExpression(false, null)); + expressions.add(this.parseExpression(false)); this.expect(TokenType.braceR); quasis.add(curElt = this.parseTemplateElement(isTagged)); } @@ -1958,7 +1897,7 @@ MethodDefinition.Kind getMethodKind() { } // Parse an object literal or binding pattern. - protected Expression parseObj(boolean isPattern, DestructuringErrors refDestructuringErrors) { + protected Expression parseObj(boolean isPattern) { Position startLoc = this.startLoc; if (!isPattern && options.allowGeneratedCodeExprs() && charAt(pos) == '{') { // Parse mustache-style placeholder expression: {{ ... }} or {{{ ... }}} @@ -1978,7 +1917,7 @@ protected Expression parseObj(boolean isPattern, DestructuringErrors refDestruct first = false; } - properties.add(this.finishNode(parseProperty(isPattern, refDestructuringErrors, propHash))); + properties.add(this.finishNode(parseProperty(isPattern, propHash))); } SourceLocation loc = new SourceLocation(startLoc); Expression node = @@ -2022,10 +1961,7 @@ protected Expression parseGeneratedCodeExpr(Position startLoc, String openingDel bodyToken.getValue())); } - protected Property parseProperty( - boolean isPattern, - DestructuringErrors refDestructuringErrors, - Map propHash) { + protected Property parseProperty(boolean isPattern, Map propHash) { Position propStartLoc = this.startLoc; boolean isGenerator = false; if (this.options.ecmaVersion() >= 6) { @@ -2040,7 +1976,7 @@ protected Property parseProperty( } else { pi.isAsync = false; } - this.parsePropertyValue(pi, refDestructuringErrors); + this.parsePropertyValue(pi); Property prop = new Property( new SourceLocation(pi.startLoc), pi.key, pi.value, pi.kind, pi.computed, pi.method); @@ -2061,14 +1997,14 @@ private boolean isAsyncProp(PropertyInfo pi) { && !this.canInsertSemicolon(); } - protected void parsePropertyValue(PropertyInfo pi, DestructuringErrors refDestructuringErrors) { + protected void parsePropertyValue(PropertyInfo pi) { if ((pi.isGenerator || pi.isAsync) && this.type == TokenType.colon) this.unexpected(); if (this.eat(TokenType.colon)) { pi.value = pi.isPattern ? this.parseMaybeDefault(this.startLoc, null) - : this.parseMaybeAssign(false, refDestructuringErrors, null); + : this.parseMaybeAssign(false, null); pi.kind = "init"; } else if (this.options.ecmaVersion() >= 6 && this.type == TokenType.parenL) { if (pi.isPattern) this.unexpected(); @@ -2108,9 +2044,7 @@ protected void parsePropertyValue(PropertyInfo pi, DestructuringErrors refDestru pi.kind = "init"; if (pi.isPattern) { pi.value = this.parseMaybeDefault(pi.startLoc, pi.key); - } else if (this.type == TokenType.eq && refDestructuringErrors != null) { - if (refDestructuringErrors.shorthandAssign == 0) - refDestructuringErrors.shorthandAssign = this.start; + } else if (this.type == TokenType.eq) { pi.value = this.parseMaybeDefault(pi.startLoc, pi.key); } else { pi.value = pi.key; @@ -2123,14 +2057,14 @@ protected void parsePropertyValue(PropertyInfo pi, DestructuringErrors refDestru protected void parsePropertyName(PropertyInfo result) { if (this.options.ecmaVersion() >= 6) { if (this.eat(TokenType.bracketL)) { - result.key = this.parseMaybeAssign(false, null, null); + result.key = this.parseMaybeAssign(false, null); result.computed = true; this.expect(TokenType.bracketR); return; } } if (this.type == TokenType.num || this.type == TokenType.string) - result.key = this.parseExprAtom(null); + result.key = this.parseExprAtom(); else result.key = this.parseIdent(true); } @@ -2147,7 +2081,7 @@ protected FunctionExpression parseMethod(boolean isGenerator, boolean isAsync) { this.expect(TokenType.parenL); List params = - this.parseBindingList(TokenType.parenR, false, this.options.ecmaVersion() >= 8, false); + this.parseBindingList(TokenType.parenR, false, this.options.ecmaVersion() >= 8); this.checkYieldAwaitInDefaultParams(); boolean generator = this.options.ecmaVersion() >= 6 && isGenerator; Node body = this.parseFunctionBody(null, params, false); @@ -2191,7 +2125,7 @@ protected Node parseFunctionBody( boolean isExpression = isArrowFunction && this.type != TokenType.braceL; Node body; if (isExpression) { - body = this.parseMaybeAssign(false, null, null); + body = this.parseMaybeAssign(false, null); } else { // Start a new scope with regard to labels and the `inFunction` // flag (restore them to their old value afterwards). @@ -2247,10 +2181,7 @@ protected void checkParams(List params) { // nothing in between them to be parsed as `null` (which is needed // for array literals). protected List parseExprList( - TokenType close, - boolean allowTrailingComma, - boolean allowEmpty, - DestructuringErrors refDestructuringErrors) { + TokenType close, boolean allowTrailingComma, boolean allowEmpty) { List elts = new ArrayList(); boolean first = true; while (!this.eat(close)) { @@ -2265,14 +2196,9 @@ protected List parseExprList( if (allowEmpty && this.type == TokenType.comma) { elt = null; } else if (this.type == TokenType.ellipsis) { - elt = this.processExprListItem(this.parseSpread(refDestructuringErrors)); - if (this.type == TokenType.comma - && refDestructuringErrors != null - && refDestructuringErrors.trailingComma == 0) { - refDestructuringErrors.trailingComma = this.start; - } + elt = this.processExprListItem(this.parseSpread()); } else - elt = this.processExprListItem(this.parseMaybeAssign(false, refDestructuringErrors, null)); + elt = this.processExprListItem(this.parseMaybeAssign(false, null)); elts.add(elt); } return elts; @@ -2335,7 +2261,7 @@ protected YieldExpression parseYield() { argument = null; } else { delegate = this.eat(TokenType.star); - argument = this.parseMaybeAssign(false, null, null); + argument = this.parseMaybeAssign(false, null); } YieldExpression node = new YieldExpression(new SourceLocation(startLoc), argument, delegate); return this.finishNode(node); @@ -2344,7 +2270,7 @@ protected YieldExpression parseYield() { protected AwaitExpression parseAwait() { Position startLoc = this.startLoc; this.next(); - Expression argument = this.parseMaybeUnary(null, true); + Expression argument = this.parseMaybeUnary(true); AwaitExpression node = new AwaitExpression(new SourceLocation(startLoc), argument); return this.finishNode(node); } @@ -2433,17 +2359,11 @@ protected List toAssignableList(List exprList, boolean i } else if (last != null && last instanceof SpreadElement) { Expression arg = ((SpreadElement) last).getArgument(); arg = (Expression) this.toAssignable(arg, isBinding); - if (!(arg instanceof Identifier - || arg instanceof MemberExpression - || arg instanceof ArrayPattern)) this.unexpected(arg.getLoc().getStart()); + if (!(arg instanceof IPattern || arg instanceof MemberExpression)) + this.unexpected(arg.getLoc().getStart()); exprList.set(end - 1, last = new RestElement(last.getLoc(), arg)); --end; } - - if (isBinding - && last instanceof RestElement - && !(((RestElement) last).getArgument() instanceof Identifier)) - this.unexpected(((RestElement) last).getArgument().getLoc().getStart()); } for (int i = 0; i < end; ++i) exprList.set(i, (Expression) this.toAssignable(exprList.get(i), isBinding)); @@ -2451,27 +2371,18 @@ protected List toAssignableList(List exprList, boolean i } // Parses spread element. - protected SpreadElement parseSpread(DestructuringErrors refDestructuringErrors) { + protected SpreadElement parseSpread() { Position start = this.startLoc; this.next(); SpreadElement node = - new SpreadElement( - new SourceLocation(start), this.parseMaybeAssign(false, refDestructuringErrors, null)); + new SpreadElement(new SourceLocation(start), this.parseMaybeAssign(false, null)); return this.finishNode(node); } - protected RestElement parseRest(boolean allowNonIdent) { + protected RestElement parseRest() { Position start = this.startLoc; this.next(); - - // RestElement inside of a function parameter must be an identifier - Expression argument = null; - if (allowNonIdent) - if (this.type == TokenType.name) argument = this.parseIdent(false); - else this.unexpected(); - else if (this.type == TokenType.name || this.type == TokenType.bracketL) - argument = this.parseBindingAtom(); - else this.unexpected(); + Expression argument = this.parseBindingAtom(); RestElement node = new RestElement(new SourceLocation(start), argument); return this.finishNode(node); } @@ -2483,18 +2394,18 @@ protected Expression parseBindingAtom() { if (this.type == TokenType.bracketL) { Position start = this.startLoc; this.next(); - List elements = this.parseBindingList(TokenType.bracketR, true, true, false); + List elements = this.parseBindingList(TokenType.bracketR, true, true); ArrayPattern node = new ArrayPattern(new SourceLocation(start), elements); return this.finishNode(node); } - if (this.type == TokenType.braceL) return this.parseObj(true, null); + if (this.type == TokenType.braceL) return this.parseObj(true); return this.parseIdent(false); } protected List parseBindingList( - TokenType close, boolean allowEmpty, boolean allowTrailingComma, boolean allowNonIdent) { + TokenType close, boolean allowEmpty, boolean allowTrailingComma) { List result = new ArrayList(); boolean first = true; while (!this.eat(close)) { @@ -2505,9 +2416,8 @@ protected List parseBindingList( } else if (allowTrailingComma && this.afterTrailingComma(close, false)) { break; } else if (this.type == TokenType.ellipsis) { - result.add(this.processBindingListItem(this.parseRest(allowNonIdent))); - if (this.type == TokenType.comma) - this.raise(this.start, "Comma is not permitted after the rest element"); + result.add(this.processBindingListItem(this.parseRest())); + this.eat(TokenType.comma); this.expect(close); break; } else { @@ -2527,7 +2437,7 @@ protected Expression parseMaybeDefault(Position startLoc, Expression left) { if (this.options.ecmaVersion() < 6 || !this.eat(TokenType.eq)) return left; AssignmentPattern node = new AssignmentPattern( - new SourceLocation(startLoc), "=", left, this.parseMaybeAssign(false, null, null)); + new SourceLocation(startLoc), "=", left, this.parseMaybeAssign(false, null)); return this.finishNode(node); } @@ -2826,7 +2736,7 @@ protected Statement parseStatement(boolean declaration, boolean topLevel, Set= 6 && this.isContextual("of"))) { - this.checkPatternErrors(refDestructuringErrors, true); init = (Expression) this.toAssignable(init, false); this.checkLVal(init, false, null); return this.parseForIn(startLoc, init); - } else { - this.checkExpressionErrors(refDestructuringErrors, true); } return this.parseFor(startLoc, init); } @@ -2972,7 +2878,7 @@ protected ReturnStatement parseReturnStatement(Position startLoc) { if (this.eagerlyTrySemicolon()) { argument = null; } else { - argument = this.parseExpression(false, null); + argument = this.parseExpression(false); this.semicolon(); } return this.finishNode(new ReturnStatement(new SourceLocation(startLoc), argument)); @@ -3005,7 +2911,7 @@ protected SwitchStatement parseSwitchStatement(Position startLoc) { curConsequent = new ArrayList(); this.next(); if (isCase) { - curTest = this.parseExpression(false, null); + curTest = this.parseExpression(false); } else { if (sawDefault) this.raiseRecoverable(this.lastTokStart, "Multiple default clauses"); sawDefault = true; @@ -3031,7 +2937,7 @@ protected ThrowStatement parseThrowStatement(Position startLoc) { this.next(); if (inputSubstring(this.lastTokEnd, this.start).matches("(?s).*(?:" + lineBreak + ").*")) this.raise(this.lastTokEnd, "Illegal newline after throw"); - Expression argument = this.parseExpression(false, null); + Expression argument = this.parseExpression(false); this.semicolon(); return this.finishNode(new ThrowStatement(new SourceLocation(startLoc), argument)); } @@ -3144,9 +3050,9 @@ protected BlockStatement parseBlock(boolean allowStrict) { // expression. protected ForStatement parseFor(Position startLoc, Node init) { this.expect(TokenType.semi); - Expression test = this.type == TokenType.semi ? null : this.parseExpression(false, null); + Expression test = this.type == TokenType.semi ? null : this.parseExpression(false); this.expect(TokenType.semi); - Expression update = this.type == TokenType.parenR ? null : this.parseExpression(false, null); + Expression update = this.type == TokenType.parenR ? null : this.parseExpression(false); this.expect(TokenType.parenR); Statement body = this.parseStatement(false, false); this.labels.pop(); @@ -3160,7 +3066,7 @@ protected Statement parseForIn(Position startLoc, Node left) { SourceLocation loc = new SourceLocation(startLoc); boolean isForIn = this.type == TokenType._in; this.next(); - Expression right = this.parseExpression(false, null); + Expression right = this.parseExpression(false); this.expect(TokenType.parenR); Statement body = this.parseStatement(false, false); this.labels.pop(); @@ -3178,7 +3084,7 @@ protected VariableDeclaration parseVar(Position startLoc, boolean isFor, String Expression id = this.parseVarId(); Expression init = null; if (this.eat(TokenType.eq)) { - init = this.parseMaybeAssign(isFor, null, null); + init = this.parseMaybeAssign(isFor, null); } else if ((kind.equals("const") || kind.equals("using")) && !(this.type == TokenType._in || (this.options.ecmaVersion() >= 6 && this.isContextual("of")))) { @@ -3280,7 +3186,7 @@ protected IFunction parseFunctionRest( protected List parseFunctionParams() { this.expect(TokenType.parenL); List params = - this.parseBindingList(TokenType.parenR, false, this.options.ecmaVersion() >= 8, true); + this.parseBindingList(TokenType.parenR, false, this.options.ecmaVersion() >= 8); this.checkYieldAwaitInDefaultParams(); return params; } @@ -3415,7 +3321,7 @@ protected Identifier parseClassId(boolean isStatement) { } protected Expression parseClassSuper() { - return this.eat(TokenType._extends) ? this.parseExprSubscripts(null) : null; + return this.eat(TokenType._extends) ? this.parseExprSubscripts() : null; } // Parses module export declaration. @@ -3462,7 +3368,7 @@ protected ExportDeclaration parseExportRest(SourceLocation loc, Set expo new ClassDeclaration(ce.getLoc(), ce.getClassDef(), noDeclareKeyword, notAbstract); else declaration = ce; } else { - declaration = this.parseMaybeAssign(false, null, null); + declaration = this.parseMaybeAssign(false, null); this.semicolon(); } return this.finishNode(new ExportDefaultDeclaration(loc, declaration)); @@ -3497,7 +3403,7 @@ protected ExportDeclaration parseExportRest(SourceLocation loc, Set expo protected Expression parseExportFrom( List specifiers, Expression source, boolean expectFrom) { if (this.eatContextual("from")) { - if (this.type == TokenType.string) source = this.parseExprAtom(null); + if (this.type == TokenType.string) source = this.parseExprAtom(); else this.unexpected(); } else { if (expectFrom) this.unexpected(); @@ -3576,7 +3482,7 @@ protected List parseExportSpecifiers(Set exports) { } else { if (this.type == TokenType.string) { // e.g. `export { Foo_new as "Foo::new" }` - Expression string = this.parseExprAtom(null); + Expression string = this.parseExprAtom(); String str = ((Literal)string).getStringValue(); exported = this.finishNode(new Identifier(loc, str)); } else { @@ -3609,7 +3515,7 @@ protected ImportDeclaration parseLegacyModuleImport(Position startLoc) { this.checkLVal(local, true, null); this.expectContextual("from"); if (this.type != TokenType.string) this.unexpected(); - Literal source = (Literal) this.parseExprAtom(null); + Literal source = (Literal) this.parseExprAtom(); this.semicolon(); List specifiers = new ArrayList(); @@ -3625,7 +3531,7 @@ protected Expression parseImportOrExportAttributesAndSemicolon() { if (!this.eatContextual("assert")) { this.expect(TokenType._with); } - result = this.parseObj(false, null); + result = this.parseObj(false); this.semicolon(); } return result; @@ -3638,12 +3544,12 @@ protected ImportDeclaration parseImportRest(SourceLocation loc) { // import '...' if (this.type == TokenType.string) { specifiers = new ArrayList(); - source = (Literal) this.parseExprAtom(null); + source = (Literal) this.parseExprAtom(); } else { specifiers = this.parseImportSpecifiers(phaseModifier); this.expectContextual("from"); if (this.type != TokenType.string) this.unexpected(); - source = (Literal) this.parseExprAtom(null); + source = (Literal) this.parseExprAtom(); } Expression attributes = this.parseImportOrExportAttributesAndSemicolon(); if (specifiers == null) return null; @@ -3698,7 +3604,7 @@ protected ImportSpecifier parseImportSpecifier() { if (this.type == TokenType.string) { // Arbitrary Module Namespace Identifiers // e.g. `import { "Foo::new" as Foo_new } from "./foo.wasm"` - Expression string = this.parseExprAtom(null); + Expression string = this.parseExprAtom(); String str = ((Literal)string).getStringValue(); imported = this.finishNode(new Identifier(loc, str)); // only makes sense if there is a local identifier diff --git a/javascript/extractor/src/com/semmle/jcorn/flow/FlowParser.java b/javascript/extractor/src/com/semmle/jcorn/flow/FlowParser.java index 7475994c27ed..ec7d4c74e4ad 100644 --- a/javascript/extractor/src/com/semmle/jcorn/flow/FlowParser.java +++ b/javascript/extractor/src/com/semmle/jcorn/flow/FlowParser.java @@ -237,7 +237,7 @@ private void flowParseDeclareModule(Position start) { this.next(); if (this.type == TokenType.string) { - this.parseExprAtom(null); + this.parseExprAtom(); } else { this.parseIdent(false); } @@ -458,7 +458,7 @@ private void flowParseTypeParameterInstantiation() { private void flowParseObjectPropertyKey() { if (this.type == TokenType.num || this.type == TokenType.string) { - this.parseExprAtom(null); + this.parseExprAtom(); } else if ("@@iterator".equals(inputSubstring(start, start + 10))) { // allow `@@iterator` as property name; this doesn't appear to be standard Flow syntax, // but is used a few times in react-native @@ -1158,9 +1158,8 @@ protected Expression parseConditionalRest(boolean noIn, Position start, Expressi } @Override - protected ParenthesisedExpressions parseParenthesisedExpressions( - DestructuringErrors refDestructuringErrors) { - ParenthesisedExpressions pe = super.parseParenthesisedExpressions(refDestructuringErrors); + protected ParenthesisedExpressions parseParenthesisedExpressions() { + ParenthesisedExpressions pe = super.parseParenthesisedExpressions(); // handle return types for arrow functions if (flow() && this.type == TokenType.colon) { @@ -1218,14 +1217,14 @@ protected FieldDefinition parseFieldDefinition(PropertyInfo pi, boolean isStatic // parse type parameters for object method shorthand @Override - protected void parsePropertyValue(PropertyInfo pi, DestructuringErrors refDestructuringErrors) { + protected void parsePropertyValue(PropertyInfo pi) { // method shorthand if (flow() && this.isRelational("<")) { this.flowParseTypeParameterDeclaration(); if (this.type != TokenType.parenL) this.unexpected(); } - super.parsePropertyValue(pi, refDestructuringErrors); + super.parsePropertyValue(pi); } @Override diff --git a/javascript/extractor/src/com/semmle/jcorn/jsx/JSXParser.java b/javascript/extractor/src/com/semmle/jcorn/jsx/JSXParser.java index 6deec72809c3..cabdb9c1e4fd 100644 --- a/javascript/extractor/src/com/semmle/jcorn/jsx/JSXParser.java +++ b/javascript/extractor/src/com/semmle/jcorn/jsx/JSXParser.java @@ -276,7 +276,7 @@ private INode jsx_parseAttributeValue() { this.raise(node, "JSX attributes must only be assigned a non-empty expression"); return node; } else if (type == jsxTagStart || type == string) { - return this.parseExprAtom(null); + return this.parseExprAtom(); } else { this.raise(this.start, "JSX value should be either an expression or a quoted JSX text"); return null; @@ -298,7 +298,7 @@ private JSXExpressionContainer jsx_parseExpressionContainer() { this.next(); INode expression; if (this.type == braceR) expression = this.jsx_parseEmptyExpression(); - else expression = this.parseExpression(false, null); + else expression = this.parseExpression(false); this.expect(braceR); return this.finishNode(new JSXExpressionContainer(loc, expression)); } @@ -308,7 +308,7 @@ private IJSXAttribute jsx_parseAttribute() { SourceLocation loc = new SourceLocation(this.startLoc); if (this.eat(braceL)) { this.expect(ellipsis); - Expression argument = this.parseMaybeAssign(false, null, null); + Expression argument = this.parseMaybeAssign(false, null); this.expect(braceR); return this.finishNode(new JSXSpreadAttribute(loc, argument)); } @@ -358,7 +358,7 @@ private JSXElement jsx_parseElementAt(Position startLoc) { } children.add(this.jsx_parseElementAt(startLoc)); } else if (type == jsxText) { - children.add(this.parseExprAtom(null)); + children.add(this.parseExprAtom()); } else if (type == braceL) { children.add(this.jsx_parseExpressionContainer()); } else { @@ -389,13 +389,13 @@ private JSXElement jsx_parseElement() { } @Override - protected Expression parseExprAtom(DestructuringErrors refDestructuringErrors) { + protected Expression parseExprAtom() { if (this.type == jsxText) { return this.parseLiteral(this.type, this.value); } else if (this.type == jsxTagStart) { return this.jsx_parseElement(); } else { - return super.parseExprAtom(refDestructuringErrors); + return super.parseExprAtom(); } } diff --git a/javascript/extractor/test/com/semmle/js/extractor/test/RobustnessTests.java b/javascript/extractor/test/com/semmle/js/extractor/test/RobustnessTests.java index 1bc64fc8ffa6..d5f8c0d1d812 100644 --- a/javascript/extractor/test/com/semmle/js/extractor/test/RobustnessTests.java +++ b/javascript/extractor/test/com/semmle/js/extractor/test/RobustnessTests.java @@ -1,11 +1,14 @@ package com.semmle.js.extractor.test; +import com.semmle.jcorn.CustomParser; import com.semmle.jcorn.Options; import com.semmle.jcorn.Parser; +import com.semmle.jcorn.SyntaxError; import com.semmle.util.io.WholeIO; import com.semmle.util.tests.TestPaths; import java.io.File; import java.nio.charset.StandardCharsets; +import org.junit.Assert; import org.junit.Test; public class RobustnessTests { @@ -16,4 +19,31 @@ public void letLookheadTest() { String src = new WholeIO(StandardCharsets.UTF_8.name()).strictread(test); new Parser(new Options(), src, 0).parse(); } + + @Test + public void permissiveDestructuringTest() { + String src = + """ + ({ shorthand = 1 }); + ([...rest,] = values); + function f(...args,) {} + function g(...[first, { nested }],) {} + const arrow = (...args,) => args; + const destructuringArrow = (...{ value },) => value; + async (...[first, { nested }],) => first; + ({ method(...{ value },) {} }); + """; + new Parser(new Options(), src, 0).parse(); + } + + @Test + public void parenthesizedRestParameterTest() { + try { + new CustomParser(new Options().preserveParens(true), "async(...(x)) => x", 0) + .parse(); + Assert.fail("Expected syntax error, but parsing succeeded."); + } catch (SyntaxError expected) { + Assert.assertEquals("Unexpected token (1:9)", expected.getMessage()); + } + } } diff --git a/javascript/extractor/tests/es2015/output/trap/restparms2.js.trap b/javascript/extractor/tests/es2015/output/trap/restparms2.js.trap index e6faa776dd50..924b02a736d4 100644 --- a/javascript/extractor/tests/es2015/output/trap/restparms2.js.trap +++ b/javascript/extractor/tests/es2015/output/trap/restparms2.js.trap @@ -9,27 +9,178 @@ hasLocation(#10000,#10002) #20000=@"global_scope" scopes(#20000,0) #20001=@"script;{#10000},1,1" -toplevels(#20001,0) -#20002=@"loc,{#10000},1,1,1,1" -locations_default(#20002,#10000,1,1,1,1) -hasLocation(#20001,#20002) -#20003=* -js_parse_errors(#20003,#20001,"Error: Unexpected token","function f(x, ...[y, z]) { -") -#20004=@"loc,{#10000},1,18,1,18" -locations_default(#20004,#10000,1,18,1,18) -hasLocation(#20003,#20004) -#20005=* -lines(#20005,#20001,"function f(x, ...[y, z]) {"," +#20002=* +lines(#20002,#20001,"function f(x, ...[y, z]) {"," ") -#20006=@"loc,{#10000},1,1,1,26" -locations_default(#20006,#10000,1,1,1,26) -hasLocation(#20005,#20006) -#20007=* -lines(#20007,#20001,"}","") -#20008=@"loc,{#10000},2,1,2,1" -locations_default(#20008,#10000,2,1,2,1) -hasLocation(#20007,#20008) -numlines(#20001,2,0,0) -numlines(#10000,2,0,0) +#20003=@"loc,{#10000},1,1,1,26" +locations_default(#20003,#10000,1,1,1,26) +hasLocation(#20002,#20003) +#20004=* +lines(#20004,#20001,"}","") +#20005=@"loc,{#10000},2,1,2,1" +locations_default(#20005,#10000,2,1,2,1) +hasLocation(#20004,#20005) +numlines(#20001,2,2,0) +#20006=* +tokeninfo(#20006,7,#20001,0,"function") +#20007=@"loc,{#10000},1,1,1,8" +locations_default(#20007,#10000,1,1,1,8) +hasLocation(#20006,#20007) +#20008=* +tokeninfo(#20008,6,#20001,1,"f") +#20009=@"loc,{#10000},1,10,1,10" +locations_default(#20009,#10000,1,10,1,10) +hasLocation(#20008,#20009) +#20010=* +tokeninfo(#20010,8,#20001,2,"(") +#20011=@"loc,{#10000},1,11,1,11" +locations_default(#20011,#10000,1,11,1,11) +hasLocation(#20010,#20011) +#20012=* +tokeninfo(#20012,6,#20001,3,"x") +#20013=@"loc,{#10000},1,12,1,12" +locations_default(#20013,#10000,1,12,1,12) +hasLocation(#20012,#20013) +#20014=* +tokeninfo(#20014,8,#20001,4,",") +#20015=@"loc,{#10000},1,13,1,13" +locations_default(#20015,#10000,1,13,1,13) +hasLocation(#20014,#20015) +#20016=* +tokeninfo(#20016,8,#20001,5,"...") +#20017=@"loc,{#10000},1,15,1,17" +locations_default(#20017,#10000,1,15,1,17) +hasLocation(#20016,#20017) +#20018=* +tokeninfo(#20018,8,#20001,6,"[") +#20019=@"loc,{#10000},1,18,1,18" +locations_default(#20019,#10000,1,18,1,18) +hasLocation(#20018,#20019) +#20020=* +tokeninfo(#20020,6,#20001,7,"y") +#20021=@"loc,{#10000},1,19,1,19" +locations_default(#20021,#10000,1,19,1,19) +hasLocation(#20020,#20021) +#20022=* +tokeninfo(#20022,8,#20001,8,",") +#20023=@"loc,{#10000},1,20,1,20" +locations_default(#20023,#10000,1,20,1,20) +hasLocation(#20022,#20023) +#20024=* +tokeninfo(#20024,6,#20001,9,"z") +#20025=@"loc,{#10000},1,22,1,22" +locations_default(#20025,#10000,1,22,1,22) +hasLocation(#20024,#20025) +#20026=* +tokeninfo(#20026,8,#20001,10,"]") +#20027=@"loc,{#10000},1,23,1,23" +locations_default(#20027,#10000,1,23,1,23) +hasLocation(#20026,#20027) +#20028=* +tokeninfo(#20028,8,#20001,11,")") +#20029=@"loc,{#10000},1,24,1,24" +locations_default(#20029,#10000,1,24,1,24) +hasLocation(#20028,#20029) +#20030=* +tokeninfo(#20030,8,#20001,12,"{") +#20031=@"loc,{#10000},1,26,1,26" +locations_default(#20031,#10000,1,26,1,26) +hasLocation(#20030,#20031) +#20032=* +tokeninfo(#20032,8,#20001,13,"}") +hasLocation(#20032,#20005) +#20033=* +tokeninfo(#20033,0,#20001,14,"") +#20034=@"loc,{#10000},2,2,2,1" +locations_default(#20034,#10000,2,2,2,1) +hasLocation(#20033,#20034) +toplevels(#20001,0) +#20035=@"loc,{#10000},1,1,2,1" +locations_default(#20035,#10000,1,1,2,1) +hasLocation(#20001,#20035) +#20036=@"var;{f};{#20000}" +variables(#20036,"f",#20000) +#20037=@"var;{this};{#20000}" +variables(#20037,"this",#20000) +#20038=* +stmts(#20038,17,#20001,0,"functio ... z]) {\n}") +hasLocation(#20038,#20035) +stmt_containers(#20038,#20001) +#20039=* +exprs(#20039,78,#20038,-1,"f") +hasLocation(#20039,#20009) +expr_containers(#20039,#20038) +literals("f","f",#20039) +decl(#20039,#20036) +#20040=* +scopes(#20040,1) +scopenodes(#20038,#20040) +scopenesting(#20040,#20000) +#20041=@"var;{this};{#20040}" +variables(#20041,"this",#20040) +#20042=@"var;{x};{#20040}" +variables(#20042,"x",#20040) +#20043=* +exprs(#20043,78,#20038,0,"x") +hasLocation(#20043,#20013) +expr_containers(#20043,#20038) +literals("x","x",#20043) +decl(#20043,#20042) +#20044=@"var;{y};{#20040}" +variables(#20044,"y",#20040) +#20045=@"var;{z};{#20040}" +variables(#20045,"z",#20040) +#20046=* +exprs(#20046,67,#20038,1,"[y, z]") +#20047=@"loc,{#10000},1,18,1,23" +locations_default(#20047,#10000,1,18,1,23) +hasLocation(#20046,#20047) +expr_containers(#20046,#20038) +#20048=* +exprs(#20048,78,#20046,0,"y") +hasLocation(#20048,#20021) +expr_containers(#20048,#20038) +literals("y","y",#20048) +decl(#20048,#20044) +#20049=* +exprs(#20049,78,#20046,1,"z") +hasLocation(#20049,#20025) +expr_containers(#20049,#20038) +literals("z","z",#20049) +decl(#20049,#20045) +array_size(#20046,2) +#20050=@"var;{arguments};{#20040}" +variables(#20050,"arguments",#20040) +is_arguments_object(#20050) +has_rest_parameter(#20038) +#20051=* +stmts(#20051,1,#20038,-2,"{\n}") +#20052=@"loc,{#10000},1,26,2,1" +locations_default(#20052,#10000,1,26,2,1) +hasLocation(#20051,#20052) +stmt_containers(#20051,#20038) +#20053=* +entry_cfg_node(#20053,#20001) +#20054=@"loc,{#10000},1,1,1,0" +locations_default(#20054,#10000,1,1,1,0) +hasLocation(#20053,#20054) +#20055=* +exit_cfg_node(#20055,#20001) +hasLocation(#20055,#20034) +successor(#20038,#20055) +#20056=* +entry_cfg_node(#20056,#20038) +hasLocation(#20056,#20054) +#20057=* +exit_cfg_node(#20057,#20038) +hasLocation(#20057,#20034) +successor(#20051,#20057) +successor(#20046,#20048) +successor(#20049,#20051) +successor(#20048,#20049) +successor(#20043,#20046) +successor(#20056,#20043) +successor(#20039,#20038) +successor(#20053,#20039) +numlines(#10000,2,2,0) filetype(#10000,"javascript") diff --git a/javascript/ql/test/library-tests/TripleDot/tst.js b/javascript/ql/test/library-tests/TripleDot/tst.js index 6f776264e84f..f7aedbdcf339 100644 --- a/javascript/ql/test/library-tests/TripleDot/tst.js +++ b/javascript/ql/test/library-tests/TripleDot/tst.js @@ -172,3 +172,19 @@ function t16() { sink(array[2]); // $ hasValueFlow=t16.1 sink(array); // $ hasTaintFlow=t16.1 } + +function t17() { + function target(...[x, y]) { + sink(x); // $ hasValueFlow=t17.1 + sink(y); // $ hasValueFlow=t17.2 + } + target(source("t17.1"), source("t17.2"), source("t17.3")); +} + +function t18() { + function target(...{0: x, 1: y}) { + sink(x); // $ hasValueFlow=t18.1 + sink(y); // $ hasValueFlow=t18.2 + } + target(source("t18.1"), source("t18.2"), source("t18.3")); +}