diff options
Diffstat (limited to 'clang/lib/StaticAnalyzer')
11 files changed, 68 insertions, 50 deletions
diff --git a/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp index bf35bee..3ddd659 100644 --- a/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp @@ -104,7 +104,7 @@ class RAIIMutexDescriptor { // this function is called instead of early returning it. To avoid this, a // bool variable (IdentifierInfoInitialized) is used and the function will // be run only once. - const auto &ASTCtx = Call.getState()->getStateManager().getContext(); + const auto &ASTCtx = Call.getASTContext(); Guard = &ASTCtx.Idents.get(GuardName); } } diff --git a/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp index 9d3aeff..2420848 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp @@ -929,7 +929,7 @@ ObjCDeallocChecker::getValueReleasedByNillingOut(const ObjCMethodCall &M, SVal Arg = M.getArgSVal(0); ProgramStateRef notNilState, nilState; std::tie(notNilState, nilState) = - M.getState()->assume(Arg.castAs<DefinedOrUnknownSVal>()); + C.getState()->assume(Arg.castAs<DefinedOrUnknownSVal>()); if (!(nilState && !notNilState)) return nullptr; diff --git a/clang/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp index f984caf..227cbfa 100644 --- a/clang/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp @@ -34,7 +34,7 @@ class ObjCSuperDeallocChecker this, "[super dealloc] should not be called more than once", categories::CoreFoundationObjectiveC}; - void initIdentifierInfoAndSelectors(ASTContext &Ctx) const; + void initIdentifierInfoAndSelectors(const ASTContext &Ctx) const; bool isSuperDeallocMessage(const ObjCMethodCall &M) const; @@ -214,8 +214,8 @@ void ObjCSuperDeallocChecker::diagnoseCallArguments(const CallEvent &CE, } } -void -ObjCSuperDeallocChecker::initIdentifierInfoAndSelectors(ASTContext &Ctx) const { +void ObjCSuperDeallocChecker::initIdentifierInfoAndSelectors( + const ASTContext &Ctx) const { if (IIdealloc) return; @@ -230,7 +230,7 @@ ObjCSuperDeallocChecker::isSuperDeallocMessage(const ObjCMethodCall &M) const { if (M.getOriginExpr()->getReceiverKind() != ObjCMessageExpr::SuperInstance) return false; - ASTContext &Ctx = M.getState()->getStateManager().getContext(); + const ASTContext &Ctx = M.getASTContext(); initIdentifierInfoAndSelectors(Ctx); return M.getSelector() == SELdealloc; diff --git a/clang/lib/StaticAnalyzer/Checkers/StdVariantChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdVariantChecker.cpp index 4fc1c57..db8bbee 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StdVariantChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StdVariantChecker.cpp @@ -211,13 +211,13 @@ private: if (!DefaultType) return; - ProgramStateRef State = ConstructorCall->getState(); + ProgramStateRef State = C.getState(); State = State->set<VariantHeldTypeMap>(ThisMemRegion, *DefaultType); C.addTransition(State); } bool handleStdGetCall(const CallEvent &Call, CheckerContext &C) const { - ProgramStateRef State = Call.getState(); + ProgramStateRef State = C.getState(); const auto &ArgType = Call.getArgSVal(0) .getType(C.getASTContext()) diff --git a/clang/lib/StaticAnalyzer/Checkers/TaggedUnionModeling.h b/clang/lib/StaticAnalyzer/Checkers/TaggedUnionModeling.h index dec4612..b8fb572 100644 --- a/clang/lib/StaticAnalyzer/Checkers/TaggedUnionModeling.h +++ b/clang/lib/StaticAnalyzer/Checkers/TaggedUnionModeling.h @@ -52,7 +52,7 @@ removeInformationStoredForDeadInstances(const CallEvent &Call, template <class TypeMap> void handleConstructorAndAssignment(const CallEvent &Call, CheckerContext &C, SVal ThisSVal) { - ProgramStateRef State = Call.getState(); + ProgramStateRef State = C.getState(); if (!State) return; diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp index 419d263..84adbf3 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp @@ -173,6 +173,14 @@ bool tryToFindPtrOrigin( if (isSingleton(E->getFoundDecl())) return callback(E, true); } + + if (auto *MemberExpr = dyn_cast<CXXDependentScopeMemberExpr>(CalleeE)) { + auto *Base = MemberExpr->getBase(); + auto MemberName = MemberExpr->getMember().getAsString(); + bool IsGetter = MemberName == "get" || MemberName == "ptr"; + if (Base && isSafePtrType(Base->getType()) && IsGetter) + return callback(E, true); + } } // Sometimes, canonical type erroneously turns Ref<T> into T. diff --git a/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp b/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp index 02f34bc..c905ee6 100644 --- a/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp +++ b/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp @@ -173,7 +173,7 @@ const PointerToMemberData *BasicValueFactory::getPointerToMemberData( return D; } -LLVM_ATTRIBUTE_UNUSED static bool hasNoRepeatedElements( +[[maybe_unused]] static bool hasNoRepeatedElements( llvm::ImmutableList<const CXXBaseSpecifier *> BaseSpecList) { llvm::SmallPtrSet<QualType, 16> BaseSpecSeen; for (const CXXBaseSpecifier *BaseSpec : BaseSpecList) { diff --git a/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp b/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp index 44c6f9f..8ee4832 100644 --- a/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -731,19 +731,22 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst, ExplodedNodeSet checkDst; NodeBuilder B(Pred, checkDst, Eng.getBuilderContext()); + ProgramStateRef State = Pred->getState(); + CallEventRef<> UpdatedCall = Call.cloneWithState(State); + // Check if any of the EvalCall callbacks can evaluate the call. for (const auto &EvalCallChecker : EvalCallCheckers) { // TODO: Support the situation when the call doesn't correspond // to any Expr. ProgramPoint L = ProgramPoint::getProgramPoint( - Call.getOriginExpr(), ProgramPoint::PostStmtKind, + UpdatedCall->getOriginExpr(), ProgramPoint::PostStmtKind, Pred->getLocationContext(), EvalCallChecker.Checker); bool evaluated = false; - { // CheckerContext generates transitions(populates checkDest) on + { // CheckerContext generates transitions (populates checkDest) on // destruction, so introduce the scope to make sure it gets properly // populated. CheckerContext C(B, Eng, Pred, L); - evaluated = EvalCallChecker(Call, C); + evaluated = EvalCallChecker(*UpdatedCall, C); } #ifndef NDEBUG if (evaluated && evaluatorChecker) { @@ -774,7 +777,7 @@ 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, Call, CallOpts); + Eng.defaultEvalCall(B, Pred, *UpdatedCall, CallOpts); } } } diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index 0c491b8..ac6c1d7 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -628,6 +628,8 @@ void ExprEngine::VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred, ProgramStateRef ExprEngine::finishArgumentConstruction(ProgramStateRef State, const CallEvent &Call) { + // WARNING: The state attached to 'Call' may be obsolete, do not call any + // methods that rely on it! const Expr *E = Call.getOriginExpr(); // FIXME: Constructors to placement arguments of operator new // are not supported yet. @@ -653,6 +655,8 @@ ProgramStateRef ExprEngine::finishArgumentConstruction(ProgramStateRef State, void ExprEngine::finishArgumentConstruction(ExplodedNodeSet &Dst, ExplodedNode *Pred, const CallEvent &Call) { + // WARNING: The state attached to 'Call' may be obsolete, do not call any + // methods that rely on it! ProgramStateRef State = Pred->getState(); ProgramStateRef CleanedState = finishArgumentConstruction(State, Call); if (CleanedState == State) { @@ -670,35 +674,33 @@ void ExprEngine::finishArgumentConstruction(ExplodedNodeSet &Dst, } void ExprEngine::evalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred, - const CallEvent &Call) { - // WARNING: At this time, the state attached to 'Call' may be older than the - // state in 'Pred'. This is a minor optimization since CheckerManager will - // use an updated CallEvent instance when calling checkers, but if 'Call' is - // ever used directly in this function all callers should be updated to pass - // the most recent state. (It is probably not worth doing the work here since - // for some callers this will not be necessary.) + const CallEvent &CallTemplate) { + // NOTE: CallTemplate is called a "template" because its attached state may + // be obsolete (compared to the state of Pred). The state-dependent methods + // of CallEvent should be used only after a `cloneWithState` call that + // attaches the up-to-date state to this template object. // Run any pre-call checks using the generic call interface. ExplodedNodeSet dstPreVisit; - getCheckerManager().runCheckersForPreCall(dstPreVisit, Pred, - Call, *this); + getCheckerManager().runCheckersForPreCall(dstPreVisit, Pred, CallTemplate, + *this); // Actually evaluate the function call. We try each of the checkers // to see if the can evaluate the function call, and get a callback at // defaultEvalCall if all of them fail. ExplodedNodeSet dstCallEvaluated; - getCheckerManager().runCheckersForEvalCall(dstCallEvaluated, dstPreVisit, - Call, *this, EvalCallOptions()); + getCheckerManager().runCheckersForEvalCall( + dstCallEvaluated, dstPreVisit, CallTemplate, *this, EvalCallOptions()); // If there were other constructors called for object-type arguments // of this call, clean them up. ExplodedNodeSet dstArgumentCleanup; for (ExplodedNode *I : dstCallEvaluated) - finishArgumentConstruction(dstArgumentCleanup, I, Call); + finishArgumentConstruction(dstArgumentCleanup, I, CallTemplate); ExplodedNodeSet dstPostCall; getCheckerManager().runCheckersForPostCall(dstPostCall, dstArgumentCleanup, - Call, *this); + CallTemplate, *this); // Escaping symbols conjured during invalidating the regions above. // Note that, for inlined calls the nodes were put back into the worklist, @@ -708,12 +710,13 @@ void ExprEngine::evalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred, // Run pointerEscape callback with the newly conjured symbols. SmallVector<std::pair<SVal, SVal>, 8> Escaped; for (ExplodedNode *I : dstPostCall) { - NodeBuilder B(I, Dst, *currBldrCtx); ProgramStateRef State = I->getState(); + CallEventRef<> Call = CallTemplate.cloneWithState(State); + NodeBuilder B(I, Dst, *currBldrCtx); Escaped.clear(); { unsigned Arg = -1; - for (const ParmVarDecl *PVD : Call.parameters()) { + for (const ParmVarDecl *PVD : Call->parameters()) { ++Arg; QualType ParamTy = PVD->getType(); if (ParamTy.isNull() || @@ -722,13 +725,13 @@ void ExprEngine::evalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred, QualType Pointee = ParamTy->getPointeeType(); if (Pointee.isConstQualified() || Pointee->isVoidType()) continue; - if (const MemRegion *MR = Call.getArgSVal(Arg).getAsRegion()) + if (const MemRegion *MR = Call->getArgSVal(Arg).getAsRegion()) Escaped.emplace_back(loc::MemRegionVal(MR), State->getSVal(MR, Pointee)); } } State = processPointerEscapedOnBind(State, Escaped, I->getLocationContext(), - PSK_EscapeOutParameters, &Call); + PSK_EscapeOutParameters, &*Call); if (State == I->getState()) Dst.insert(I); @@ -1212,48 +1215,47 @@ static bool isTrivialObjectAssignment(const CallEvent &Call) { } void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred, - const CallEvent &CallTemplate, + const CallEvent &Call, const EvalCallOptions &CallOpts) { // Make sure we have the most recent state attached to the call. ProgramStateRef State = Pred->getState(); - CallEventRef<> Call = CallTemplate.cloneWithState(State); // Special-case trivial assignment operators. - if (isTrivialObjectAssignment(*Call)) { - performTrivialCopy(Bldr, Pred, *Call); + if (isTrivialObjectAssignment(Call)) { + performTrivialCopy(Bldr, Pred, Call); return; } // Try to inline the call. // The origin expression here is just used as a kind of checksum; // this should still be safe even for CallEvents that don't come from exprs. - const Expr *E = Call->getOriginExpr(); + const Expr *E = Call.getOriginExpr(); ProgramStateRef InlinedFailedState = getInlineFailedState(State, E); if (InlinedFailedState) { // If we already tried once and failed, make sure we don't retry later. State = InlinedFailedState; } else { - RuntimeDefinition RD = Call->getRuntimeDefinition(); - Call->setForeign(RD.isForeign()); + RuntimeDefinition RD = Call.getRuntimeDefinition(); + Call.setForeign(RD.isForeign()); const Decl *D = RD.getDecl(); - if (shouldInlineCall(*Call, D, Pred, CallOpts)) { + if (shouldInlineCall(Call, D, Pred, CallOpts)) { if (RD.mayHaveOtherDefinitions()) { AnalyzerOptions &Options = getAnalysisManager().options; // Explore with and without inlining the call. if (Options.getIPAMode() == IPAK_DynamicDispatchBifurcate) { - BifurcateCall(RD.getDispatchRegion(), *Call, D, Bldr, Pred); + BifurcateCall(RD.getDispatchRegion(), Call, D, Bldr, Pred); return; } // Don't inline if we're not in any dynamic dispatch mode. if (Options.getIPAMode() != IPAK_DynamicDispatch) { - conservativeEvalCall(*Call, Bldr, Pred, State); + conservativeEvalCall(Call, Bldr, Pred, State); return; } } - ctuBifurcate(*Call, D, Bldr, Pred, State); + ctuBifurcate(Call, D, Bldr, Pred, State); return; } } @@ -1261,10 +1263,10 @@ void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred, // If we can't inline it, clean up the state traits used only if the function // is inlined. State = removeStateTraitsUsedForArrayEvaluation( - State, dyn_cast_or_null<CXXConstructExpr>(E), Call->getLocationContext()); + State, dyn_cast_or_null<CXXConstructExpr>(E), Call.getLocationContext()); // Also handle the return value and invalidate the regions. - conservativeEvalCall(*Call, Bldr, Pred, State); + conservativeEvalCall(Call, Bldr, Pred, State); } void ExprEngine::BifurcateCall(const MemRegion *BifurReg, diff --git a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index ab45e67..245a730 100644 --- a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -983,7 +983,7 @@ public: } /// Check equivalence data for consistency. - [[nodiscard]] LLVM_ATTRIBUTE_UNUSED static bool + [[nodiscard]] [[maybe_unused]] static bool isClassDataConsistent(ProgramStateRef State); [[nodiscard]] QualType getType() const { @@ -1041,8 +1041,7 @@ private: // Constraint functions //===----------------------------------------------------------------------===// -[[nodiscard]] LLVM_ATTRIBUTE_UNUSED bool -areFeasible(ConstraintRangeTy Constraints) { +[[nodiscard]] [[maybe_unused]] bool areFeasible(ConstraintRangeTy Constraints) { return llvm::none_of( Constraints, [](const std::pair<EquivalenceClass, RangeSet> &ClassConstraint) { @@ -1134,7 +1133,7 @@ template <class EndTy> return End; } -[[nodiscard]] LLVM_ATTRIBUTE_UNUSED inline std::optional<RangeSet> +[[nodiscard]] [[maybe_unused]] inline std::optional<RangeSet> intersect(RangeSet::Factory &F, const RangeSet *End) { // This is an extraneous conversion from a raw pointer into // std::optional<RangeSet> diff --git a/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp index 4efde59..f6a3e79 100644 --- a/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp +++ b/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp @@ -62,7 +62,9 @@ ALWAYS_ENABLED_STATISTIC( "The # of visited basic blocks in the analyzed functions."); ALWAYS_ENABLED_STATISTIC(PercentReachableBlocks, "The % of reachable basic blocks."); -STAT_MAX(MaxCFGSize, "The maximum number of basic blocks in a function."); +ALWAYS_ENABLED_STATISTIC(MaxCFGSize, + "The maximum number of basic blocks in a function."); +static UnsignedEPStat CFGSize("CFGSize"); //===----------------------------------------------------------------------===// // AnalysisConsumer declaration. //===----------------------------------------------------------------------===// @@ -783,15 +785,19 @@ void AnalysisConsumer::HandleCode(Decl *D, AnalysisMode Mode, void AnalysisConsumer::RunPathSensitiveChecks(Decl *D, ExprEngine::InliningModes IMode, SetOfConstDecls *VisitedCallees) { + auto *CFG = Mgr->getCFG(D); + // Construct the analysis engine. First check if the CFG is valid. // FIXME: Inter-procedural analysis will need to handle invalid CFGs. - if (!Mgr->getCFG(D)) + if (!CFG) return; // See if the LiveVariables analysis scales. if (!Mgr->getAnalysisDeclContext(D)->getAnalysis<RelaxedLiveVariables>()) return; + CFGSize.set(CFG->size()); + ExprEngine Eng(CTU, *Mgr, VisitedCallees, &FunctionSummaries, IMode); // Execute the worklist algorithm. |