Author: DonĂ¡t Nagy Date: 2026-06-26T14:32:39+02:00 New Revision: 236f63474d40850a33fd4bcc6d8d63a31cebb8f1
URL: https://github.com/llvm/llvm-project/commit/236f63474d40850a33fd4bcc6d8d63a31cebb8f1 DIFF: https://github.com/llvm/llvm-project/commit/236f63474d40850a33fd4bcc6d8d63a31cebb8f1.diff LOG: [NFC][analyzer] Remove NodeBuilders around defaultEvalCall (#203923) This change removes `NodeBuilder`s from the functions connected to `defaultEvalCall` that were previously passing around `NodeBuilder` arguments instead of the more usual `ExplodedNodeSet &Dst` out-parameters. Although these `NodeBuilder`s "travelled through" many functions, their usage pattern was relatively simple and their back-and-forth set manipulation didn't provide any advantage over a plain exploded node set. In addition to the removal of the `NodeBuilder`s, this commit performs minor simplifications in the affected code and renames the old method `BifurcateCall` to the more specific `dynDispatchBifurcate` (because the old name was too vague now that we also have `ctuBifurcate`). Added: Modified: clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h clang/lib/StaticAnalyzer/Core/CheckerManager.cpp clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp Removed: ################################################################################ diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h index bb2de45cec92a..ce9b20185444e 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -772,7 +772,7 @@ class ExprEngine { const CallEvent &Call); /// Default implementation of call evaluation. - void defaultEvalCall(NodeBuilder &B, ExplodedNode *Pred, + void defaultEvalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred, const CallEvent &Call, const EvalCallOptions &CallOpts = {}); @@ -908,9 +908,9 @@ class ExprEngine { const StackFrame *SF); void inlineCall(WorkList *WList, const CallEvent &Call, const Decl *D, - NodeBuilder &Bldr, ExplodedNode *Pred, ProgramStateRef State); + ExplodedNode *Pred, ProgramStateRef State); - void ctuBifurcate(const CallEvent &Call, const Decl *D, NodeBuilder &Bldr, + void ctuBifurcate(const CallEvent &Call, const Decl *D, ExplodedNodeSet &Dst, ExplodedNode *Pred, ProgramStateRef State); /// Returns true if the CTU analysis is running its second phase. @@ -918,20 +918,20 @@ class ExprEngine { /// Conservatively evaluate call by invalidating regions and binding /// a conjured return value. - void conservativeEvalCall(const CallEvent &Call, NodeBuilder &Bldr, - ExplodedNode *Pred, ProgramStateRef State); + ExplodedNode *conservativeEvalCall(const CallEvent &Call, ExplodedNode *Pred, + ProgramStateRef State); /// Either inline or process the call conservatively (or both), based /// on DynamicDispatchBifurcation data. - void BifurcateCall(const MemRegion *BifurReg, - const CallEvent &Call, const Decl *D, NodeBuilder &Bldr, - ExplodedNode *Pred); + void dynDispatchBifurcate(const MemRegion *BifurReg, const CallEvent &Call, + const Decl *D, ExplodedNodeSet &Dst, + ExplodedNode *Pred); bool replayWithoutInlining(ExplodedNode *P, const StackFrame *CalleeSF); /// Models a trivial copy or move constructor or trivial assignment operator /// call with a simple bind. - void performTrivialCopy(NodeBuilder &Bldr, ExplodedNode *Pred, + void performTrivialCopy(ExplodedNodeSet &Dst, ExplodedNode *Pred, const CallEvent &Call); /// If the value of the given expression \p InitWithAdjustments is a NonLoc, diff --git a/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp b/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp index 80c03899d1e39..4db6b6ecaa9f7 100644 --- a/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -808,10 +808,8 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst, } // If none of the checkers evaluated the call, ask ExprEngine to handle it. - if (!evaluatorChecker) { - NodeBuilder B(Pred, Dst, Eng.getBuilderContext()); - Eng.defaultEvalCall(B, Pred, *UpdatedCall, CallOpts); - } + if (!evaluatorChecker) + Eng.defaultEvalCall(Dst, Pred, *UpdatedCall, CallOpts); } } diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index 291533d0c3289..e048fc210e608 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -42,9 +42,7 @@ void ExprEngine::CreateCXXTemporaryObject(const MaterializeTemporaryExpr *ME, Bldr.generateNode(ME, Pred, state); } -// FIXME: This is the sort of code that should eventually live in a Core -// checker rather than as a special case in ExprEngine. -void ExprEngine::performTrivialCopy(NodeBuilder &Bldr, ExplodedNode *Pred, +void ExprEngine::performTrivialCopy(ExplodedNodeSet &Dst, ExplodedNode *Pred, const CallEvent &Call) { SVal ThisVal; bool AlwaysReturnsLValue; @@ -67,8 +65,7 @@ void ExprEngine::performTrivialCopy(NodeBuilder &Bldr, ExplodedNode *Pred, const StackFrame *SF = Pred->getStackFrame(); const Expr *CallExpr = Call.getOriginExpr(); - ExplodedNodeSet Dst; - Bldr.takeNodes(Pred); + ExplodedNodeSet DstEval; assert(ThisRD); @@ -87,23 +84,22 @@ void ExprEngine::performTrivialCopy(NodeBuilder &Bldr, ExplodedNode *Pred, evalLocation(Tmp, CallExpr, VExpr, Pred, Pred->getState(), V, /*isLoad=*/true); for (ExplodedNode *N : Tmp) - evalBind(Dst, CallExpr, N, ThisVal, V, !AlwaysReturnsLValue); + evalBind(DstEval, CallExpr, N, ThisVal, V, !AlwaysReturnsLValue); } else { // We can't copy empty classes because of empty base class optimization. // In that case, copying the empty base class subobject would overwrite the // object that it overlaps with - so let's not do that. // See issue-157467.cpp for an example. - Dst.insert(Pred); + DstEval.insert(Pred); } - PostStmt PS(CallExpr, SF); - for (ExplodedNode *N : Dst) { + for (ExplodedNode *N : DstEval) { ProgramStateRef State = N->getState(); if (AlwaysReturnsLValue) State = State->BindExpr(CallExpr, SF, ThisVal); else State = bindReturnValue(Call, SF, State); - Bldr.generateNode(PS, State, N); + Dst.insert(Engine.makePostStmtNode(CallExpr, State, N)); } } @@ -729,10 +725,9 @@ void ExprEngine::handleConstructor(const Expr *E, if (CE && CE->getConstructor()->isTrivial() && CE->getConstructor()->isCopyOrMoveConstructor() && !CallOpts.IsArrayCtorOrDtor) { - NodeBuilder Bldr(DstPreCall, DstEvaluated, *currBldrCtx); // FIXME: Handle other kinds of trivial constructors as well. for (ExplodedNode *N : DstPreCall) - performTrivialCopy(Bldr, N, *Call); + performTrivialCopy(DstEvaluated, N, *Call); } else { for (ExplodedNode *N : DstPreCall) @@ -862,9 +857,8 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType, *Call, *this); ExplodedNodeSet DstInvalidated; - NodeBuilder Bldr(DstPreCall, DstInvalidated, *currBldrCtx); for (ExplodedNode *N : DstPreCall) - defaultEvalCall(Bldr, N, *Call, CallOpts); + defaultEvalCall(DstInvalidated, N, *Call, CallOpts); getCheckerManager().runCheckersForPostCall(Dst, DstInvalidated, *Call, *this); @@ -887,7 +881,6 @@ void ExprEngine::VisitCXXNewAllocatorCall(const CXXNewExpr *CNE, *Call, *this); ExplodedNodeSet DstPostCall; - NodeBuilder CallBldr(DstPreCall, DstPostCall, *currBldrCtx); for (ExplodedNode *I : DstPreCall) { // Operator new calls (CXXNewExpr) are intentionally not eval-called, // because it does not make sense to eval-call user-provided functions. @@ -897,7 +890,7 @@ void ExprEngine::VisitCXXNewAllocatorCall(const CXXNewExpr *CNE, // is what we want anyway. // So the best is to not allow eval-calling CXXNewExprs from checkers. // Checkers can provide their pre/post-call callbacks if needed. - defaultEvalCall(CallBldr, I, *Call); + defaultEvalCall(DstPostCall, I, *Call); } // If the call is inlined, DstPostCall will be empty and we bail out now. @@ -1095,13 +1088,12 @@ void ExprEngine::VisitCXXDeleteExpr(const CXXDeleteExpr *CDE, ExplodedNodeSet DstPostCall; if (AMgr.getAnalyzerOptions().MayInlineCXXAllocator) { - NodeBuilder Bldr(DstPreCall, DstPostCall, *currBldrCtx); for (ExplodedNode *I : DstPreCall) { // Intentionally either inline or conservative eval-call the operator // delete, but avoid triggering an eval-call event for checkers. // As detailed at handling CXXNewExprs, in short, because it does not // really make sense to eval-call user-provided functions. - defaultEvalCall(Bldr, I, *Call); + defaultEvalCall(DstPostCall, I, *Call); } } else { DstPostCall = std::move(DstPreCall); diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index 97c655c103b0a..b66b5c5e358e4 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -493,36 +493,33 @@ REGISTER_MAP_WITH_PROGRAMSTATE(DynamicDispatchBifurcationMap, REGISTER_TRAIT_WITH_PROGRAMSTATE(CTUDispatchBifurcation, bool) void ExprEngine::ctuBifurcate(const CallEvent &Call, const Decl *D, - NodeBuilder &Bldr, ExplodedNode *Pred, + ExplodedNodeSet &Dst, ExplodedNode *Pred, ProgramStateRef State) { - ProgramStateRef ConservativeEvalState = nullptr; if (Call.isForeign() && !isSecondPhaseCTU()) { const auto IK = AMgr.options.getCTUPhase1Inlining(); const bool DoInline = IK == CTUPhase1InliningKind::All || (IK == CTUPhase1InliningKind::Small && isSmall(AMgr.getAnalysisDeclContext(D))); if (DoInline) { - inlineCall(Engine.getWorkList(), Call, D, Bldr, Pred, State); + inlineCall(Engine.getWorkList(), Call, D, Pred, State); return; } const bool BState = State->get<CTUDispatchBifurcation>(); if (!BState) { // This is the first time we see this foreign function. // Enqueue it to be analyzed in the second (ctu) phase. - inlineCall(Engine.getCTUWorkList(), Call, D, Bldr, Pred, State); + inlineCall(Engine.getCTUWorkList(), Call, D, Pred, State); // Conservatively evaluate in the first phase. - ConservativeEvalState = State->set<CTUDispatchBifurcation>(true); - conservativeEvalCall(Call, Bldr, Pred, ConservativeEvalState); - } else { - conservativeEvalCall(Call, Bldr, Pred, State); + State = State->set<CTUDispatchBifurcation>(true); } + Dst.insert(conservativeEvalCall(Call, Pred, State)); return; } - inlineCall(Engine.getWorkList(), Call, D, Bldr, Pred, State); + inlineCall(Engine.getWorkList(), Call, D, Pred, State); } void ExprEngine::inlineCall(WorkList *WList, const CallEvent &Call, - const Decl *D, NodeBuilder &Bldr, - ExplodedNode *Pred, ProgramStateRef State) { + const Decl *D, ExplodedNode *Pred, + ProgramStateRef State) { assert(D); const StackFrame *CallerSF = Pred->getStackFrame(); @@ -556,10 +553,6 @@ void ExprEngine::inlineCall(WorkList *WList, const CallEvent &Call, WList->enqueue(N); } - // If we decided to inline the call, the successor has been manually - // added onto the work list so remove it from the node builder. - Bldr.takeNodes(Pred); - NumInlinedCalls++; Engine.FunctionSummaries->bumpNumTimesInlined(D); @@ -837,14 +830,15 @@ ProgramStateRef ExprEngine::bindReturnValue(const CallEvent &Call, // Conservatively evaluate call by invalidating regions and binding // a conjured return value. -void ExprEngine::conservativeEvalCall(const CallEvent &Call, NodeBuilder &Bldr, - ExplodedNode *Pred, ProgramStateRef State) { +ExplodedNode *ExprEngine::conservativeEvalCall(const CallEvent &Call, + ExplodedNode *Pred, + ProgramStateRef State) { State = Call.invalidateRegions(getNumVisitedCurrent(), State); State = bindReturnValue(Call, Pred->getStackFrame(), State); // And make the result node. static SimpleProgramPointTag PT("ExprEngine", "Conservative eval call"); - Bldr.generateNode(Call.getProgramPoint(false, &PT), State, Pred); + return Engine.makeNode(Call.getProgramPoint(false, &PT), State, Pred); } ExprEngine::CallInlinePolicy @@ -1219,7 +1213,7 @@ static bool isTrivialObjectAssignment(const CallEvent &Call) { return MD->isTrivial(); } -void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred, +void ExprEngine::defaultEvalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred, const CallEvent &Call, const EvalCallOptions &CallOpts) { // Make sure we have the most recent state attached to the call. @@ -1227,7 +1221,7 @@ void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred, // Special-case trivial assignment operators. if (isTrivialObjectAssignment(Call)) { - performTrivialCopy(Bldr, Pred, Call); + performTrivialCopy(Dst, Pred, Call); return; } @@ -1247,17 +1241,17 @@ void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred, // Explore with and without inlining the call. if (Options.getIPAMode() == IPAK_DynamicDispatchBifurcate) { - BifurcateCall(RD.getDispatchRegion(), Call, D, Bldr, Pred); + dynDispatchBifurcate(RD.getDispatchRegion(), Call, D, Dst, Pred); return; } // Don't inline if we're not in any dynamic dispatch mode. if (Options.getIPAMode() != IPAK_DynamicDispatch) { - conservativeEvalCall(Call, Bldr, Pred, State); + Dst.insert(conservativeEvalCall(Call, Pred, State)); return; } } - ctuBifurcate(Call, D, Bldr, Pred, State); + ctuBifurcate(Call, D, Dst, Pred, State); return; } } @@ -1268,12 +1262,13 @@ void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred, State, dyn_cast_or_null<CXXConstructExpr>(E), Call.getStackFrame()); // Also handle the return value and invalidate the regions. - conservativeEvalCall(Call, Bldr, Pred, State); + Dst.insert(conservativeEvalCall(Call, Pred, State)); } -void ExprEngine::BifurcateCall(const MemRegion *BifurReg, - const CallEvent &Call, const Decl *D, - NodeBuilder &Bldr, ExplodedNode *Pred) { +void ExprEngine::dynDispatchBifurcate(const MemRegion *BifurReg, + const CallEvent &Call, const Decl *D, + ExplodedNodeSet &Dst, + ExplodedNode *Pred) { assert(BifurReg); BifurReg = BifurReg->StripCasts(); @@ -1285,11 +1280,11 @@ void ExprEngine::BifurcateCall(const MemRegion *BifurReg, if (BState) { // If we are on "inline path", keep inlining if possible. if (*BState == DynamicDispatchModeInlined) - ctuBifurcate(Call, D, Bldr, Pred, State); + ctuBifurcate(Call, D, Dst, Pred, State); // If inline failed, or we are on the path where we assume we // don't have enough info about the receiver to inline, conjure the // return value and invalidate the regions. - conservativeEvalCall(Call, Bldr, Pred, State); + Dst.insert(conservativeEvalCall(Call, Pred, State)); return; } @@ -1298,12 +1293,12 @@ void ExprEngine::BifurcateCall(const MemRegion *BifurReg, ProgramStateRef IState = State->set<DynamicDispatchBifurcationMap>(BifurReg, DynamicDispatchModeInlined); - ctuBifurcate(Call, D, Bldr, Pred, IState); + ctuBifurcate(Call, D, Dst, Pred, IState); ProgramStateRef NoIState = State->set<DynamicDispatchBifurcationMap>(BifurReg, DynamicDispatchModeConservative); - conservativeEvalCall(Call, Bldr, Pred, NoIState); + Dst.insert(conservativeEvalCall(Call, Pred, NoIState)); NumOfDynamicDispatchPathSplits++; } diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp index 15f0275691e92..26b8ca3e1f2bc 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp @@ -256,36 +256,21 @@ void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME, // Proceed with evaluate the message expression. ExplodedNodeSet dstEval; - NodeBuilder Bldr(dstGenericPrevisit, dstEval, *currBldrCtx); - for (ExplodedNodeSet::iterator DI = dstGenericPrevisit.begin(), - DE = dstGenericPrevisit.end(); DI != DE; ++DI) { - ExplodedNode *Pred = *DI; + for (ExplodedNode *Pred : dstGenericPrevisit) { ProgramStateRef State = Pred->getState(); CallEventRef<ObjCMethodCall> UpdatedMsg = Msg.cloneWithState(State); - if (UpdatedMsg->isInstanceMessage()) { - SVal recVal = UpdatedMsg->getReceiverSVal(); - if (!recVal.isUndef()) { - if (ObjCNoRet.isImplicitNoReturn(ME)) { - // If we raise an exception, for now treat it as a sink. - // Eventually we will want to handle exceptions properly. - Bldr.generateSink(ME, Pred, State); - continue; - } - } - } else { - // Check for special class methods that are known to not return - // and that we should treat as a sink. - if (ObjCNoRet.isImplicitNoReturn(ME)) { - // If we raise an exception, for now treat it as a sink. - // Eventually we will want to handle exceptions properly. - Bldr.generateSink(ME, Pred, Pred->getState()); - continue; - } + if (ObjCNoRet.isImplicitNoReturn(ME) && + !(UpdatedMsg->isInstanceMessage() && + UpdatedMsg->getReceiverSVal().isUndef())) { + // If we raise an exception, for now treat it as a sink. + // Eventually we will want to handle exceptions properly. + Engine.makePostStmtNode(ME, State, Pred, /*MarkAsSink=*/true); + continue; } - defaultEvalCall(Bldr, Pred, *UpdatedMsg); + defaultEvalCall(dstEval, Pred, *UpdatedMsg); } // If there were constructors called for object-type arguments, clean them up. _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
