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

Nithin VR via Phabricator via cfe-commits cfe-commits at lists.llvm.org
Mon Aug 17 05:07:55 PDT 2020


vrnithinkumar marked an inline comment as done.
vrnithinkumar added inline comments.


================
Comment at: clang/lib/StaticAnalyzer/Core/CheckerManager.cpp:682
         anyEvaluated = true;
+        Dst.clear();
         Dst.insert(checkDst);
----------------
NoQ wrote:
> 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.
> 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.
Oh, I missed that point.

Made the above suggested changes to make sure exactly one `NodeBuilder` exists at any given time.


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