diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h --- a/clang/include/clang/AST/Stmt.h +++ b/clang/include/clang/AST/Stmt.h @@ -2277,6 +2277,8 @@ enum { VarOffset = 0, BodyOffsetFromCond = 1 }; enum { NumMandatoryStmtPtr = 2 }; + SourceLocation LParenLoc, RParenLoc; + unsigned varOffset() const { return VarOffset; } unsigned condOffset() const { return VarOffset + hasVarStorage(); } unsigned bodyOffset() const { return condOffset() + BodyOffsetFromCond; } @@ -2287,7 +2289,8 @@ /// Build a while statement. WhileStmt(const ASTContext &Ctx, VarDecl *Var, Expr *Cond, Stmt *Body, - SourceLocation WL); + SourceLocation WL, SourceLocation LParenLoc, + SourceLocation RParenLoc); /// Build an empty while statement. explicit WhileStmt(EmptyShell Empty, bool HasVar); @@ -2295,7 +2298,8 @@ public: /// Create a while statement. static WhileStmt *Create(const ASTContext &Ctx, VarDecl *Var, Expr *Cond, - Stmt *Body, SourceLocation WL); + Stmt *Body, SourceLocation WL, + SourceLocation LParenLoc, SourceLocation RParenLoc); /// Create an empty while statement optionally with storage for /// a condition variable. @@ -2359,6 +2363,11 @@ SourceLocation getWhileLoc() const { return WhileStmtBits.WhileLoc; } void setWhileLoc(SourceLocation L) { WhileStmtBits.WhileLoc = L; } + SourceLocation getLParenLoc() const { return LParenLoc; } + void setLParenLoc(SourceLocation L) { LParenLoc = L; } + SourceLocation getRParenLoc() const { return RParenLoc; } + void setRParenLoc(SourceLocation L) { RParenLoc = L; } + SourceLocation getBeginLoc() const { return getWhileLoc(); } SourceLocation getEndLoc() const LLVM_READONLY { return getBody()->getEndLoc(); diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -2071,8 +2071,9 @@ StmtResult ParseCompoundStatementBody(bool isStmtExpr = false); bool ParseParenExprOrCondition(StmtResult *InitStmt, Sema::ConditionResult &CondResult, - SourceLocation Loc, - Sema::ConditionKind CK); + SourceLocation Loc, Sema::ConditionKind CK, + SourceLocation *LParenLoc = nullptr, + SourceLocation *RParenLoc = nullptr); StmtResult ParseIfStatement(SourceLocation *TrailingElseLoc); StmtResult ParseSwitchStatement(SourceLocation *TrailingElseLoc); StmtResult ParseWhileStatement(SourceLocation *TrailingElseLoc); diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -4377,7 +4377,8 @@ ConditionResult Cond); StmtResult ActOnFinishSwitchStmt(SourceLocation SwitchLoc, Stmt *Switch, Stmt *Body); - StmtResult ActOnWhileStmt(SourceLocation WhileLoc, ConditionResult Cond, + StmtResult ActOnWhileStmt(SourceLocation WhileLoc, SourceLocation LParenLoc, + ConditionResult Cond, SourceLocation RParenLoc, Stmt *Body); StmtResult ActOnDoStmt(SourceLocation DoLoc, Stmt *Body, SourceLocation WhileLoc, SourceLocation CondLParen, diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -6117,11 +6117,13 @@ auto ToCond = importChecked(Err, S->getCond()); auto ToBody = importChecked(Err, S->getBody()); auto ToWhileLoc = importChecked(Err, S->getWhileLoc()); + auto ToLParenLoc = importChecked(Err, S->getLParenLoc()); + auto ToRParenLoc = importChecked(Err, S->getRParenLoc()); if (Err) return std::move(Err); return WhileStmt::Create(Importer.getToContext(), ToConditionVariable, ToCond, - ToBody, ToWhileLoc); + ToBody, ToWhileLoc, ToLParenLoc, ToRParenLoc); } ExpectedStmt ASTNodeImporter::VisitDoStmt(DoStmt *S) { diff --git a/clang/lib/AST/Stmt.cpp b/clang/lib/AST/Stmt.cpp --- a/clang/lib/AST/Stmt.cpp +++ b/clang/lib/AST/Stmt.cpp @@ -1012,7 +1012,8 @@ } WhileStmt::WhileStmt(const ASTContext &Ctx, VarDecl *Var, Expr *Cond, - Stmt *Body, SourceLocation WL) + Stmt *Body, SourceLocation WL, SourceLocation LParenLoc, + SourceLocation RParenLoc) : Stmt(WhileStmtClass) { bool HasVar = Var != nullptr; WhileStmtBits.HasVar = HasVar; @@ -1023,6 +1024,8 @@ setConditionVariable(Ctx, Var); setWhileLoc(WL); + setLParenLoc(LParenLoc); + setRParenLoc(RParenLoc); } WhileStmt::WhileStmt(EmptyShell Empty, bool HasVar) @@ -1031,12 +1034,14 @@ } WhileStmt *WhileStmt::Create(const ASTContext &Ctx, VarDecl *Var, Expr *Cond, - Stmt *Body, SourceLocation WL) { + Stmt *Body, SourceLocation WL, + SourceLocation LParenLoc, + SourceLocation RParenLoc) { bool HasVar = Var != nullptr; void *Mem = Ctx.Allocate(totalSizeToAlloc(NumMandatoryStmtPtr + HasVar), alignof(WhileStmt)); - return new (Mem) WhileStmt(Ctx, Var, Cond, Body, WL); + return new (Mem) WhileStmt(Ctx, Var, Cond, Body, WL, LParenLoc, RParenLoc); } WhileStmt *WhileStmt::CreateEmpty(const ASTContext &Ctx, bool HasVar) { diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp --- a/clang/lib/Parse/ParseStmt.cpp +++ b/clang/lib/Parse/ParseStmt.cpp @@ -1156,10 +1156,14 @@ /// should try to recover harder. It returns false if the condition is /// successfully parsed. Note that a successful parse can still have semantic /// errors in the condition. +/// Additionally, if LParenLoc and RParenLoc are non-null, it will assign +/// the location of the outer-most '(' and ')', respectively, to them. bool Parser::ParseParenExprOrCondition(StmtResult *InitStmt, Sema::ConditionResult &Cond, SourceLocation Loc, - Sema::ConditionKind CK) { + Sema::ConditionKind CK, + SourceLocation *LParenLoc, + SourceLocation *RParenLoc) { BalancedDelimiterTracker T(*this, tok::l_paren); T.consumeOpen(); @@ -1189,6 +1193,13 @@ // Otherwise the condition is valid or the rparen is present. T.consumeClose(); + if (LParenLoc != nullptr) { + *LParenLoc = T.getOpenLocation(); + } + if (RParenLoc != nullptr) { + *RParenLoc = T.getCloseLocation(); + } + // Check for extraneous ')'s to catch things like "if (foo())) {". We know // that all callers are looking for a statement after the condition, so ")" // isn't valid. @@ -1582,8 +1593,10 @@ // Parse the condition. Sema::ConditionResult Cond; + SourceLocation LParen; + SourceLocation RParen; if (ParseParenExprOrCondition(nullptr, Cond, WhileLoc, - Sema::ConditionKind::Boolean)) + Sema::ConditionKind::Boolean, &LParen, &RParen)) return StmtError(); // C99 6.8.5p5 - In C99, the body of the while statement is a scope, even if @@ -1613,7 +1626,7 @@ if (Cond.isInvalid() || Body.isInvalid()) return StmtError(); - return Actions.ActOnWhileStmt(WhileLoc, Cond, Body.get()); + return Actions.ActOnWhileStmt(WhileLoc, LParen, Cond, RParen, Body.get()); } /// ParseDoStatement diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -1328,8 +1328,9 @@ } } -StmtResult Sema::ActOnWhileStmt(SourceLocation WhileLoc, ConditionResult Cond, - Stmt *Body) { +StmtResult Sema::ActOnWhileStmt(SourceLocation WhileLoc, + SourceLocation LParenLoc, ConditionResult Cond, + SourceLocation RParenLoc, Stmt *Body) { if (Cond.isInvalid()) return StmtError(); @@ -1344,7 +1345,7 @@ getCurCompoundScope().setHasEmptyLoopBodies(); return WhileStmt::Create(Context, CondVal.first, CondVal.second, Body, - WhileLoc); + WhileLoc, LParenLoc, RParenLoc); } StmtResult diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -1347,9 +1347,10 @@ /// /// By default, performs semantic analysis to build the new statement. /// Subclasses may override this routine to provide different behavior. - StmtResult RebuildWhileStmt(SourceLocation WhileLoc, - Sema::ConditionResult Cond, Stmt *Body) { - return getSema().ActOnWhileStmt(WhileLoc, Cond, Body); + StmtResult RebuildWhileStmt(SourceLocation WhileLoc, SourceLocation LParenLoc, + Sema::ConditionResult Cond, + SourceLocation RParenLoc, Stmt *Body) { + return getSema().ActOnWhileStmt(WhileLoc, LParenLoc, Cond, RParenLoc, Body); } /// Build a new do-while statement. @@ -7335,7 +7336,8 @@ Body.get() == S->getBody()) return Owned(S); - return getDerived().RebuildWhileStmt(S->getWhileLoc(), Cond, Body.get()); + return getDerived().RebuildWhileStmt(S->getWhileLoc(), S->getLParenLoc(), + Cond, S->getRParenLoc(), Body.get()); } template diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -271,6 +271,8 @@ S->setConditionVariable(Record.getContext(), readDeclAs()); S->setWhileLoc(readSourceLocation()); + S->setLParenLoc(readSourceLocation()); + S->setRParenLoc(readSourceLocation()); } void ASTStmtReader::VisitDoStmt(DoStmt *S) { diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -194,6 +194,8 @@ Record.AddDeclRef(S->getConditionVariable()); Record.AddSourceLocation(S->getWhileLoc()); + Record.AddSourceLocation(S->getLParenLoc()); + Record.AddSourceLocation(S->getRParenLoc()); Code = serialization::STMT_WHILE; } diff --git a/clang/unittests/AST/SourceLocationTest.cpp b/clang/unittests/AST/SourceLocationTest.cpp --- a/clang/unittests/AST/SourceLocationTest.cpp +++ b/clang/unittests/AST/SourceLocationTest.cpp @@ -60,6 +60,59 @@ EXPECT_FALSE(Verifier.match("int i;", varDecl())); } +class WhileParenLocationVerifier : public MatchVerifier { + unsigned ExpectLParenLine = 0, ExpectLParenColumn = 0; + unsigned ExpectRParenLine = 0, ExpectRParenColumn = 0; + +public: + void expectLocations(unsigned LParenLine, unsigned LParenColumn, + unsigned RParenLine, unsigned RParenColumn) { + ExpectLParenLine = LParenLine; + ExpectLParenColumn = LParenColumn; + ExpectRParenLine = RParenLine; + ExpectRParenColumn = RParenColumn; + } + +protected: + void verify(const MatchFinder::MatchResult &Result, + const WhileStmt &Node) override { + SourceLocation LParenLoc = Node.getLParenLoc(); + SourceLocation RParenLoc = Node.getRParenLoc(); + unsigned LParenLine = + Result.SourceManager->getSpellingLineNumber(LParenLoc); + unsigned LParenColumn = + Result.SourceManager->getSpellingColumnNumber(LParenLoc); + unsigned RParenLine = + Result.SourceManager->getSpellingLineNumber(RParenLoc); + unsigned RParenColumn = + Result.SourceManager->getSpellingColumnNumber(RParenLoc); + + if (LParenLine != ExpectLParenLine || LParenColumn != ExpectLParenColumn || + RParenLine != ExpectRParenLine || RParenColumn != ExpectRParenColumn) { + std::string MsgStr; + llvm::raw_string_ostream Msg(MsgStr); + Msg << "Expected LParen Location <" << ExpectLParenLine << ":" + << ExpectLParenColumn << ">, found <"; + LParenLoc.print(Msg, *Result.SourceManager); + Msg << ">\n"; + + Msg << "Expected RParen Location <" << ExpectRParenLine << ":" + << ExpectRParenColumn << ">, found <"; + RParenLoc.print(Msg, *Result.SourceManager); + Msg << ">"; + + this->setFailure(Msg.str()); + } + } +}; + +TEST(LocationVerifier, WhileParenLoc) { + WhileParenLocationVerifier Verifier; + Verifier.expectLocations(1, 17, 1, 38); + EXPECT_TRUE(Verifier.match("void f() { while(true/*some comment*/) {} }", + whileStmt())); +} + class LabelDeclRangeVerifier : public RangeVerifier { protected: SourceRange getRange(const LabelStmt &Node) override {