[PATCH] D85796: [Analysis] Bug fix for exploded graph branching in evalCall for constructor

Artem Dergachev via Phabricator via cfe-commits cfe-commits at lists.llvm.org
Sat Aug 15 14:52:54 PDT 2020


NoQ added inline comments.


================
Comment at: clang/lib/StaticAnalyzer/Core/CheckerManager.cpp:682
         anyEvaluated = true;
+        Dst.clear();
         Dst.insert(checkDst);
----------------
vrnithinkumar wrote:
> > runCheckersForEvalCall() already has its own builder, you don't need another.
> 
> In that case is it okay to clear the  `ExplodedNodeSet DstEvaluated` passed as `Dst` which contains parent node [[ https://github.com/llvm/llvm-project/blob/master/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp#L604 | contains parent node ]] ?
> 
> When the any of the evaluated checker is evaluated the node successfully we can clear the `Dst` which is part of the `StmtNodeBuilder Bldr(DstPreCall, DstEvaluated, *currBldrCtx);` before inserting new nodes.
Hmm actually your code is now incorrect because `runCheckersForEvalCall()` is in fact invoked multiple times in a loop (if there were state splits in PreStmt/PreCall), therefore it's possible that `Dst` does in fact already contain nodes.

That also means that i can't put in the assertions that i thought of; the destination set is indeed potentially non-empty in many cases.


Anyway, here's what i meant:
```lang=diff
   ExplodedNodeSet DstPreCall;
   getCheckerManager().runCheckersForPreCall(DstPreCall, PreInitialized,
                                             *Call, *this);

   ExplodedNodeSet DstEvaluated;
-  StmtNodeBuilder Bldr(DstPreCall, DstEvaluated, *currBldrCtx);

   if (CE && CE->getConstructor()->isTrivial() &&
       CE->getConstructor()->isCopyOrMoveConstructor() &&
       !CallOpts.IsArrayCtorOrDtor) {
+    StmtNodeBuilder Bldr(DstPreCall, DstEvaluated, *currBldrCtx);
     // FIXME: Handle other kinds of trivial constructors as well.
     for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end();
          I != E; ++I)
       performTrivialCopy(Bldr, *I, *Call);

   } else {
     for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end();
          I != E; ++I)
       getCheckerManager().runCheckersForEvalCall(DstEvaluated, *I, *Call, *this,
                                                  CallOpts);
   }

   // If the CFG was constructed without elements for temporary destructors
   // and the just-called constructor created a temporary object then
   // stop exploration if the temporary object has a noreturn constructor.
   // This can lose coverage because the destructor, if it were present
   // in the CFG, would be called at the end of the full expression or
   // later (for life-time extended temporaries) -- but avoids infeasible
   // paths when no-return temporary destructors are used for assertions.
+  ExplodedNodeSet DstEvaluatedPostProcessed;
+  StmtNodeBuilder Bldr(DstEvaluated, DstEvaluatedPostProcessed, *currBldrCtx);
   const AnalysisDeclContext *ADC = LCtx->getAnalysisDeclContext();
   if (!ADC->getCFGBuildOptions().AddTemporaryDtors) {
     if (llvm::isa_and_nonnull<CXXTempObjectRegion>(TargetRegion) &&
         cast<CXXConstructorDecl>(Call->getDecl())
             ->getParent()
             ->isAnyDestructorNoReturn()) {

       // If we've inlined the constructor, then DstEvaluated would be empty.
       // In this case we still want a sink, which could be implemented
       // in processCallExit. But we don't have that implemented at the moment,
       // so if you hit this assertion, see if you can avoid inlining
       // the respective constructor when analyzer-config cfg-temporary-dtors
       // is set to false.
       // Otherwise there's nothing wrong with inlining such constructor.
       assert(!DstEvaluated.empty() &&
              "We should not have inlined this constructor!");

       for (ExplodedNode *N : DstEvaluated) {
         Bldr.generateSink(E, N, N->getState());
       }

       // There is no need to run the PostCall and PostStmt checker
       // callbacks because we just generated sinks on all nodes in th
       // frontier.
       return;
     }
   }

   ExplodedNodeSet DstPostArgumentCleanup;
-  for (ExplodedNode *I : DstEvaluated)
+  for (ExplodedNode *I : DstEvaluatedPostProcessed)
     finishArgumentConstruction(DstPostArgumentCleanup, I, *Call);
```
This way exactly one builder exists at any given moment of time and exactly one builder operates on each pair of source-destination sets.

Also this entire `AddTemporaryDtors` option could probably be banned already which would make things a whole lot easier; i'll look into that.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D85796/new/

https://reviews.llvm.org/D85796



More information about the cfe-commits mailing list