diff options
| author | Eli Friedman <efriedma@codeaurora.org> | 2017-07-04 00:52:24 +0000 |
|---|---|---|
| committer | Eli Friedman <efriedma@codeaurora.org> | 2017-07-04 00:52:24 +0000 |
| commit | e91b2e682cde043537d16a766ee246d756bb2476 (patch) | |
| tree | ad4b821371c5972f98157d36274a9c848bf6ab4d /clang/lib | |
| parent | fa6e67526780ec76b39f511f348376d1d8d421a2 (diff) | |
| download | bcm5719-llvm-e91b2e682cde043537d16a766ee246d756bb2476.tar.gz bcm5719-llvm-e91b2e682cde043537d16a766ee246d756bb2476.zip | |
[Sema] Make BreakContinueFinder handle nested loops.
We don't care about break or continue statements that aren't
associated with the current loop, so make sure the visitor
doesn't find them.
Fixes https://bugs.llvm.org/show_bug.cgi?id=32648 .
Differential Revision: https://reviews.llvm.org/D34568
llvm-svn: 307051
Diffstat (limited to 'clang/lib')
| -rw-r--r-- | clang/lib/Sema/SemaStmt.cpp | 67 |
1 files changed, 61 insertions, 6 deletions
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index eed10b077eb..2a38a1f8e1d 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -1544,23 +1544,78 @@ namespace { // A visitor to determine if a continue or break statement is a // subexpression. - class BreakContinueFinder : public EvaluatedExprVisitor<BreakContinueFinder> { + class BreakContinueFinder : public ConstEvaluatedExprVisitor<BreakContinueFinder> { SourceLocation BreakLoc; SourceLocation ContinueLoc; + bool InSwitch = false; + public: - BreakContinueFinder(Sema &S, Stmt* Body) : + BreakContinueFinder(Sema &S, const Stmt* Body) : Inherited(S.Context) { Visit(Body); } - typedef EvaluatedExprVisitor<BreakContinueFinder> Inherited; + typedef ConstEvaluatedExprVisitor<BreakContinueFinder> Inherited; - void VisitContinueStmt(ContinueStmt* E) { + void VisitContinueStmt(const ContinueStmt* E) { ContinueLoc = E->getContinueLoc(); } - void VisitBreakStmt(BreakStmt* E) { - BreakLoc = E->getBreakLoc(); + void VisitBreakStmt(const BreakStmt* E) { + if (!InSwitch) + BreakLoc = E->getBreakLoc(); + } + + void VisitSwitchStmt(const SwitchStmt* S) { + if (const Stmt *Init = S->getInit()) + Visit(Init); + if (const Stmt *CondVar = S->getConditionVariableDeclStmt()) + Visit(CondVar); + if (const Stmt *Cond = S->getCond()) + Visit(Cond); + + // Don't return break statements from the body of a switch. + InSwitch = true; + if (const Stmt *Body = S->getBody()) + Visit(Body); + InSwitch = false; + } + + void VisitForStmt(const ForStmt *S) { + // Only visit the init statement of a for loop; the body + // has a different break/continue scope. + if (const Stmt *Init = S->getInit()) + Visit(Init); + } + + void VisitWhileStmt(const WhileStmt *) { + // Do nothing; the children of a while loop have a different + // break/continue scope. + } + + void VisitDoStmt(const DoStmt *) { + // Do nothing; the children of a while loop have a different + // break/continue scope. + } + + void VisitCXXForRangeStmt(const CXXForRangeStmt *S) { + // Only visit the initialization of a for loop; the body + // has a different break/continue scope. + if (const Stmt *Range = S->getRangeStmt()) + Visit(Range); + if (const Stmt *Begin = S->getBeginStmt()) + Visit(Begin); + if (const Stmt *End = S->getEndStmt()) + Visit(End); + } + + void VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S) { + // Only visit the initialization of a for loop; the body + // has a different break/continue scope. + if (const Stmt *Element = S->getElement()) + Visit(Element); + if (const Stmt *Collection = S->getCollection()) + Visit(Collection); } bool ContinueFound() { return ContinueLoc.isValid(); } |

