2828#include " clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h"
2929#include " clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
3030#include " clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
31+ #include " clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
3132#include " clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
33+ #include " clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
3234#include " llvm/ADT/DenseMap.h"
35+ #include " llvm/ADT/STLExtras.h"
3336#include " llvm/Support/FormatVariadic.h"
3437#include " llvm/Support/raw_ostream.h"
3538#include < optional>
@@ -42,17 +45,36 @@ using llvm::formatv;
4245namespace {
4346
4447class SuppressReportsHavingWeakLoopAssumption : public BugReporterVisitor {
48+ public:
49+ SuppressReportsHavingWeakLoopAssumption (const PathSensitiveBugReport &R)
50+ : OrigCursor(R.getErrorNode()) {}
51+
4552private:
4653 friend class BugReporterVisitor ;
54+
55+ // The exploded node we are currently visiting, but in the original exploded
56+ // graph. This graph is not trimmed, unlike the one we usually visit.
57+ const ExplodedNode *OrigCursor;
58+
4759 llvm::DenseSet<const Stmt *> LoopsSeen;
48- llvm::DenseMap<const Expr *, unsigned > EagerlyAssumedTrue ;
49- llvm::DenseMap<const Expr *, unsigned > EagerlyAssumedFalse ;
60+ llvm::DenseMap<const Expr *, unsigned > NumTimesTakenTrueDecision ;
61+ llvm::DenseMap<const Expr *, unsigned > NumTimesTakenFalseDecision ;
5062
5163 void Profile (llvm::FoldingSetNodeID &ID) const final {
5264 static const bool Tag = 0 ;
5365 ID.AddPointer (&Tag);
5466 }
5567
68+ static const ExplodedNode *selectMatchingPred (const ExplodedNode *OrigSucc,
69+ unsigned SoughtPredId) {
70+ auto MatchesSoughtId = [SoughtPredId](const ExplodedNode *N) {
71+ return N->getID () == SoughtPredId;
72+ };
73+ auto It = llvm::find_if (OrigSucc->preds (), MatchesSoughtId);
74+ assert (It != OrigSucc->pred_end ());
75+ return *It;
76+ }
77+
5678 static bool isLoopAndNonNull (const Stmt *S) {
5779 return isa_and_nonnull<ForStmt, WhileStmt, CXXForRangeStmt, DoStmt>(S);
5880 }
@@ -74,21 +96,33 @@ class SuppressReportsHavingWeakLoopAssumption : public BugReporterVisitor {
7496 PathDiagnosticPieceRef VisitNode (const ExplodedNode *Succ,
7597 BugReporterContext &BRC,
7698 PathSensitiveBugReport &BR) final {
99+ OrigCursor = selectMatchingPred (OrigCursor, Succ->getID ());
100+ assert (OrigCursor->getID () == Succ->getID ());
101+
77102 registerLoopStatements (Succ);
78103
79104 auto AsPostStmt = Succ->getLocationAs <PostStmt>();
80105 const Expr *CurrExpr =
81106 AsPostStmt ? dyn_cast<Expr>(AsPostStmt->getStmt ()) : nullptr ;
82107
83- const auto *Tag = Succ->getLocation ().getTag ();
84- if (!Tag)
108+ bool IsSplittingTwoWays = OrigCursor->succ_size () == 2 ;
109+
110+ if (!CurrExpr || !IsSplittingTwoWays)
85111 return nullptr ;
86112
87- StringRef TagDesc = Tag->getTagDescription ();
88- if (TagDesc == " ExprEngine : Eagerly Assume True" )
89- registerOccurrence (EagerlyAssumedTrue, CurrExpr);
90- if (TagDesc == " ExprEngine : Eagerly Assume False" )
91- registerOccurrence (EagerlyAssumedFalse, CurrExpr);
113+ const ExplodedNode *Next = Succ->getFirstSucc ();
114+ SValBuilder &SVB = Succ->getState ()->getStateManager ().getSValBuilder ();
115+ auto Query = SVB.evalCast (Succ->getSVal (CurrExpr), SVB.getConditionType (),
116+ CurrExpr->getType ())
117+ .getAs <DefinedOrUnknownSVal>();
118+ if (!Query)
119+ return nullptr ;
120+
121+ ProgramStateRef TakenTrueBranch = Next->getState ()->assume (*Query, true );
122+ registerOccurrence (TakenTrueBranch ? NumTimesTakenTrueDecision
123+ : NumTimesTakenFalseDecision,
124+ CurrExpr);
125+
92126 return nullptr ;
93127 }
94128
@@ -110,30 +144,26 @@ class SuppressReportsHavingWeakLoopAssumption : public BugReporterVisitor {
110144
111145 void trySuppressReport (PathSensitiveBugReport &R, const Stmt *Loop,
112146 const Expr *CondSubExpr) const {
113- auto NumEagerlyTrue = EagerlyAssumedTrue.lookup (CondSubExpr);
114- auto NumEagerlyFalse = EagerlyAssumedFalse.lookup (CondSubExpr);
147+ unsigned NumTimesTakenLoopBody =
148+ NumTimesTakenTrueDecision.lookup (CondSubExpr);
149+ unsigned NumTimesNotTakenLoopBody =
150+ NumTimesTakenFalseDecision.lookup (CondSubExpr);
151+
152+ // Adjust for do-while loops, where the loop body is always taken.
153+ if (isa<DoStmt>(Loop))
154+ NumTimesTakenLoopBody += 1 ;
115155
116156 // Suppress the report if it avoided a loop with an eager assumption.
117- if (NumEagerlyTrue == 0 && NumEagerlyFalse == 1 ) {
157+ if (NumTimesTakenLoopBody == 0 && NumTimesNotTakenLoopBody == 1 ) {
158+ // llvm::errs() << "eagerly decided never taking the loop body\n";
118159 R.markInvalid (" eagerly decided never taking the loop body" , CondSubExpr);
119160 return ;
120161 }
121162
122- // Account for do-while loops where the body is already "taken"
123- // unconditionally for the first round.
124- if (isa<DoStmt>(Loop)) {
125- // Suppress the report if we have taken the loop body for the second time
126- // with an eager assumption.
127- if (NumEagerlyTrue > 1 && NumEagerlyFalse == 0 ) {
128- R.markInvalid (" eagerly taken the do-while body at least 2 times" ,
129- CondSubExpr);
130- }
131- return ;
132- }
133-
134163 // Suppress the report if it iterates with eager assumptions 3 or more
135164 // times.
136- if (NumEagerlyTrue > 2 && NumEagerlyFalse == 0 ) {
165+ if (NumTimesTakenLoopBody > 2 && NumTimesNotTakenLoopBody == 0 ) {
166+ // llvm::errs() << "eagerly taken the loop body at least 2 times\n";
137167 R.markInvalid (" eagerly taken the loop body at least 2 times" ,
138168 CondSubExpr);
139169 }
@@ -854,7 +884,7 @@ void ArrayBoundCheckerV2::reportOOB(CheckerContext &C,
854884 if (Extent)
855885 markPartsInteresting (*BR, ErrorState, *Extent, IsTaintBug);
856886
857- BR->addVisitor <SuppressReportsHavingWeakLoopAssumption>();
887+ BR->addVisitor <SuppressReportsHavingWeakLoopAssumption>(*BR );
858888 C.emitReport (std::move (BR));
859889}
860890
0 commit comments