//===--- ParseStmt.cpp - Statement and Block Parser -----------------------===// // // The LLVM Compiler Infrastructure // // This file was developed by Chris Lattner and is distributed under // the University of Illinois Open Source License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file implements the Statement and Block portions of the Parser // interface. // //===----------------------------------------------------------------------===// #include "clang/Parse/Parser.h" #include "clang/Basic/Diagnostic.h" #include "clang/Parse/DeclSpec.h" #include "clang/Parse/Scope.h" using namespace llvm; using namespace clang; //===----------------------------------------------------------------------===// // C99 6.8: Statements and Blocks. //===----------------------------------------------------------------------===// /// ParseStatementOrDeclaration - Read 'statement' or 'declaration'. /// StatementOrDeclaration: /// statement /// declaration /// /// statement: /// labeled-statement /// compound-statement /// expression-statement /// selection-statement /// iteration-statement /// jump-statement /// [OBC] objc-throw-statement [TODO] /// [OBC] objc-try-catch-statement [TODO] /// [OBC] objc-synchronized-statement [TODO] /// [GNU] asm-statement /// [OMP] openmp-construct [TODO] /// /// labeled-statement: /// identifier ':' statement /// 'case' constant-expression ':' statement /// 'default' ':' statement /// /// selection-statement: /// if-statement /// switch-statement /// /// iteration-statement: /// while-statement /// do-statement /// for-statement /// /// expression-statement: /// expression[opt] ';' /// /// jump-statement: /// 'goto' identifier ';' /// 'continue' ';' /// 'break' ';' /// 'return' expression[opt] ';' /// [GNU] 'goto' '*' expression ';' /// /// [OBC] objc-throw-statement: [TODO] /// [OBC] '@' 'throw' expression ';' [TODO] /// [OBC] '@' 'throw' ';' [TODO] /// Parser::StmtResult Parser::ParseStatementOrDeclaration(bool OnlyStatement) { const char *SemiError = 0; Parser::StmtResult Res; // Cases in this switch statement should fall through if the parser expects // the token to end in a semicolon (in which case SemiError should be set), // or they directly 'return;' if not. switch (Tok.getKind()) { case tok::identifier: // C99 6.8.1: labeled-statement // identifier ':' statement // declaration (if !OnlyStatement) // expression[opt] ';' return ParseIdentifierStatement(OnlyStatement); default: if (!OnlyStatement && isDeclarationSpecifier()) { // TODO: warn/disable if declaration is in the middle of a block and !C99. ParseDeclaration(Declarator::BlockContext); // FIXME: Make a DeclStmt node! return 0; } else if (Tok.getKind() == tok::r_brace) { Diag(Tok, diag::err_expected_statement); return true; } else { // expression[opt] ';' ExprResult Res = ParseExpression(); if (Res.isInvalid) { // If the expression is invalid, skip ahead to the next semicolon. Not // doing this opens us up to the possibility of infinite loops if // ParseExpression does not consume any tokens. SkipUntil(tok::semi); return true; } else { return Actions.ParseExprStmt(Res.Val); } } case tok::kw_case: // C99 6.8.1: labeled-statement return ParseCaseStatement(); case tok::kw_default: // C99 6.8.1: labeled-statement return ParseDefaultStatement(); case tok::l_brace: // C99 6.8.2: compound-statement return ParseCompoundStatement(); case tok::semi: // C99 6.8.3: expression[opt] ';' // TODO: Could return a NullStmt action result if we cared to. ConsumeToken(); return 0; case tok::kw_if: // C99 6.8.4.1: if-statement return ParseIfStatement(); case tok::kw_switch: // C99 6.8.4.2: switch-statement return ParseSwitchStatement(); case tok::kw_while: // C99 6.8.5.1: while-statement return ParseWhileStatement(); case tok::kw_do: // C99 6.8.5.2: do-statement Res = ParseDoStatement(); SemiError = "do/while loop"; break; case tok::kw_for: // C99 6.8.5.3: for-statement return ParseForStatement(); case tok::kw_goto: // C99 6.8.6.1: goto-statement Res = ParseGotoStatement(); SemiError = "goto statement"; break; case tok::kw_continue: // C99 6.8.6.2: continue-statement Res = ParseContinueStatement(); SemiError = "continue statement"; break; case tok::kw_break: // C99 6.8.6.3: break-statement Res = ParseBreakStatement(); SemiError = "break statement"; break; case tok::kw_return: // C99 6.8.6.4: return-statement Res = ParseReturnStatement(); SemiError = "return statement"; break; case tok::kw_asm: Res = ParseAsmStatement(); SemiError = "asm statement"; break; } // If we reached this code, the statement must end in a semicolon. if (Tok.getKind() == tok::semi) { ConsumeToken(); } else { Diag(Tok, diag::err_expected_semi_after, SemiError); SkipUntil(tok::semi); } return Res; } /// ParseIdentifierStatement - Because we don't have two-token lookahead, we /// have a bit of a quandry here. Reading the identifier is necessary to see if /// there is a ':' after it. If there is, this is a label, regardless of what /// else the identifier can mean. If not, this is either part of a declaration /// (if the identifier is a type-name) or part of an expression. /// /// labeled-statement: /// identifier ':' statement /// [GNU] identifier ':' attributes[opt] statement /// declaration (if !OnlyStatement) /// expression[opt] ';' /// Parser::StmtResult Parser::ParseIdentifierStatement(bool OnlyStatement) { assert(Tok.getKind() == tok::identifier && Tok.getIdentifierInfo() && "Not an identifier!"); LexerToken IdentTok = Tok; // Save the whole token. ConsumeToken(); // eat the identifier. // identifier ':' statement if (Tok.getKind() == tok::colon) { SourceLocation ColonLoc = ConsumeToken(); // Read label attributes, if present. if (Tok.getKind() == tok::kw___attribute) // TODO: save these somewhere. ParseAttributes(); StmtResult SubStmt = ParseStatement(); if (SubStmt.isInvalid) return true; // FIXME: Enter this label into the symbol table for the function. return Actions.ParseLabelStmt(IdentTok.getLocation(), IdentTok.getIdentifierInfo(), ColonLoc, SubStmt.Val); } // Check to see if this is a declaration. void *TypeRep; if (!OnlyStatement && (TypeRep = Actions.isTypeName(*IdentTok.getIdentifierInfo(), CurScope))) { // Handle this. Warn/disable if in middle of block and !C99. DeclSpec DS; // Add the typedef name to the start of the decl-specs. const char *PrevSpec = 0; int isInvalid = DS.SetTypeSpecType(DeclSpec::TST_typedef, IdentTok.getLocation(), PrevSpec, TypeRep); assert(!isInvalid && "First declspec can't be invalid!"); // ParseDeclarationSpecifiers will continue from there. ParseDeclarationSpecifiers(DS); // C99 6.7.2.3p6: Handle "struct-or-union identifier;", "enum { X };" // declaration-specifiers init-declarator-list[opt] ';' if (Tok.getKind() == tok::semi) { // TODO: emit error on 'int;' or 'const enum foo;'. // if (!DS.isMissingDeclaratorOk()) Diag(...); ConsumeToken(); // FIXME: Return this as a type decl. return 0; } // Parse all the declarators. Declarator DeclaratorInfo(DS, Declarator::BlockContext); ParseDeclarator(DeclaratorInfo); ParseInitDeclaratorListAfterFirstDeclarator(DeclaratorInfo); // FIXME: Return this as a declstmt. return 0; } // Otherwise, this is an expression. Seed it with II and parse it. ExprResult Res = ParseExpressionWithLeadingIdentifier(IdentTok); if (Res.isInvalid) { SkipUntil(tok::semi); return true; } else if (Tok.getKind() != tok::semi) { Diag(Tok, diag::err_expected_semi_after, "expression"); SkipUntil(tok::semi); return true; } else { ConsumeToken(); return Actions.ParseExprStmt(Res.Val); } } /// ParseCaseStatement /// labeled-statement: /// 'case' constant-expression ':' statement /// [GNU] 'case' constant-expression '...' constant-expression ':' statement /// /// Note that this does not parse the 'statement' at the end. /// Parser::StmtResult Parser::ParseCaseStatement() { assert(Tok.getKind() == tok::kw_case && "Not a case stmt!"); SourceLocation CaseLoc = ConsumeToken(); // eat the 'case'. ExprResult LHS = ParseConstantExpression(); if (LHS.isInvalid) { SkipUntil(tok::colon); return true; } // GNU case range extension. SourceLocation DotDotDotLoc; ExprTy *RHSVal = 0; if (Tok.getKind() == tok::ellipsis) { Diag(Tok, diag::ext_gnu_case_range); DotDotDotLoc = ConsumeToken(); ExprResult RHS = ParseConstantExpression(); if (RHS.isInvalid) { SkipUntil(tok::colon); return true; } RHSVal = RHS.Val; } if (Tok.getKind() != tok::colon) { Diag(Tok, diag::err_expected_colon_after, "'case'"); SkipUntil(tok::colon); return true; } SourceLocation ColonLoc = ConsumeToken(); // Diagnose the common error "switch (X) { case 4: }", which is not valid. if (Tok.getKind() == tok::r_brace) { Diag(Tok, diag::err_label_end_of_compound_statement); return true; } StmtResult SubStmt = ParseStatement(); if (SubStmt.isInvalid) return true; // TODO: look up enclosing switch stmt. return Actions.ParseCaseStmt(CaseLoc, LHS.Val, DotDotDotLoc, RHSVal, ColonLoc, SubStmt.Val); } /// ParseDefaultStatement /// labeled-statement: /// 'default' ':' statement /// Note that this does not parse the 'statement' at the end. /// Parser::StmtResult Parser::ParseDefaultStatement() { assert(Tok.getKind() == tok::kw_default && "Not a default stmt!"); SourceLocation DefaultLoc = ConsumeToken(); // eat the 'default'. if (Tok.getKind() != tok::colon) { Diag(Tok, diag::err_expected_colon_after, "'default'"); SkipUntil(tok::colon); return true; } SourceLocation ColonLoc = ConsumeToken(); // Diagnose the common error "switch (X) {... default: }", which is not valid. if (Tok.getKind() == tok::r_brace) { Diag(Tok, diag::err_label_end_of_compound_statement); return true; } StmtResult SubStmt = ParseStatement(); if (SubStmt.isInvalid) return true; // TODO: look up enclosing switch stmt. return Actions.ParseDefaultStmt(DefaultLoc, ColonLoc, SubStmt.Val); } /// ParseCompoundStatement - Parse a "{}" block. /// /// compound-statement: [C99 6.8.2] /// { block-item-list[opt] } /// [GNU] { label-declarations block-item-list } [TODO] /// /// block-item-list: /// block-item /// block-item-list block-item /// /// block-item: /// declaration /// [GNU] '__extension__' declaration [TODO] /// statement /// [OMP] openmp-directive [TODO] /// /// [GNU] label-declarations: /// [GNU] label-declaration /// [GNU] label-declarations label-declaration /// /// [GNU] label-declaration: /// [GNU] '__label__' identifier-list ';' /// /// [OMP] openmp-directive: [TODO] /// [OMP] barrier-directive /// [OMP] flush-directive /// Parser::StmtResult Parser::ParseCompoundStatement() { assert(Tok.getKind() == tok::l_brace && "Not a compount stmt!"); // Enter a scope to hold everything within the compound stmt. EnterScope(0); // Parse the statements in the body. StmtResult Body = ParseCompoundStatementBody(); ExitScope(); return Body; } /// ParseCompoundStatementBody - Parse a sequence of statements and invoke the /// ParseCompoundStmt action. This expects the '{' to be the current token, and /// consume the '}' at the end of the block. It does not manipulate the scope /// stack. Parser::StmtResult Parser::ParseCompoundStatementBody() { SourceLocation LBraceLoc = ConsumeBrace(); // eat the '{'. SmallVector Stmts; while (Tok.getKind() != tok::r_brace && Tok.getKind() != tok::eof) { StmtResult R = ParseStatementOrDeclaration(false); if (!R.isInvalid && R.Val) Stmts.push_back(R.Val); } // We broke out of the while loop because we found a '}' or EOF. if (Tok.getKind() != tok::r_brace) { Diag(Tok, diag::err_expected_rbrace); return 0; } SourceLocation RBraceLoc = ConsumeBrace(); return Actions.ParseCompoundStmt(LBraceLoc, RBraceLoc, &Stmts[0], Stmts.size()); } /// ParseIfStatement /// if-statement: [C99 6.8.4.1] /// 'if' '(' expression ')' statement /// 'if' '(' expression ')' statement 'else' statement /// Parser::StmtResult Parser::ParseIfStatement() { assert(Tok.getKind() == tok::kw_if && "Not an if stmt!"); SourceLocation IfLoc = ConsumeToken(); // eat the 'if'. if (Tok.getKind() != tok::l_paren) { Diag(Tok, diag::err_expected_lparen_after, "if"); SkipUntil(tok::semi); return true; } // Parse the condition. ExprResult CondExp = ParseSimpleParenExpression(); if (CondExp.isInvalid) { SkipUntil(tok::semi); return true; } // Read the if condition. StmtResult CondStmt = ParseStatement(); // If it has an else, parse it. SourceLocation ElseLoc; StmtResult ElseStmt(false); if (Tok.getKind() == tok::kw_else) { ElseLoc = ConsumeToken(); ElseStmt = ParseStatement(); } if (CondStmt.isInvalid || ElseStmt.isInvalid) return true; return Actions.ParseIfStmt(IfLoc, CondExp.Val, CondStmt.Val, ElseLoc, ElseStmt.Val); } /// ParseSwitchStatement /// switch-statement: /// 'switch' '(' expression ')' statement Parser::StmtResult Parser::ParseSwitchStatement() { assert(Tok.getKind() == tok::kw_switch && "Not a switch stmt!"); SourceLocation SwitchLoc = ConsumeToken(); // eat the 'switch'. if (Tok.getKind() != tok::l_paren) { Diag(Tok, diag::err_expected_lparen_after, "switch"); SkipUntil(tok::semi); return true; } // Start the switch scope. EnterScope(Scope::BreakScope); // Parse the condition. ExprResult Cond = ParseSimpleParenExpression(); // Read the body statement. StmtResult Body = ParseStatement(); ExitScope(); if (Cond.isInvalid || Body.isInvalid) return true; return Actions.ParseSwitchStmt(SwitchLoc, Cond.Val, Body.Val); } /// ParseWhileStatement /// while-statement: [C99 6.8.5.1] /// 'while' '(' expression ')' statement Parser::StmtResult Parser::ParseWhileStatement() { assert(Tok.getKind() == tok::kw_while && "Not a while stmt!"); SourceLocation WhileLoc = Tok.getLocation(); ConsumeToken(); // eat the 'while'. if (Tok.getKind() != tok::l_paren) { Diag(Tok, diag::err_expected_lparen_after, "while"); SkipUntil(tok::semi); return true; } // Start the loop scope. EnterScope(Scope::BreakScope | Scope::ContinueScope); // Parse the condition. ExprResult Cond = ParseSimpleParenExpression(); // Read the body statement. StmtResult Body = ParseStatement(); ExitScope(); if (Cond.isInvalid || Body.isInvalid) return true; return Actions.ParseWhileStmt(WhileLoc, Cond.Val, Body.Val); } /// ParseDoStatement /// do-statement: [C99 6.8.5.2] /// 'do' statement 'while' '(' expression ')' ';' /// Note: this lets the caller parse the end ';'. Parser::StmtResult Parser::ParseDoStatement() { assert(Tok.getKind() == tok::kw_do && "Not a do stmt!"); SourceLocation DoLoc = ConsumeToken(); // eat the 'do'. // Start the loop scope. EnterScope(Scope::BreakScope | Scope::ContinueScope); // Read the body statement. StmtResult Body = ParseStatement(); if (Tok.getKind() != tok::kw_while) { ExitScope(); Diag(Tok, diag::err_expected_while); Diag(DoLoc, diag::err_matching, "do"); SkipUntil(tok::semi); return true; } SourceLocation WhileLoc = ConsumeToken(); if (Tok.getKind() != tok::l_paren) { ExitScope(); Diag(Tok, diag::err_expected_lparen_after, "do/while"); SkipUntil(tok::semi); return true; } // Parse the condition. ExprResult Cond = ParseSimpleParenExpression(); ExitScope(); if (Cond.isInvalid || Body.isInvalid) return true; return Actions.ParseDoStmt(DoLoc, Body.Val, WhileLoc, Cond.Val); } /// ParseForStatement /// for-statement: [C99 6.8.5.3] /// 'for' '(' expr[opt] ';' expr[opt] ';' expr[opt] ')' statement /// 'for' '(' declaration expr[opt] ';' expr[opt] ')' statement Parser::StmtResult Parser::ParseForStatement() { assert(Tok.getKind() == tok::kw_for && "Not a for stmt!"); SourceLocation ForLoc = ConsumeToken(); // eat the 'for'. if (Tok.getKind() != tok::l_paren) { Diag(Tok, diag::err_expected_lparen_after, "for"); SkipUntil(tok::semi); return true; } EnterScope(Scope::BreakScope | Scope::ContinueScope); SourceLocation LParenLoc = ConsumeParen(); ExprResult Value; StmtTy *FirstPart = 0; ExprTy *SecondPart = 0, *ThirdPart = 0; // Parse the first part of the for specifier. if (Tok.getKind() == tok::semi) { // for (; // no first part, eat the ';'. ConsumeToken(); } else if (isDeclarationSpecifier()) { // for (int X = 4; // Parse declaration, which eats the ';'. if (!getLang().C99) // Use of C99-style for loops in C90 mode? Diag(Tok, diag::ext_c99_variable_decl_in_for_loop); ParseDeclaration(Declarator::ForContext); // FIXME: Turn declaration into a stmt ast node. FirstPart = 0; } else { Value = ParseExpression(); // Turn the expression into a stmt. if (!Value.isInvalid) { StmtResult R = Actions.ParseExprStmt(Value.Val); if (!R.isInvalid) FirstPart = R.Val; } if (Tok.getKind() == tok::semi) { ConsumeToken(); } else { if (!Value.isInvalid) Diag(Tok, diag::err_expected_semi_for); SkipUntil(tok::semi); } } // Parse the second part of the for specifier. if (Tok.getKind() == tok::semi) { // for (...;; // no second part. Value = ExprResult(); } else { Value = ParseExpression(); if (!Value.isInvalid) SecondPart = Value.Val; } if (Tok.getKind() == tok::semi) { ConsumeToken(); } else { if (!Value.isInvalid) Diag(Tok, diag::err_expected_semi_for); SkipUntil(tok::semi); } // Parse the third part of the for specifier. if (Tok.getKind() == tok::r_paren) { // for (...;...;) // no third part. Value = ExprResult(); } else { Value = ParseExpression(); if (!Value.isInvalid) ThirdPart = Value.Val; } // Match the ')'. SourceLocation RParenLoc = MatchRHSPunctuation(tok::r_paren, LParenLoc); // Read the body statement. StmtResult Body = ParseStatement(); // Leave the for-scope. ExitScope(); if (Body.isInvalid) return Body; return Actions.ParseForStmt(ForLoc, LParenLoc, FirstPart, SecondPart, ThirdPart, RParenLoc, Body.Val); } /// ParseGotoStatement /// jump-statement: /// 'goto' identifier ';' /// [GNU] 'goto' '*' expression ';' /// /// Note: this lets the caller parse the end ';'. /// Parser::StmtResult Parser::ParseGotoStatement() { assert(Tok.getKind() == tok::kw_goto && "Not a goto stmt!"); SourceLocation GotoLoc = ConsumeToken(); // eat the 'goto'. StmtResult Res; if (Tok.getKind() == tok::identifier) { Res = Actions.ParseGotoStmt(GotoLoc, Tok.getLocation(), Tok.getIdentifierInfo()); ConsumeToken(); } else if (Tok.getKind() == tok::star && !getLang().NoExtensions) { // GNU indirect goto extension. Diag(Tok, diag::ext_gnu_indirect_goto); SourceLocation StarLoc = ConsumeToken(); ExprResult R = ParseExpression(); if (R.isInvalid) { // Skip to the semicolon, but don't consume it. SkipUntil(tok::semi, false, true); return true; } Res = Actions.ParseIndirectGotoStmt(GotoLoc, StarLoc, R.Val); } return Res; } /// ParseContinueStatement /// jump-statement: /// 'continue' ';' /// /// Note: this lets the caller parse the end ';'. /// Parser::StmtResult Parser::ParseContinueStatement() { SourceLocation ContinueLoc = ConsumeToken(); // eat the 'continue'. return Actions.ParseContinueStmt(ContinueLoc, CurScope); } /// ParseBreakStatement /// jump-statement: /// 'break' ';' /// /// Note: this lets the caller parse the end ';'. /// Parser::StmtResult Parser::ParseBreakStatement() { SourceLocation BreakLoc = ConsumeToken(); // eat the 'break'. return Actions.ParseBreakStmt(BreakLoc, CurScope); } /// ParseReturnStatement /// jump-statement: /// 'return' expression[opt] ';' Parser::StmtResult Parser::ParseReturnStatement() { assert(Tok.getKind() == tok::kw_return && "Not a return stmt!"); SourceLocation ReturnLoc = ConsumeToken(); // eat the 'return'. ExprResult R(0); if (Tok.getKind() != tok::semi) { R = ParseExpression(); if (R.isInvalid) { // Skip to the semicolon, but don't consume it. SkipUntil(tok::semi, false, true); return true; } } return Actions.ParseReturnStmt(ReturnLoc, R.Val); } /// ParseAsmStatement - Parse a GNU extended asm statement. /// [GNU] asm-statement: /// 'asm' type-qualifier[opt] '(' asm-argument ')' ';' /// /// [GNU] asm-argument: /// asm-string-literal /// asm-string-literal ':' asm-operands[opt] /// asm-string-literal ':' asm-operands[opt] ':' asm-operands[opt] /// asm-string-literal ':' asm-operands[opt] ':' asm-operands[opt] /// ':' asm-clobbers /// /// [GNU] asm-clobbers: /// asm-string-literal /// asm-clobbers ',' asm-string-literal /// Parser::StmtResult Parser::ParseAsmStatement() { assert(Tok.getKind() == tok::kw_asm && "Not an asm stmt"); ConsumeToken(); DeclSpec DS; SourceLocation Loc = Tok.getLocation(); ParseTypeQualifierListOpt(DS); // GNU asms accept, but warn, about type-qualifiers other than volatile. if (DS.getTypeQualifiers() & DeclSpec::TQ_const) Diag(Loc, diag::w_asm_qualifier_ignored, "const"); if (DS.getTypeQualifiers() & DeclSpec::TQ_restrict) Diag(Loc, diag::w_asm_qualifier_ignored, "restrict"); // Remember if this was a volatile asm. //bool isVolatile = DS.TypeQualifiers & DeclSpec::TQ_volatile; if (Tok.getKind() != tok::l_paren) { Diag(Tok, diag::err_expected_lparen_after, "asm"); SkipUntil(tok::r_paren); return true; } Loc = ConsumeParen(); ParseAsmStringLiteral(); // Parse Outputs, if present. ParseAsmOperandsOpt(); // Parse Inputs, if present. ParseAsmOperandsOpt(); // Parse the clobbers, if present. if (Tok.getKind() == tok::colon) { ConsumeToken(); if (isTokenStringLiteral()) { // Parse the asm-string list for clobbers. while (1) { ParseAsmStringLiteral(); if (Tok.getKind() != tok::comma) break; ConsumeToken(); } } } MatchRHSPunctuation(tok::r_paren, Loc); // FIXME: Implement action for asm parsing. return false; } /// ParseAsmOperands - Parse the asm-operands production as used by /// asm-statement. We also parse a leading ':' token. If the leading colon is /// not present, we do not parse anything. /// /// [GNU] asm-operands: /// asm-operand /// asm-operands ',' asm-operand /// /// [GNU] asm-operand: /// asm-string-literal '(' expression ')' /// '[' identifier ']' asm-string-literal '(' expression ')' /// void Parser::ParseAsmOperandsOpt() { // Only do anything if this operand is present. if (Tok.getKind() != tok::colon) return; ConsumeToken(); // 'asm-operands' isn't present? if (!isTokenStringLiteral() && Tok.getKind() != tok::l_square) return; while (1) { // Read the [id] if present. if (Tok.getKind() == tok::l_square) { SourceLocation Loc = ConsumeBracket(); if (Tok.getKind() != tok::identifier) { Diag(Tok, diag::err_expected_ident); SkipUntil(tok::r_paren); return; } MatchRHSPunctuation(tok::r_square, Loc); } ParseAsmStringLiteral(); if (Tok.getKind() != tok::l_paren) { Diag(Tok, diag::err_expected_lparen_after, "asm operand"); SkipUntil(tok::r_paren); return; } // Read the parenthesized expression. ExprResult Res = ParseSimpleParenExpression(); if (Res.isInvalid) { SkipUntil(tok::r_paren); return; } // Eat the comma and continue parsing if it exists. if (Tok.getKind() != tok::comma) return; ConsumeToken(); } }