diff options
Diffstat (limited to 'clang/lib/StaticAnalyzer/Core')
30 files changed, 1658 insertions, 1063 deletions
diff --git a/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp b/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp index 009cbd4559b5..86ef4a568665 100644 --- a/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp +++ b/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp @@ -23,6 +23,7 @@ #include "llvm/Support/raw_ostream.h" #include <cassert> #include <cstddef> +#include <optional> #include <utility> #include <vector> @@ -64,45 +65,44 @@ void AnalyzerOptions::printFormattedEntry( ExplorationStrategyKind AnalyzerOptions::getExplorationStrategy() const { auto K = - llvm::StringSwitch<llvm::Optional<ExplorationStrategyKind>>( - ExplorationStrategy) + llvm::StringSwitch<std::optional<ExplorationStrategyKind>>( + ExplorationStrategy) .Case("dfs", ExplorationStrategyKind::DFS) .Case("bfs", ExplorationStrategyKind::BFS) - .Case("unexplored_first", - ExplorationStrategyKind::UnexploredFirst) + .Case("unexplored_first", ExplorationStrategyKind::UnexploredFirst) .Case("unexplored_first_queue", ExplorationStrategyKind::UnexploredFirstQueue) .Case("unexplored_first_location_queue", ExplorationStrategyKind::UnexploredFirstLocationQueue) .Case("bfs_block_dfs_contents", ExplorationStrategyKind::BFSBlockDFSContents) - .Default(None); + .Default(std::nullopt); assert(K && "User mode is invalid."); - return K.value(); + return *K; } CTUPhase1InliningKind AnalyzerOptions::getCTUPhase1Inlining() const { - auto K = llvm::StringSwitch<llvm::Optional<CTUPhase1InliningKind>>( + auto K = llvm::StringSwitch<std::optional<CTUPhase1InliningKind>>( CTUPhase1InliningMode) .Case("none", CTUPhase1InliningKind::None) .Case("small", CTUPhase1InliningKind::Small) .Case("all", CTUPhase1InliningKind::All) - .Default(None); + .Default(std::nullopt); assert(K && "CTU inlining mode is invalid."); - return K.value(); + return *K; } IPAKind AnalyzerOptions::getIPAMode() const { - auto K = llvm::StringSwitch<llvm::Optional<IPAKind>>(IPAMode) - .Case("none", IPAK_None) - .Case("basic-inlining", IPAK_BasicInlining) - .Case("inlining", IPAK_Inlining) - .Case("dynamic", IPAK_DynamicDispatch) - .Case("dynamic-bifurcate", IPAK_DynamicDispatchBifurcate) - .Default(None); + auto K = llvm::StringSwitch<std::optional<IPAKind>>(IPAMode) + .Case("none", IPAK_None) + .Case("basic-inlining", IPAK_BasicInlining) + .Case("inlining", IPAK_Inlining) + .Case("dynamic", IPAK_DynamicDispatch) + .Case("dynamic-bifurcate", IPAK_DynamicDispatchBifurcate) + .Default(std::nullopt); assert(K && "IPA Mode is invalid."); - return K.value(); + return *K; } bool @@ -111,14 +111,13 @@ AnalyzerOptions::mayInlineCXXMemberFunction( if (getIPAMode() < IPAK_Inlining) return false; - auto K = - llvm::StringSwitch<llvm::Optional<CXXInlineableMemberKind>>( - CXXMemberInliningMode) - .Case("constructors", CIMK_Constructors) - .Case("destructors", CIMK_Destructors) - .Case("methods", CIMK_MemberFunctions) - .Case("none", CIMK_None) - .Default(None); + auto K = llvm::StringSwitch<std::optional<CXXInlineableMemberKind>>( + CXXMemberInliningMode) + .Case("constructors", CIMK_Constructors) + .Case("destructors", CIMK_Destructors) + .Case("methods", CIMK_MemberFunctions) + .Case("none", CIMK_None) + .Default(std::nullopt); assert(K && "Invalid c++ member function inlining mode."); @@ -162,12 +161,12 @@ StringRef AnalyzerOptions::getCheckerStringOption(const ento::CheckerBase *C, bool AnalyzerOptions::getCheckerBooleanOption(StringRef CheckerName, StringRef OptionName, bool SearchInParents) const { - auto Ret = llvm::StringSwitch<llvm::Optional<bool>>( - getCheckerStringOption(CheckerName, OptionName, - SearchInParents)) - .Case("true", true) - .Case("false", false) - .Default(None); + auto Ret = + llvm::StringSwitch<std::optional<bool>>( + getCheckerStringOption(CheckerName, OptionName, SearchInParents)) + .Case("true", true) + .Case("false", false) + .Default(std::nullopt); assert(Ret && "This option should be either 'true' or 'false', and should've been " diff --git a/clang/lib/StaticAnalyzer/Core/BugReporter.cpp b/clang/lib/StaticAnalyzer/Core/BugReporter.cpp index 4d6b82e63f6a..a7f149b87e79 100644 --- a/clang/lib/StaticAnalyzer/Core/BugReporter.cpp +++ b/clang/lib/StaticAnalyzer/Core/BugReporter.cpp @@ -46,8 +46,6 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/FoldingSet.h" -#include "llvm/ADT/None.h" -#include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallString.h" @@ -66,6 +64,7 @@ #include <cstddef> #include <iterator> #include <memory> +#include <optional> #include <queue> #include <string> #include <tuple> @@ -221,8 +220,8 @@ class PathDiagnosticBuilder : public BugReporterContext { public: /// Find a non-invalidated report for a given equivalence class, and returns /// a PathDiagnosticBuilder able to construct bug reports for different - /// consumers. Returns None if no valid report is found. - static Optional<PathDiagnosticBuilder> + /// consumers. Returns std::nullopt if no valid report is found. + static std::optional<PathDiagnosticBuilder> findValidReport(ArrayRef<PathSensitiveBugReport *> &bugReports, PathSensitiveBugReporter &Reporter); @@ -309,7 +308,7 @@ std::string StackHintGeneratorForSymbol::getMessage(const ExplodedNode *N){ } // Check if the parameter is a pointer to the symbol. - if (Optional<loc::MemRegionVal> Reg = SV.getAs<loc::MemRegionVal>()) { + if (std::optional<loc::MemRegionVal> Reg = SV.getAs<loc::MemRegionVal>()) { // Do not attempt to dereference void*. if ((*I)->getType()->isVoidPointerType()) continue; @@ -1033,7 +1032,7 @@ static bool isContainedByStmt(const ParentMap &PM, const Stmt *S, static const Stmt *getStmtBeforeCond(const ParentMap &PM, const Stmt *Term, const ExplodedNode *N) { while (N) { - Optional<StmtPoint> SP = N->getLocation().getAs<StmtPoint>(); + std::optional<StmtPoint> SP = N->getLocation().getAs<StmtPoint>(); if (SP) { const Stmt *S = SP->getStmt(); if (!isContainedByStmt(PM, Term, S)) @@ -1194,7 +1193,7 @@ void PathDiagnosticBuilder::generatePathDiagnosticsForNode( "location context associated with the active path!"); // Have we encountered an exit from a function call? - if (Optional<CallExitEnd> CE = P.getAs<CallExitEnd>()) { + if (std::optional<CallExitEnd> CE = P.getAs<CallExitEnd>()) { // We are descending into a call (backwards). Construct // a new call piece to contain the path pieces for that call. @@ -1566,21 +1565,22 @@ static void simplifySimpleBranches(PathPieces &pieces) { /// Returns the number of bytes in the given (character-based) SourceRange. /// -/// If the locations in the range are not on the same line, returns None. +/// If the locations in the range are not on the same line, returns +/// std::nullopt. /// /// Note that this does not do a precise user-visible character or column count. -static Optional<size_t> getLengthOnSingleLine(const SourceManager &SM, - SourceRange Range) { +static std::optional<size_t> getLengthOnSingleLine(const SourceManager &SM, + SourceRange Range) { SourceRange ExpansionRange(SM.getExpansionLoc(Range.getBegin()), SM.getExpansionRange(Range.getEnd()).getEnd()); FileID FID = SM.getFileID(ExpansionRange.getBegin()); if (FID != SM.getFileID(ExpansionRange.getEnd())) - return None; + return std::nullopt; - Optional<MemoryBufferRef> Buffer = SM.getBufferOrNone(FID); + std::optional<MemoryBufferRef> Buffer = SM.getBufferOrNone(FID); if (!Buffer) - return None; + return std::nullopt; unsigned BeginOffset = SM.getFileOffset(ExpansionRange.getBegin()); unsigned EndOffset = SM.getFileOffset(ExpansionRange.getEnd()); @@ -1591,15 +1591,15 @@ static Optional<size_t> getLengthOnSingleLine(const SourceManager &SM, // SourceRange is covering a large or small amount of space in the user's // editor. if (Snippet.find_first_of("\r\n") != StringRef::npos) - return None; + return std::nullopt; // This isn't Unicode-aware, but it doesn't need to be. return Snippet.size(); } /// \sa getLengthOnSingleLine(SourceManager, SourceRange) -static Optional<size_t> getLengthOnSingleLine(const SourceManager &SM, - const Stmt *S) { +static std::optional<size_t> getLengthOnSingleLine(const SourceManager &SM, + const Stmt *S) { return getLengthOnSingleLine(SM, S->getSourceRange()); } @@ -1658,9 +1658,9 @@ static void removeContextCycles(PathPieces &Path, const SourceManager &SM) { if (s1Start && s2Start && s1Start == s2End && s2Start == s1End) { const size_t MAX_SHORT_LINE_LENGTH = 80; - Optional<size_t> s1Length = getLengthOnSingleLine(SM, s1Start); + std::optional<size_t> s1Length = getLengthOnSingleLine(SM, s1Start); if (s1Length && *s1Length <= MAX_SHORT_LINE_LENGTH) { - Optional<size_t> s2Length = getLengthOnSingleLine(SM, s2Start); + std::optional<size_t> s2Length = getLengthOnSingleLine(SM, s2Start); if (s2Length && *s2Length <= MAX_SHORT_LINE_LENGTH) { Path.erase(I); I = Path.erase(NextI); @@ -1719,7 +1719,7 @@ static void removePunyEdges(PathPieces &path, const SourceManager &SM, std::swap(SecondLoc, FirstLoc); SourceRange EdgeRange(FirstLoc, SecondLoc); - Optional<size_t> ByteWidth = getLengthOnSingleLine(SM, EdgeRange); + std::optional<size_t> ByteWidth = getLengthOnSingleLine(SM, EdgeRange); // If the statements are on different lines, continue. if (!ByteWidth) @@ -2310,7 +2310,7 @@ void PathSensitiveBugReport::markInteresting(const LocationContext *LC) { InterestingLocationContexts.insert(LC); } -Optional<bugreporter::TrackingKind> +std::optional<bugreporter::TrackingKind> PathSensitiveBugReport::getInterestingnessKind(SVal V) const { auto RKind = getInterestingnessKind(V.getAsRegion()); auto SKind = getInterestingnessKind(V.getAsSymbol()); @@ -2332,25 +2332,25 @@ PathSensitiveBugReport::getInterestingnessKind(SVal V) const { "BugReport::getInterestingnessKind currently can only handle 2 different " "tracking kinds! Please define what tracking kind should we return here " "when the kind of getAsRegion() and getAsSymbol() is different!"); - return None; + return std::nullopt; } -Optional<bugreporter::TrackingKind> +std::optional<bugreporter::TrackingKind> PathSensitiveBugReport::getInterestingnessKind(SymbolRef sym) const { if (!sym) - return None; + return std::nullopt; // We don't currently consider metadata symbols to be interesting // even if we know their region is interesting. Is that correct behavior? auto It = InterestingSymbols.find(sym); if (It == InterestingSymbols.end()) - return None; + return std::nullopt; return It->getSecond(); } -Optional<bugreporter::TrackingKind> +std::optional<bugreporter::TrackingKind> PathSensitiveBugReport::getInterestingnessKind(const MemRegion *R) const { if (!R) - return None; + return std::nullopt; R = R->getBaseRegion(); auto It = InterestingRegions.find(R); @@ -2359,7 +2359,7 @@ PathSensitiveBugReport::getInterestingnessKind(const MemRegion *R) const { if (const auto *SR = dyn_cast<SymbolicRegion>(R)) return getInterestingnessKind(SR->getSymbol()); - return None; + return std::nullopt; } bool PathSensitiveBugReport::isInteresting(SVal V) const { @@ -2387,7 +2387,7 @@ const Stmt *PathSensitiveBugReport::getStmt() const { ProgramPoint ProgP = ErrorNode->getLocation(); const Stmt *S = nullptr; - if (Optional<BlockEntrance> BE = ProgP.getAs<BlockEntrance>()) { + if (std::optional<BlockEntrance> BE = ProgP.getAs<BlockEntrance>()) { CFGBlock &Exit = ProgP.getLocationContext()->getCFG()->getExit(); if (BE->getBlock() == &Exit) S = ErrorNode->getPreviousStmtForDiagnostics(); @@ -2419,7 +2419,7 @@ PathSensitiveBugReport::getLocation() const { if (!S) { // If this is an implicit call, return the implicit call point location. - if (Optional<PreImplicitCall> PIE = P.getAs<PreImplicitCall>()) + if (std::optional<PreImplicitCall> PIE = P.getAs<PreImplicitCall>()) return PathDiagnosticLocation(PIE->getLocation(), SM); if (auto FE = P.getAs<FunctionExitPoint>()) { if (const ReturnStmt *RS = FE->getStmt()) @@ -2821,7 +2821,7 @@ generateVisitorsDiagnostics(PathSensitiveBugReport *R, return Notes; } -Optional<PathDiagnosticBuilder> PathDiagnosticBuilder::findValidReport( +std::optional<PathDiagnosticBuilder> PathDiagnosticBuilder::findValidReport( ArrayRef<PathSensitiveBugReport *> &bugReports, PathSensitiveBugReporter &Reporter) { @@ -2880,7 +2880,7 @@ PathSensitiveBugReporter::generatePathDiagnostics( auto Out = std::make_unique<DiagnosticForConsumerMapTy>(); - Optional<PathDiagnosticBuilder> PDB = + std::optional<PathDiagnosticBuilder> PDB = PathDiagnosticBuilder::findValidReport(bugReports, *this); if (PDB) { @@ -3097,9 +3097,8 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) { if (getAnalyzerOptions().ShouldDisplayNotesAsEvents) { // For path diagnostic consumers that don't support extra notes, // we may optionally convert those to path notes. - for (auto I = report->getNotes().rbegin(), - E = report->getNotes().rend(); I != E; ++I) { - PathDiagnosticNotePiece *Piece = I->get(); + for (const auto &I : llvm::reverse(report->getNotes())) { + PathDiagnosticNotePiece *Piece = I.get(); auto ConvertedPiece = std::make_shared<PathDiagnosticEventPiece>( Piece->getLocation(), Piece->getString()); for (const auto &R: Piece->getRanges()) @@ -3108,9 +3107,8 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) { Pieces.push_front(std::move(ConvertedPiece)); } } else { - for (auto I = report->getNotes().rbegin(), - E = report->getNotes().rend(); I != E; ++I) - Pieces.push_front(*I); + for (const auto &I : llvm::reverse(report->getNotes())) + Pieces.push_front(I); } for (const auto &I : report->getFixits()) diff --git a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index 3a90c37a36da..2b461acf9a73 100644 --- a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -46,8 +46,6 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" #include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/None.h" -#include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallString.h" @@ -60,6 +58,7 @@ #include <cassert> #include <deque> #include <memory> +#include <optional> #include <string> #include <utility> @@ -206,8 +205,8 @@ static bool hasVisibleUpdate(const ExplodedNode *LeftNode, SVal LeftVal, RLCV->getStore() == RightNode->getState()->getStore(); } -static Optional<SVal> getSValForVar(const Expr *CondVarExpr, - const ExplodedNode *N) { +static std::optional<SVal> getSValForVar(const Expr *CondVarExpr, + const ExplodedNode *N) { ProgramStateRef State = N->getState(); const LocationContext *LCtx = N->getLocationContext(); @@ -227,16 +226,16 @@ static Optional<SVal> getSValForVar(const Expr *CondVarExpr, if (auto FieldL = State->getSVal(ME, LCtx).getAs<Loc>()) return State->getRawSVal(*FieldL, FD->getType()); - return None; + return std::nullopt; } -static Optional<const llvm::APSInt *> +static std::optional<const llvm::APSInt *> getConcreteIntegerValue(const Expr *CondVarExpr, const ExplodedNode *N) { - if (Optional<SVal> V = getSValForVar(CondVarExpr, N)) + if (std::optional<SVal> V = getSValForVar(CondVarExpr, N)) if (auto CI = V->getAs<nonloc::ConcreteInt>()) return &CI->getValue(); - return None; + return std::nullopt; } static bool isVarAnInterestingCondition(const Expr *CondVarExpr, @@ -248,8 +247,9 @@ static bool isVarAnInterestingCondition(const Expr *CondVarExpr, if (!B->getErrorNode()->getStackFrame()->isParentOf(N->getStackFrame())) return false; - if (Optional<SVal> V = getSValForVar(CondVarExpr, N)) - if (Optional<bugreporter::TrackingKind> K = B->getInterestingnessKind(*V)) + if (std::optional<SVal> V = getSValForVar(CondVarExpr, N)) + if (std::optional<bugreporter::TrackingKind> K = + B->getInterestingnessKind(*V)) return *K == bugreporter::TrackingKind::Condition; return false; @@ -257,7 +257,7 @@ static bool isVarAnInterestingCondition(const Expr *CondVarExpr, static bool isInterestingExpr(const Expr *E, const ExplodedNode *N, const PathSensitiveBugReport *B) { - if (Optional<SVal> V = getSValForVar(E, N)) + if (std::optional<SVal> V = getSValForVar(E, N)) return B->getInterestingnessKind(*V).has_value(); return false; } @@ -538,8 +538,8 @@ private: /// Dereferences fields up to a given recursion limit. /// Note that \p Vec is passed by value, leading to quadratic copying cost, /// but it's OK in practice since its length is limited to DEREFERENCE_LIMIT. - /// \return A chain fields leading to the region of interest or None. - const Optional<RegionVector> + /// \return A chain fields leading to the region of interest or std::nullopt. + const std::optional<RegionVector> findRegionOfInterestInRecord(const RecordDecl *RD, ProgramStateRef State, const MemRegion *R, const RegionVector &Vec = {}, int depth = 0); @@ -619,26 +619,26 @@ static bool potentiallyWritesIntoIvar(const Decl *Parent, /// Dereferences fields up to a given recursion limit. /// Note that \p Vec is passed by value, leading to quadratic copying cost, /// but it's OK in practice since its length is limited to DEREFERENCE_LIMIT. -/// \return A chain fields leading to the region of interest or None. -const Optional<NoStoreFuncVisitor::RegionVector> +/// \return A chain fields leading to the region of interest or std::nullopt. +const std::optional<NoStoreFuncVisitor::RegionVector> NoStoreFuncVisitor::findRegionOfInterestInRecord( const RecordDecl *RD, ProgramStateRef State, const MemRegion *R, const NoStoreFuncVisitor::RegionVector &Vec /* = {} */, int depth /* = 0 */) { if (depth == DEREFERENCE_LIMIT) // Limit the recursion depth. - return None; + return std::nullopt; if (const auto *RDX = dyn_cast<CXXRecordDecl>(RD)) if (!RDX->hasDefinition()) - return None; + return std::nullopt; // Recursively examine the base classes. // Note that following base classes does not increase the recursion depth. if (const auto *RDX = dyn_cast<CXXRecordDecl>(RD)) for (const auto &II : RDX->bases()) if (const RecordDecl *RRD = II.getType()->getAsRecordDecl()) - if (Optional<RegionVector> Out = + if (std::optional<RegionVector> Out = findRegionOfInterestInRecord(RRD, State, R, Vec, depth)) return Out; @@ -664,12 +664,12 @@ NoStoreFuncVisitor::findRegionOfInterestInRecord( continue; if (const RecordDecl *RRD = PT->getAsRecordDecl()) - if (Optional<RegionVector> Out = + if (std::optional<RegionVector> Out = findRegionOfInterestInRecord(RRD, State, VR, VecF, depth + 1)) return Out; } - return None; + return std::nullopt; } PathDiagnosticPieceRef @@ -730,7 +730,7 @@ PathDiagnosticPieceRef NoStoreFuncVisitor::maybeEmitNoteForParameters( ProgramStateRef State = N->getState(); if (const RecordDecl *RD = PT->getAsRecordDecl()) - if (Optional<RegionVector> P = + if (std::optional<RegionVector> P = findRegionOfInterestInRecord(RD, State, MR)) return maybeEmitNote(R, Call, N, *P, RegionOfInterest, ParamName, ParamIsReferenceType, IndirectionLevel); @@ -928,12 +928,12 @@ public: private: /// \return Source location of right hand side of an assignment /// into \c RegionOfInterest, empty optional if none found. - Optional<SourceLocation> matchAssignment(const ExplodedNode *N) { + std::optional<SourceLocation> matchAssignment(const ExplodedNode *N) { const Stmt *S = N->getStmtForDiagnostics(); ProgramStateRef State = N->getState(); auto *LCtx = N->getLocationContext(); if (!S) - return None; + return std::nullopt; if (const auto *DS = dyn_cast<DeclStmt>(S)) { if (const auto *VD = dyn_cast<VarDecl>(DS->getSingleDecl())) @@ -948,7 +948,7 @@ private: return RHS->getBeginLoc(); } } - return None; + return std::nullopt; } }; @@ -1001,7 +1001,7 @@ public: if (N->getLocationContext() != CalleeSFC) return nullptr; - Optional<StmtPoint> SP = N->getLocationAs<StmtPoint>(); + std::optional<StmtPoint> SP = N->getLocationAs<StmtPoint>(); if (!SP) return nullptr; @@ -1023,7 +1023,7 @@ public: assert(RetE && "Tracking a return value for a void function"); // Handle cases where a reference is returned and then immediately used. - Optional<Loc> LValue; + std::optional<Loc> LValue; if (RetE->isGLValue()) { if ((LValue = V.getAs<Loc>())) { SVal RValue = State->getRawSVal(*LValue, RetE->getType()); @@ -1122,7 +1122,7 @@ public: assert(Options.ShouldAvoidSuppressingNullArgumentPaths); // Are we at the entry node for this call? - Optional<CallEnter> CE = N->getLocationAs<CallEnter>(); + std::optional<CallEnter> CE = N->getLocationAs<CallEnter>(); if (!CE) return nullptr; @@ -1140,7 +1140,7 @@ public: ProgramStateRef State = N->getState(); CallEventRef<> Call = CallMgr.getCaller(CalleeSFC, State); for (unsigned I = 0, E = Call->getNumArgs(); I != E; ++I) { - Optional<Loc> ArgV = Call->getArgSVal(I).getAs<Loc>(); + std::optional<Loc> ArgV = Call->getArgSVal(I).getAs<Loc>(); if (!ArgV) continue; @@ -1187,8 +1187,6 @@ public: } }; -} // end of anonymous namespace - //===----------------------------------------------------------------------===// // StoreSiteFinder //===----------------------------------------------------------------------===// @@ -1228,6 +1226,7 @@ public: BugReporterContext &BRC, PathSensitiveBugReport &BR) override; }; +} // namespace void StoreSiteFinder::Profile(llvm::FoldingSetNodeID &ID) const { static int tag = 0; @@ -1241,7 +1240,7 @@ void StoreSiteFinder::Profile(llvm::FoldingSetNodeID &ID) const { /// Returns true if \p N represents the DeclStmt declaring and initializing /// \p VR. static bool isInitializationOfVar(const ExplodedNode *N, const VarRegion *VR) { - Optional<PostStmt> P = N->getLocationAs<PostStmt>(); + std::optional<PostStmt> P = N->getLocationAs<PostStmt>(); if (!P) return false; @@ -1340,13 +1339,12 @@ static void showBRDiagnostics(llvm::raw_svector_ostream &OS, StoreInfo SI) { static void showBRParamDiagnostics(llvm::raw_svector_ostream &OS, StoreInfo SI) { const auto *VR = cast<VarRegion>(SI.Dest); - const auto *Param = cast<ParmVarDecl>(VR->getDecl()); + const auto *D = VR->getDecl(); OS << "Passing "; if (isa<loc::ConcreteInt>(SI.Value)) { - OS << (isObjCPointer(Param) ? "nil object reference" - : "null pointer value"); + OS << (isObjCPointer(D) ? "nil object reference" : "null pointer value"); } else if (SI.Value.isUndef()) { OS << "uninitialized value"; @@ -1361,12 +1359,19 @@ static void showBRParamDiagnostics(llvm::raw_svector_ostream &OS, OS << "value"; } - // Printed parameter indexes are 1-based, not 0-based. - unsigned Idx = Param->getFunctionScopeIndex() + 1; - OS << " via " << Idx << llvm::getOrdinalSuffix(Idx) << " parameter"; - if (VR->canPrintPretty()) { - OS << " "; - VR->printPretty(OS); + if (const auto *Param = dyn_cast<ParmVarDecl>(VR->getDecl())) { + // Printed parameter indexes are 1-based, not 0-based. + unsigned Idx = Param->getFunctionScopeIndex() + 1; + OS << " via " << Idx << llvm::getOrdinalSuffix(Idx) << " parameter"; + if (VR->canPrintPretty()) { + OS << " "; + VR->printPretty(OS); + } + } else if (const auto *ImplParam = dyn_cast<ImplicitParamDecl>(D)) { + if (ImplParam->getParameterKind() == + ImplicitParamDecl::ImplicitParamKind::ObjCSelf) { + OS << " via implicit parameter 'self'"; + } } } @@ -1410,6 +1415,83 @@ static void showBRDefaultDiagnostics(llvm::raw_svector_ostream &OS, } } +static bool isTrivialCopyOrMoveCtor(const CXXConstructExpr *CE) { + if (!CE) + return false; + + const auto *CtorDecl = CE->getConstructor(); + + return CtorDecl->isCopyOrMoveConstructor() && CtorDecl->isTrivial(); +} + +static const Expr *tryExtractInitializerFromList(const InitListExpr *ILE, + const MemRegion *R) { + + const auto *TVR = dyn_cast_or_null<TypedValueRegion>(R); + + if (!TVR) + return nullptr; + + const auto ITy = ILE->getType().getCanonicalType(); + + // Push each sub-region onto the stack. + std::stack<const TypedValueRegion *> TVRStack; + while (isa<FieldRegion>(TVR) || isa<ElementRegion>(TVR)) { + // We found a region that matches the type of the init list, + // so we assume this is the outer-most region. This can happen + // if the initializer list is inside a class. If our assumption + // is wrong, we return a nullptr in the end. + if (ITy == TVR->getValueType().getCanonicalType()) + break; + + TVRStack.push(TVR); + TVR = cast<TypedValueRegion>(TVR->getSuperRegion()); + } + + // If the type of the outer most region doesn't match the type + // of the ILE, we can't match the ILE and the region. + if (ITy != TVR->getValueType().getCanonicalType()) + return nullptr; + + const Expr *Init = ILE; + while (!TVRStack.empty()) { + TVR = TVRStack.top(); + TVRStack.pop(); + + // We hit something that's not an init list before + // running out of regions, so we most likely failed. + if (!isa<InitListExpr>(Init)) + return nullptr; + + ILE = cast<InitListExpr>(Init); + auto NumInits = ILE->getNumInits(); + + if (const auto *FR = dyn_cast<FieldRegion>(TVR)) { + const auto *FD = FR->getDecl(); + + if (FD->getFieldIndex() >= NumInits) + return nullptr; + + Init = ILE->getInit(FD->getFieldIndex()); + } else if (const auto *ER = dyn_cast<ElementRegion>(TVR)) { + const auto Ind = ER->getIndex(); + + // If index is symbolic, we can't figure out which expression + // belongs to the region. + if (!Ind.isConstant()) + return nullptr; + + const auto IndVal = Ind.getAsInteger()->getLimitedValue(); + if (IndVal >= NumInits) + return nullptr; + + Init = ILE->getInit(IndVal); + } + } + + return Init; +} + PathDiagnosticPieceRef StoreSiteFinder::VisitNode(const ExplodedNode *Succ, BugReporterContext &BRC, PathSensitiveBugReport &BR) { @@ -1431,7 +1513,8 @@ PathDiagnosticPieceRef StoreSiteFinder::VisitNode(const ExplodedNode *Succ, // If this is a post initializer expression, initializing the region, we // should track the initializer expression. - if (Optional<PostInitializer> PIP = Pred->getLocationAs<PostInitializer>()) { + if (std::optional<PostInitializer> PIP = + Pred->getLocationAs<PostInitializer>()) { const MemRegion *FieldReg = (const MemRegion *)PIP->getLocationValue(); if (FieldReg == R) { StoreSite = Pred; @@ -1449,25 +1532,101 @@ PathDiagnosticPieceRef StoreSiteFinder::VisitNode(const ExplodedNode *Succ, return nullptr; if (hasVisibleUpdate(Pred, Pred->getState()->getSVal(R), Succ, V)) { - Optional<PostStore> PS = Succ->getLocationAs<PostStore>(); + std::optional<PostStore> PS = Succ->getLocationAs<PostStore>(); if (!PS || PS->getLocationValue() != R) return nullptr; } StoreSite = Succ; - // If this is an assignment expression, we can track the value - // being assigned. - if (Optional<PostStmt> P = Succ->getLocationAs<PostStmt>()) - if (const BinaryOperator *BO = P->getStmtAs<BinaryOperator>()) + if (std::optional<PostStmt> P = Succ->getLocationAs<PostStmt>()) { + // If this is an assignment expression, we can track the value + // being assigned. + if (const BinaryOperator *BO = P->getStmtAs<BinaryOperator>()) { if (BO->isAssignmentOp()) InitE = BO->getRHS(); + } + // If we have a declaration like 'S s{1,2}' that needs special + // handling, we handle it here. + else if (const auto *DS = P->getStmtAs<DeclStmt>()) { + const auto *Decl = DS->getSingleDecl(); + if (isa<VarDecl>(Decl)) { + const auto *VD = cast<VarDecl>(Decl); + + // FIXME: Here we only track the inner most region, so we lose + // information, but it's still better than a crash or no information + // at all. + // + // E.g.: The region we have is 's.s2.s3.s4.y' and we only track 'y', + // and throw away the rest. + if (const auto *ILE = dyn_cast<InitListExpr>(VD->getInit())) + InitE = tryExtractInitializerFromList(ILE, R); + } + } else if (const auto *CE = P->getStmtAs<CXXConstructExpr>()) { + + const auto State = Succ->getState(); + + if (isTrivialCopyOrMoveCtor(CE) && isa<SubRegion>(R)) { + // Migrate the field regions from the current object to + // the parent object. If we track 'a.y.e' and encounter + // 'S a = b' then we need to track 'b.y.e'. + + // Push the regions to a stack, from last to first, so + // considering the example above the stack will look like + // (bottom) 'e' -> 'y' (top). + + std::stack<const SubRegion *> SRStack; + const SubRegion *SR = cast<SubRegion>(R); + while (isa<FieldRegion>(SR) || isa<ElementRegion>(SR)) { + SRStack.push(SR); + SR = cast<SubRegion>(SR->getSuperRegion()); + } + + // Get the region for the object we copied/moved from. + const auto *OriginEx = CE->getArg(0); + const auto OriginVal = + State->getSVal(OriginEx, Succ->getLocationContext()); + + // Pop the stored field regions and apply them to the origin + // object in the same order we had them on the copy. + // OriginField will evolve like 'b' -> 'b.y' -> 'b.y.e'. + SVal OriginField = OriginVal; + while (!SRStack.empty()) { + const auto *TopR = SRStack.top(); + SRStack.pop(); + + if (const auto *FR = dyn_cast<FieldRegion>(TopR)) { + OriginField = State->getLValue(FR->getDecl(), OriginField); + } else if (const auto *ER = dyn_cast<ElementRegion>(TopR)) { + OriginField = State->getLValue(ER->getElementType(), + ER->getIndex(), OriginField); + } else { + // FIXME: handle other region type + } + } + + // Track 'b.y.e'. + getParentTracker().track(V, OriginField.getAsRegion(), Options); + InitE = OriginEx; + } + } + // This branch can occur in cases like `Ctor() : field{ x, y } {}'. + else if (const auto *ILE = P->getStmtAs<InitListExpr>()) { + // FIXME: Here we only track the top level region, so we lose + // information, but it's still better than a crash or no information + // at all. + // + // E.g.: The region we have is 's.s2.s3.s4.y' and we only track 'y', and + // throw away the rest. + InitE = tryExtractInitializerFromList(ILE, R); + } + } // If this is a call entry, the variable should be a parameter. // FIXME: Handle CXXThisRegion as well. (This is not a priority because // 'this' should never be NULL, but this visitor isn't just for NULL and // UndefinedVal.) - if (Optional<CallEnter> CE = Succ->getLocationAs<CallEnter>()) { + if (std::optional<CallEnter> CE = Succ->getLocationAs<CallEnter>()) { if (const auto *VR = dyn_cast<VarRegion>(R)) { if (const auto *Param = dyn_cast<ParmVarDecl>(VR->getDecl())) { @@ -1590,7 +1749,7 @@ PathDiagnosticPieceRef StoreSiteFinder::VisitNode(const ExplodedNode *Succ, R, OldRegion}; - if (Optional<PostStmt> PS = StoreSite->getLocationAs<PostStmt>()) { + if (std::optional<PostStmt> PS = StoreSite->getLocationAs<PostStmt>()) { const Stmt *S = PS->getStmt(); const auto *DS = dyn_cast<DeclStmt>(S); const auto *VR = dyn_cast<VarRegion>(R); @@ -1983,7 +2142,7 @@ static const Expr *peelOffOuterExpr(const Expr *Ex, const ExplodedNode *N) { const ExplodedNode *NI = N; do { ProgramPoint ProgPoint = NI->getLocation(); - if (Optional<BlockEdge> BE = ProgPoint.getAs<BlockEdge>()) { + if (std::optional<BlockEdge> BE = ProgPoint.getAs<BlockEdge>()) { const CFGBlock *srcBlk = BE->getSrc(); if (const Stmt *term = srcBlk->getTerminatorStmt()) { if (term == CO) { @@ -2058,6 +2217,7 @@ PathDiagnosticPieceRef StoreHandler::constructNote(StoreInfo SI, return std::make_shared<PathDiagnosticEventPiece>(L, NodeText); } +namespace { class DefaultStoreHandler final : public StoreHandler { public: using StoreHandler::StoreHandler; @@ -2255,7 +2415,8 @@ class InlinedFunctionCallHandler final : public ExpressionHandler { do { // If that is satisfied we found our statement as an inlined call. - if (Optional<CallExitEnd> CEE = ExprNode->getLocationAs<CallExitEnd>()) + if (std::optional<CallExitEnd> CEE = + ExprNode->getLocationAs<CallExitEnd>()) if (CEE->getCalleeContext()->getCallSite() == E) break; @@ -2270,7 +2431,7 @@ class InlinedFunctionCallHandler final : public ExpressionHandler { // FIXME: This code currently bypasses the call site for the // conservatively evaluated allocator. if (!BypassCXXNewExprEval) - if (Optional<StmtPoint> SP = ExprNode->getLocationAs<StmtPoint>()) + if (std::optional<StmtPoint> SP = ExprNode->getLocationAs<StmtPoint>()) // See if we do not enter into another context. if (SP->getStmt() == E && CurrentSFC == PredSFC) break; @@ -2285,7 +2446,7 @@ class InlinedFunctionCallHandler final : public ExpressionHandler { return {}; // Finally, see if we inlined the call. - Optional<CallExitEnd> CEE = ExprNode->getLocationAs<CallExitEnd>(); + std::optional<CallExitEnd> CEE = ExprNode->getLocationAs<CallExitEnd>(); if (!CEE) return {}; @@ -2299,7 +2460,7 @@ class InlinedFunctionCallHandler final : public ExpressionHandler { // Handle cases where a reference is returned and then immediately used. if (cast<Expr>(E)->isGLValue()) - if (Optional<Loc> LValue = RetVal.getAs<Loc>()) + if (std::optional<Loc> LValue = RetVal.getAs<Loc>()) RetVal = State->getSVal(*LValue); // See if the return value is NULL. If so, suppress the report. @@ -2307,7 +2468,7 @@ class InlinedFunctionCallHandler final : public ExpressionHandler { bool EnableNullFPSuppression = false; if (Opts.EnableNullFPSuppression && Options.ShouldSuppressNullReturnPaths) - if (Optional<Loc> RetLoc = RetVal.getAs<Loc>()) + if (std::optional<Loc> RetLoc = RetVal.getAs<Loc>()) EnableNullFPSuppression = State->isNull(*RetLoc).isConstrainedTrue(); PathSensitiveBugReport &Report = getParentTracker().getReport(); @@ -2342,7 +2503,7 @@ public: // what is written inside the pointer. bool CanDereference = true; if (const auto *SR = L->getRegionAs<SymbolicRegion>()) { - if (SR->getSymbol()->getType()->getPointeeType()->isVoidType()) + if (SR->getPointeeStaticType()->isVoidType()) CanDereference = false; } else if (L->getRegionAs<AllocaRegion>()) CanDereference = false; @@ -2395,6 +2556,29 @@ public: if (!RVNode) return {}; + Tracker::Result CombinedResult; + Tracker &Parent = getParentTracker(); + + const auto track = [&CombinedResult, &Parent, ExprNode, + Opts](const Expr *Inner) { + CombinedResult.combineWith(Parent.track(Inner, ExprNode, Opts)); + }; + + // FIXME: Initializer lists can appear in many different contexts + // and most of them needs a special handling. For now let's handle + // what we can. If the initializer list only has 1 element, we track + // that. + // This snippet even handles nesting, e.g.: int *x{{{{{y}}}}}; + if (const auto *ILE = dyn_cast<InitListExpr>(E)) { + if (ILE->getNumInits() == 1) { + track(ILE->getInit(0)); + + return CombinedResult; + } + + return {}; + } + ProgramStateRef RVState = RVNode->getState(); SVal V = RVState->getSValAsScalarOrLoc(E, RVNode->getLocationContext()); const auto *BO = dyn_cast<BinaryOperator>(E); @@ -2406,13 +2590,6 @@ public: SVal LHSV = RVState->getSVal(BO->getLHS(), RVNode->getLocationContext()); // Track both LHS and RHS of a multiplication. - Tracker::Result CombinedResult; - Tracker &Parent = getParentTracker(); - - const auto track = [&CombinedResult, &Parent, ExprNode, Opts](Expr *Inner) { - CombinedResult.combineWith(Parent.track(Inner, ExprNode, Opts)); - }; - if (BO->getOpcode() == BO_Mul) { if (LHSV.isZeroConstant()) track(BO->getLHS()); @@ -2426,6 +2603,7 @@ public: return CombinedResult; } }; +} // namespace Tracker::Tracker(PathSensitiveBugReport &Report) : Report(Report) { // Default expression handlers. @@ -2524,7 +2702,7 @@ const Expr *NilReceiverBRVisitor::getNilReceiver(const Stmt *S, PathDiagnosticPieceRef NilReceiverBRVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC, PathSensitiveBugReport &BR) { - Optional<PreStmt> P = N->getLocationAs<PreStmt>(); + std::optional<PreStmt> P = N->getLocationAs<PreStmt>(); if (!P) return nullptr; @@ -2588,7 +2766,7 @@ ConditionBRVisitor::VisitNodeImpl(const ExplodedNode *N, // If an assumption was made on a branch, it should be caught // here by looking at the state transition. - if (Optional<BlockEdge> BE = ProgPoint.getAs<BlockEdge>()) { + if (std::optional<BlockEdge> BE = ProgPoint.getAs<BlockEdge>()) { const CFGBlock *SrcBlock = BE->getSrc(); if (const Stmt *Term = SrcBlock->getTerminatorStmt()) { // If the tag of the previous node is 'Eagerly Assume...' the current @@ -2605,7 +2783,7 @@ ConditionBRVisitor::VisitNodeImpl(const ExplodedNode *N, return nullptr; } - if (Optional<PostStmt> PS = ProgPoint.getAs<PostStmt>()) { + if (std::optional<PostStmt> PS = ProgPoint.getAs<PostStmt>()) { const ProgramPointTag *CurrentNodeTag = PS->getTag(); if (CurrentNodeTag != Tags.first && CurrentNodeTag != Tags.second) return nullptr; @@ -2744,13 +2922,11 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, BugReporterContext &BRC, Loc, TookTrue ? GenericTrueMessage : GenericFalseMessage); } -bool ConditionBRVisitor::patternMatch(const Expr *Ex, - const Expr *ParentEx, - raw_ostream &Out, - BugReporterContext &BRC, +bool ConditionBRVisitor::patternMatch(const Expr *Ex, const Expr *ParentEx, + raw_ostream &Out, BugReporterContext &BRC, PathSensitiveBugReport &report, const ExplodedNode *N, - Optional<bool> &prunable, + std::optional<bool> &prunable, bool IsSameFieldName) { const Expr *OriginalExpr = Ex; Ex = Ex->IgnoreParenCasts(); @@ -2836,7 +3012,7 @@ PathDiagnosticPieceRef ConditionBRVisitor::VisitTrueTest( PathSensitiveBugReport &R, const ExplodedNode *N, bool TookTrue, bool IsAssuming) { bool shouldInvert = false; - Optional<bool> shouldPrune; + std::optional<bool> shouldPrune; // Check if the field name of the MemberExprs is ambiguous. Example: // " 'a.d' is equal to 'h.d' " in 'test/Analysis/null-deref-path-notes.cpp'. @@ -2947,7 +3123,7 @@ PathDiagnosticPieceRef ConditionBRVisitor::VisitTrueTest( PathDiagnosticLocation Loc(Cond, SM, LCtx); auto event = std::make_shared<PathDiagnosticEventPiece>(Loc, Message); if (shouldPrune) - event->setPrunable(shouldPrune.value()); + event->setPrunable(*shouldPrune); return event; } @@ -3070,7 +3246,7 @@ bool ConditionBRVisitor::printValue(const Expr *CondVarExpr, raw_ostream &Out, if (!Ty->isIntegralOrEnumerationType()) return false; - Optional<const llvm::APSInt *> IntValue; + std::optional<const llvm::APSInt *> IntValue; if (!IsAssuming) IntValue = getConcreteIntegerValue(CondVarExpr, N); @@ -3081,9 +3257,9 @@ bool ConditionBRVisitor::printValue(const Expr *CondVarExpr, raw_ostream &Out, Out << (TookTrue ? "not equal to 0" : "0"); } else { if (Ty->isBooleanType()) - Out << (IntValue.value()->getBoolValue() ? "true" : "false"); + Out << ((*IntValue)->getBoolValue() ? "true" : "false"); else - Out << *IntValue.value(); + Out << **IntValue; } return true; @@ -3196,7 +3372,7 @@ UndefOrNullArgVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC, ProgramPoint ProgLoc = N->getLocation(); // We are only interested in visiting CallEnter nodes. - Optional<CallEnter> CEnter = ProgLoc.getAs<CallEnter>(); + std::optional<CallEnter> CEnter = ProgLoc.getAs<CallEnter>(); if (!CEnter) return nullptr; @@ -3275,11 +3451,11 @@ void FalsePositiveRefutationBRVisitor::finalizeVisitor( } // And check for satisfiability - Optional<bool> IsSAT = RefutationSolver->check(); + std::optional<bool> IsSAT = RefutationSolver->check(); if (!IsSAT) return; - if (!IsSAT.value()) + if (!*IsSAT) BR.markInvalid("Infeasible constraints", EndPathNode->getLocationContext()); } @@ -3334,7 +3510,7 @@ PathDiagnosticPieceRef TagVisitor::VisitNode(const ExplodedNode *N, if (!T) return nullptr; - if (Optional<std::string> Msg = T->generateMessage(BRC, R)) { + if (std::optional<std::string> Msg = T->generateMessage(BRC, R)) { PathDiagnosticLocation Loc = PathDiagnosticLocation::create(PP, BRC.getSourceManager()); auto Piece = std::make_shared<PathDiagnosticEventPiece>(Loc, *Msg); diff --git a/clang/lib/StaticAnalyzer/Core/CallDescription.cpp b/clang/lib/StaticAnalyzer/Core/CallDescription.cpp index bb8b7492e248..94b2fde0a6f3 100644 --- a/clang/lib/StaticAnalyzer/Core/CallDescription.cpp +++ b/clang/lib/StaticAnalyzer/Core/CallDescription.cpp @@ -17,13 +17,13 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/Optional.h" #include <iterator> +#include <optional> using namespace llvm; using namespace clang; -using MaybeCount = Optional<unsigned>; +using MaybeCount = std::optional<unsigned>; // A constructor helper. static MaybeCount readRequiredParams(MaybeCount RequiredArgs, @@ -32,11 +32,11 @@ static MaybeCount readRequiredParams(MaybeCount RequiredArgs, return RequiredParams; if (RequiredArgs) return RequiredArgs; - return None; + return std::nullopt; } ento::CallDescription::CallDescription(CallDescriptionFlags Flags, - ArrayRef<const char *> QualifiedName, + ArrayRef<StringRef> QualifiedName, MaybeCount RequiredArgs /*= None*/, MaybeCount RequiredParams /*= None*/) : RequiredArgs(RequiredArgs), @@ -44,11 +44,12 @@ ento::CallDescription::CallDescription(CallDescriptionFlags Flags, Flags(Flags) { assert(!QualifiedName.empty()); this->QualifiedName.reserve(QualifiedName.size()); - llvm::copy(QualifiedName, std::back_inserter(this->QualifiedName)); + llvm::transform(QualifiedName, std::back_inserter(this->QualifiedName), + [](StringRef From) { return From.str(); }); } /// Construct a CallDescription with default flags. -ento::CallDescription::CallDescription(ArrayRef<const char *> QualifiedName, +ento::CallDescription::CallDescription(ArrayRef<StringRef> QualifiedName, MaybeCount RequiredArgs /*= None*/, MaybeCount RequiredParams /*= None*/) : CallDescription(CDF_None, QualifiedName, RequiredArgs, RequiredParams) {} diff --git a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp index 3a8d69df7a64..8516e3643425 100644 --- a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -49,8 +49,6 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/ImmutableList.h" -#include "llvm/ADT/None.h" -#include "llvm/ADT/Optional.h" #include "llvm/ADT/PointerIntPair.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallVector.h" @@ -62,6 +60,7 @@ #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" #include <cassert> +#include <optional> #include <utility> #define DEBUG_TYPE "static-analyzer-call-event" @@ -424,6 +423,38 @@ static SVal processArgument(SVal Value, const Expr *ArgumentExpr, return Value; } +/// Cast the argument value to the type of the parameter at the function +/// declaration. +/// Returns the argument value if it didn't need a cast. +/// Or returns the cast argument if it needed a cast. +/// Or returns 'Unknown' if it would need a cast but the callsite and the +/// runtime definition don't match in terms of argument and parameter count. +static SVal castArgToParamTypeIfNeeded(const CallEvent &Call, unsigned ArgIdx, + SVal ArgVal, SValBuilder &SVB) { + const FunctionDecl *RTDecl = + Call.getRuntimeDefinition().getDecl()->getAsFunction(); + const auto *CallExprDecl = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); + + if (!RTDecl || !CallExprDecl) + return ArgVal; + + // The function decl of the Call (in the AST) will not have any parameter + // declarations, if it was 'only' declared without a prototype. However, the + // engine will find the appropriate runtime definition - basically a + // redeclaration, which has a function body (and a function prototype). + if (CallExprDecl->hasPrototype() || !RTDecl->hasPrototype()) + return ArgVal; + + // Only do this cast if the number arguments at the callsite matches with + // the parameters at the runtime definition. + if (Call.getNumArgs() != RTDecl->getNumParams()) + return UnknownVal(); + + const Expr *ArgExpr = Call.getArgExpr(ArgIdx); + const ParmVarDecl *Param = RTDecl->getParamDecl(ArgIdx); + return SVB.evalCast(ArgVal, Param->getType(), ArgExpr->getType()); +} + static void addParameterValuesToBindings(const StackFrameContext *CalleeCtx, CallEvent::BindingsTy &Bindings, SValBuilder &SVB, @@ -449,12 +480,18 @@ static void addParameterValuesToBindings(const StackFrameContext *CalleeCtx, // which makes getArgSVal() fail and return UnknownVal. SVal ArgVal = Call.getArgSVal(Idx); const Expr *ArgExpr = Call.getArgExpr(Idx); - if (!ArgVal.isUnknown()) { - Loc ParamLoc = SVB.makeLoc( - MRMgr.getParamVarRegion(Call.getOriginExpr(), Idx, CalleeCtx)); - Bindings.push_back( - std::make_pair(ParamLoc, processArgument(ArgVal, ArgExpr, *I, SVB))); - } + + if (ArgVal.isUnknown()) + continue; + + // Cast the argument value to match the type of the parameter in some + // edge-cases. + ArgVal = castArgToParamTypeIfNeeded(Call, Idx, ArgVal, SVB); + + Loc ParamLoc = SVB.makeLoc( + MRMgr.getParamVarRegion(Call.getOriginExpr(), Idx, CalleeCtx)); + Bindings.push_back( + std::make_pair(ParamLoc, processArgument(ArgVal, ArgExpr, *I, SVB))); } // FIXME: Variadic arguments are not handled at all right now. @@ -477,24 +514,23 @@ const ConstructionContext *CallEvent::getConstructionContext() const { return nullptr; } -Optional<SVal> -CallEvent::getReturnValueUnderConstruction() const { +std::optional<SVal> CallEvent::getReturnValueUnderConstruction() const { const auto *CC = getConstructionContext(); if (!CC) - return None; + return std::nullopt; EvalCallOptions CallOpts; ExprEngine &Engine = getState()->getStateManager().getOwningEngine(); - SVal RetVal = - Engine.computeObjectUnderConstruction(getOriginExpr(), getState(), - getLocationContext(), CC, CallOpts); + SVal RetVal = Engine.computeObjectUnderConstruction( + getOriginExpr(), getState(), &Engine.getBuilderContext(), + getLocationContext(), CC, CallOpts); return RetVal; } ArrayRef<ParmVarDecl*> AnyFunctionCall::parameters() const { const FunctionDecl *D = getDecl(); if (!D) - return None; + return std::nullopt; return D->parameters(); } @@ -770,7 +806,7 @@ void CXXInstanceCall::getInitialStackFrameContents( QualType Ty = Ctx.getPointerType(Ctx.getRecordType(Class)); // FIXME: CallEvent maybe shouldn't be directly accessing StoreManager. - Optional<SVal> V = + std::optional<SVal> V = StateMgr.getStoreManager().evalBaseToDerived(ThisVal, Ty); if (!V) { // We might have suffered some sort of placement new earlier, so @@ -819,7 +855,7 @@ const BlockDataRegion *BlockCall::getBlockRegion() const { ArrayRef<ParmVarDecl*> BlockCall::parameters() const { const BlockDecl *D = getDecl(); if (!D) - return None; + return std::nullopt; return D->parameters(); } @@ -908,7 +944,7 @@ RuntimeDefinition CXXDestructorCall::getRuntimeDefinition() const { ArrayRef<ParmVarDecl*> ObjCMethodCall::parameters() const { const ObjCMethodDecl *D = getDecl(); if (!D) - return None; + return std::nullopt; return D->parameters(); } @@ -1124,7 +1160,7 @@ static const ObjCMethodDecl *findDefiningRedecl(const ObjCMethodDecl *MD) { // Find the redeclaration that defines the method. if (!MD->hasBody()) { - for (auto I : MD->redecls()) + for (auto *I : MD->redecls()) if (I->hasBody()) MD = cast<ObjCMethodDecl>(I); } @@ -1185,10 +1221,10 @@ lookupRuntimeDefinition(const ObjCInterfaceDecl *Interface, // stays around until clang quits, which also may be bad if we // need to release memory. using PrivateMethodCache = - llvm::DenseMap<PrivateMethodKey, Optional<const ObjCMethodDecl *>>; + llvm::DenseMap<PrivateMethodKey, std::optional<const ObjCMethodDecl *>>; static PrivateMethodCache PMC; - Optional<const ObjCMethodDecl *> &Val = + std::optional<const ObjCMethodDecl *> &Val = PMC[{Interface, LookupSelector, InstanceMethod}]; // Query lookupPrivateMethod() if the cache does not hit. @@ -1398,9 +1434,10 @@ CallEventManager::getCaller(const StackFrameContext *CalleeCtx, SVal ThisVal = State->getSVal(ThisPtr); const Stmt *Trigger; - if (Optional<CFGAutomaticObjDtor> AutoDtor = E.getAs<CFGAutomaticObjDtor>()) + if (std::optional<CFGAutomaticObjDtor> AutoDtor = + E.getAs<CFGAutomaticObjDtor>()) Trigger = AutoDtor->getTriggerStmt(); - else if (Optional<CFGDeleteDtor> DeleteDtor = E.getAs<CFGDeleteDtor>()) + else if (std::optional<CFGDeleteDtor> DeleteDtor = E.getAs<CFGDeleteDtor>()) Trigger = DeleteDtor->getDeleteExpr(); else Trigger = Dtor->getBody(); diff --git a/clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp b/clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp index 626ae1ae8066..84ad20a54807 100644 --- a/clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp +++ b/clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp @@ -14,6 +14,7 @@ #include "clang/AST/Decl.h" #include "clang/AST/Expr.h" #include "clang/Lex/Preprocessor.h" +#include <optional> namespace clang { @@ -110,14 +111,13 @@ Nullability getNullabilityAnnotation(QualType Type) { return Nullability::Unspecified; } -llvm::Optional<int> tryExpandAsInteger(StringRef Macro, - const Preprocessor &PP) { +std::optional<int> tryExpandAsInteger(StringRef Macro, const Preprocessor &PP) { const auto *MacroII = PP.getIdentifierInfo(Macro); if (!MacroII) - return llvm::None; + return std::nullopt; const MacroInfo *MI = PP.getMacroInfo(MacroII); if (!MI) - return llvm::None; + return std::nullopt; // Filter out parens. std::vector<Token> FilteredTokens; @@ -131,12 +131,12 @@ llvm::Optional<int> tryExpandAsInteger(StringRef Macro, // FIXME: EOF macro token coming from a PCH file on macOS while marked as // literal, doesn't contain any literal data if (!T.isLiteral() || !T.getLiteralData()) - return llvm::None; + return std::nullopt; StringRef ValueStr = StringRef(T.getLiteralData(), T.getLength()); llvm::APInt IntValue; constexpr unsigned AutoSenseRadix = 0; if (ValueStr.getAsInteger(AutoSenseRadix, IntValue)) - return llvm::None; + return std::nullopt; // Parse an optional minus sign. size_t Size = FilteredTokens.size(); diff --git a/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp b/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp index 94287b7992dd..6fc16223ea82 100644 --- a/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -28,6 +28,7 @@ #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FormatVariadic.h" #include <cassert> +#include <optional> #include <vector> using namespace clang; @@ -35,10 +36,7 @@ using namespace ento; bool CheckerManager::hasPathSensitiveCheckers() const { const auto IfAnyAreNonEmpty = [](const auto &... Callbacks) -> bool { - bool Result = false; - // FIXME: Use fold expressions in C++17. - LLVM_ATTRIBUTE_UNUSED int Unused[]{0, (Result |= !Callbacks.empty())...}; - return Result; + return (!Callbacks.empty() || ...); }; return IfAnyAreNonEmpty( StmtCheckers, PreObjCMessageCheckers, ObjCMessageNilCheckers, @@ -656,7 +654,7 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst, ExprEngine &Eng, const EvalCallOptions &CallOpts) { for (auto *const Pred : Src) { - Optional<CheckerNameRef> evaluatorChecker; + std::optional<CheckerNameRef> evaluatorChecker; ExplodedNodeSet checkDst; NodeBuilder B(Pred, checkDst, Eng.getBuilderContext()); diff --git a/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp b/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp index de90f4a71be0..386a34f6cd4a 100644 --- a/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp @@ -26,7 +26,6 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h" #include "clang/StaticAnalyzer/Core/PathSensitive/WorkList.h" -#include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/Statistic.h" #include "llvm/Support/Casting.h" @@ -34,6 +33,7 @@ #include <algorithm> #include <cassert> #include <memory> +#include <optional> #include <utility> using namespace clang; @@ -273,10 +273,10 @@ void CoreEngine::HandleBlockEdge(const BlockEdge &L, ExplodedNode *Pred) { const ReturnStmt *RS = nullptr; if (!L.getSrc()->empty()) { CFGElement LastElement = L.getSrc()->back(); - if (Optional<CFGStmt> LastStmt = LastElement.getAs<CFGStmt>()) { + if (std::optional<CFGStmt> LastStmt = LastElement.getAs<CFGStmt>()) { RS = dyn_cast<ReturnStmt>(LastStmt->getStmt()); - } else if (Optional<CFGAutomaticObjDtor> AutoDtor = - LastElement.getAs<CFGAutomaticObjDtor>()) { + } else if (std::optional<CFGAutomaticObjDtor> AutoDtor = + LastElement.getAs<CFGAutomaticObjDtor>()) { RS = dyn_cast<ReturnStmt>(AutoDtor->getTriggerStmt()); } } @@ -314,11 +314,10 @@ void CoreEngine::HandleBlockEntrance(const BlockEntrance &L, setBlockCounter(Counter); // Process the entrance of the block. - if (Optional<CFGElement> E = L.getFirstElement()) { + if (std::optional<CFGElement> E = L.getFirstElement()) { NodeBuilderContext Ctx(*this, L.getBlock(), Pred); ExprEng.processCFGElement(*E, Pred, 0, &Ctx); - } - else + } else HandleBlockExit(L.getBlock(), Pred); } @@ -616,7 +615,7 @@ void CoreEngine::enqueue(ExplodedNodeSet &Set, } void CoreEngine::enqueueEndOfFunction(ExplodedNodeSet &Set, const ReturnStmt *RS) { - for (auto I : Set) { + for (auto *I : Set) { // If we are in an inlined call, generate CallExitBegin node. if (I->getLocationContext()->getParent()) { I = generateCallExitBeginNode(I, RS); diff --git a/clang/lib/StaticAnalyzer/Core/DynamicExtent.cpp b/clang/lib/StaticAnalyzer/Core/DynamicExtent.cpp index db9698b4086e..6a86536492cd 100644 --- a/clang/lib/StaticAnalyzer/Core/DynamicExtent.cpp +++ b/clang/lib/StaticAnalyzer/Core/DynamicExtent.cpp @@ -44,6 +44,7 @@ DefinedOrUnknownSVal getDynamicElementCount(ProgramStateRef State, const MemRegion *MR, SValBuilder &SVB, QualType ElementTy) { + assert(MR != nullptr && "Not-null region expected"); MR = MR->StripCasts(); DefinedOrUnknownSVal Size = getDynamicExtent(State, MR, SVB); diff --git a/clang/lib/StaticAnalyzer/Core/Environment.cpp b/clang/lib/StaticAnalyzer/Core/Environment.cpp index 719793fa224c..3d017b81762a 100644 --- a/clang/lib/StaticAnalyzer/Core/Environment.cpp +++ b/clang/lib/StaticAnalyzer/Core/Environment.cpp @@ -274,7 +274,8 @@ void Environment::printJson(raw_ostream &Out, const ASTContext &Ctx, const Stmt *S = I->first.getStmt(); Indent(Out, InnerSpace, IsDot) - << "{ \"stmt_id\": " << S->getID(Ctx) << ", \"pretty\": "; + << "{ \"stmt_id\": " << S->getID(Ctx) << ", \"kind\": \"" + << S->getStmtClassName() << "\", \"pretty\": "; S->printJson(Out, nullptr, PP, /*AddQuotes=*/true); Out << ", \"value\": "; diff --git a/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp b/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp index 294572b7dbe4..d274d4d16db3 100644 --- a/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp @@ -25,12 +25,12 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/FoldingSet.h" -#include "llvm/ADT/Optional.h" #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/Casting.h" #include <cassert> #include <memory> +#include <optional> using namespace clang; using namespace ento; @@ -139,7 +139,7 @@ bool ExplodedGraph::shouldCollect(const ExplodedNode *node) { // Condition 10. const ProgramPoint SuccLoc = succ->getLocation(); - if (Optional<StmtPoint> SP = SuccLoc.getAs<StmtPoint>()) + if (std::optional<StmtPoint> SP = SuccLoc.getAs<StmtPoint>()) if (CallEvent::isCallStmt(SP->getStmt())) return false; diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 19149d079822..977c2b7f51fd 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -48,6 +48,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" #include "clang/StaticAnalyzer/Core/PathSensitive/LoopUnrolling.h" #include "clang/StaticAnalyzer/Core/PathSensitive/LoopWidening.h" @@ -64,7 +65,6 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/ImmutableMap.h" #include "llvm/ADT/ImmutableSet.h" -#include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Statistic.h" #include "llvm/Support/Casting.h" @@ -77,6 +77,7 @@ #include <cassert> #include <cstdint> #include <memory> +#include <optional> #include <string> #include <tuple> #include <utility> @@ -187,7 +188,7 @@ REGISTER_TRAIT_WITH_PROGRAMSTATE(ObjectsUnderConstruction, // This trait is responsible for storing the index of the element that is to be // constructed in the next iteration. As a result a CXXConstructExpr is only -// stored if it is array type. Also the index is the index of the continous +// stored if it is array type. Also the index is the index of the continuous // memory region, which is important for multi-dimensional arrays. E.g:: int // arr[2][2]; assume arr[1][1] will be the next element under construction, so // the index is 3. @@ -204,6 +205,12 @@ typedef llvm::ImmutableMap< std::pair<const CXXConstructExpr *, const LocationContext *>, unsigned> PendingInitLoopMap; REGISTER_TRAIT_WITH_PROGRAMSTATE(PendingInitLoop, PendingInitLoopMap) + +typedef llvm::ImmutableMap<const LocationContext *, unsigned> + PendingArrayDestructionMap; +REGISTER_TRAIT_WITH_PROGRAMSTATE(PendingArrayDestruction, + PendingArrayDestructionMap) + //===----------------------------------------------------------------------===// // Engine construction and deletion. //===----------------------------------------------------------------------===// @@ -263,7 +270,7 @@ ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) { svalBuilder.makeZeroVal(T), svalBuilder.getConditionType()); - Optional<DefinedOrUnknownSVal> Constraint = + std::optional<DefinedOrUnknownSVal> Constraint = Constraint_untested.getAs<DefinedOrUnknownSVal>(); if (!Constraint) @@ -283,7 +290,7 @@ ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) { const MemRegion *R = state->getRegion(SelfD, InitLoc); SVal V = state->getSVal(loc::MemRegionVal(R)); - if (Optional<Loc> LV = V.getAs<Loc>()) { + if (std::optional<Loc> LV = V.getAs<Loc>()) { // Assume that the pointer value in 'self' is non-null. state = state->assume(*LV, true); assert(state && "'self' cannot be null"); @@ -299,7 +306,7 @@ ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) { if (SFC->getParent() == nullptr) { loc::MemRegionVal L = svalBuilder.getCXXThis(MD, SFC); SVal V = state->getSVal(L); - if (Optional<Loc> LV = V.getAs<Loc>()) { + if (std::optional<Loc> LV = V.getAs<Loc>()) { state = state->assume(*LV, true); assert(state && "'this' cannot be null"); } @@ -375,7 +382,7 @@ ProgramStateRef ExprEngine::createTemporaryRegionIfNeeded( // into that region. This is not correct, but it is better than nothing. const TypedValueRegion *TR = nullptr; if (const auto *MT = dyn_cast<MaterializeTemporaryExpr>(Result)) { - if (Optional<SVal> V = getObjectUnderConstruction(State, MT, LC)) { + if (std::optional<SVal> V = getObjectUnderConstruction(State, MT, LC)) { State = finishObjectConstruction(State, MT, LC); State = State->BindExpr(Result, LC, *V); return State; @@ -470,12 +477,11 @@ ProgramStateRef ExprEngine::setIndexOfElementToConstruct( return State->set<IndexOfElementToConstruct>(Key, Idx); } -Optional<unsigned> ExprEngine::getPendingInitLoop(ProgramStateRef State, - const CXXConstructExpr *E, - const LocationContext *LCtx) { - - return Optional<unsigned>::create( - State->get<PendingInitLoop>({E, LCtx->getStackFrame()})); +std::optional<unsigned> +ExprEngine::getPendingInitLoop(ProgramStateRef State, const CXXConstructExpr *E, + const LocationContext *LCtx) { + const unsigned *V = State->get<PendingInitLoop>({E, LCtx->getStackFrame()}); + return V ? std::make_optional(*V) : std::nullopt; } ProgramStateRef ExprEngine::removePendingInitLoop(ProgramStateRef State, @@ -498,13 +504,13 @@ ProgramStateRef ExprEngine::setPendingInitLoop(ProgramStateRef State, return State->set<PendingInitLoop>(Key, Size); } -Optional<unsigned> +std::optional<unsigned> ExprEngine::getIndexOfElementToConstruct(ProgramStateRef State, const CXXConstructExpr *E, const LocationContext *LCtx) { - - return Optional<unsigned>::create( - State->get<IndexOfElementToConstruct>({E, LCtx->getStackFrame()})); + const unsigned *V = + State->get<IndexOfElementToConstruct>({E, LCtx->getStackFrame()}); + return V ? std::make_optional(*V) : std::nullopt; } ProgramStateRef @@ -517,6 +523,36 @@ ExprEngine::removeIndexOfElementToConstruct(ProgramStateRef State, return State->remove<IndexOfElementToConstruct>(Key); } +std::optional<unsigned> +ExprEngine::getPendingArrayDestruction(ProgramStateRef State, + const LocationContext *LCtx) { + assert(LCtx && "LocationContext shouldn't be null!"); + + const unsigned *V = + State->get<PendingArrayDestruction>(LCtx->getStackFrame()); + return V ? std::make_optional(*V) : std::nullopt; +} + +ProgramStateRef ExprEngine::setPendingArrayDestruction( + ProgramStateRef State, const LocationContext *LCtx, unsigned Idx) { + assert(LCtx && "LocationContext shouldn't be null!"); + + auto Key = LCtx->getStackFrame(); + + return State->set<PendingArrayDestruction>(Key, Idx); +} + +ProgramStateRef +ExprEngine::removePendingArrayDestruction(ProgramStateRef State, + const LocationContext *LCtx) { + assert(LCtx && "LocationContext shouldn't be null!"); + + auto Key = LCtx->getStackFrame(); + + assert(LCtx && State->contains<PendingArrayDestruction>(Key)); + return State->remove<PendingArrayDestruction>(Key); +} + ProgramStateRef ExprEngine::addObjectUnderConstruction(ProgramStateRef State, const ConstructionContextItem &Item, @@ -537,9 +573,10 @@ ExprEngine::addObjectUnderConstruction(ProgramStateRef State, Init = Item.getCXXCtorInitializer()->getInit(); // In an ArrayInitLoopExpr the real initializer is returned by - // getSubExpr(). + // getSubExpr(). Note that AILEs can be nested in case of + // multidimesnional arrays. if (const auto *AILE = dyn_cast_or_null<ArrayInitLoopExpr>(Init)) - Init = AILE->getSubExpr(); + Init = extractElementInitializerFromNestedAILE(AILE); // FIXME: Currently the state might already contain the marker due to // incorrect handling of temporaries bound to default parameters. @@ -557,12 +594,13 @@ ExprEngine::addObjectUnderConstruction(ProgramStateRef State, return State->set<ObjectsUnderConstruction>(Key, V); } -Optional<SVal> +std::optional<SVal> ExprEngine::getObjectUnderConstruction(ProgramStateRef State, const ConstructionContextItem &Item, const LocationContext *LC) { ConstructedObjectKey Key(Item, LC->getStackFrame()); - return Optional<SVal>::create(State->get<ObjectsUnderConstruction>(Key)); + const SVal *V = State->get<ObjectsUnderConstruction>(Key); + return V ? std::make_optional(*V) : std::nullopt; } ProgramStateRef @@ -656,7 +694,7 @@ printObjectsUnderConstructionJson(raw_ostream &Out, ProgramStateRef State, continue; if (!HasItem) { - Out << "[" << NL; + Out << '[' << NL; HasItem = true; } @@ -687,12 +725,11 @@ printObjectsUnderConstructionJson(raw_ostream &Out, ProgramStateRef State, static void printIndicesOfElementsToConstructJson( raw_ostream &Out, ProgramStateRef State, const char *NL, - const LocationContext *LCtx, const ASTContext &Context, - unsigned int Space = 0, bool IsDot = false) { + const LocationContext *LCtx, unsigned int Space = 0, bool IsDot = false) { using KeyT = std::pair<const Expr *, const LocationContext *>; - PrintingPolicy PP = - LCtx->getAnalysisDeclContext()->getASTContext().getPrintingPolicy(); + const auto &Context = LCtx->getAnalysisDeclContext()->getASTContext(); + PrintingPolicy PP = Context.getPrintingPolicy(); ++Space; bool HasItem = false; @@ -705,7 +742,7 @@ static void printIndicesOfElementsToConstructJson( continue; if (!HasItem) { - Out << "[" << NL; + Out << '[' << NL; HasItem = true; } @@ -724,17 +761,17 @@ static void printIndicesOfElementsToConstructJson( const Expr *E = Key.first; Out << "\"stmt_id\": " << E->getID(Context); - // Kind - hack to display the current index - Out << ", \"kind\": \"Cur: " << Value - 1 << "\""; + // Kind + Out << ", \"kind\": null"; // Pretty-print Out << ", \"pretty\": "; - Out << "\"" << E->getStmtClassName() << " " + Out << "\"" << E->getStmtClassName() << ' ' << E->getSourceRange().printToString(Context.getSourceManager()) << " '" << QualType::getAsString(E->getType().split(), PP); Out << "'\""; - Out << ", \"value\": \"Next: " << Value << "\" }"; + Out << ", \"value\": \"Current index: " << Value - 1 << "\" }"; if (Key != LastKey) Out << ','; @@ -748,40 +785,168 @@ static void printIndicesOfElementsToConstructJson( } } -void ExprEngine::printJson(raw_ostream &Out, ProgramStateRef State, - const LocationContext *LCtx, const char *NL, - unsigned int Space, bool IsDot) const { - Indent(Out, Space, IsDot) << "\"constructing_objects\": "; +static void printPendingInitLoopJson(raw_ostream &Out, ProgramStateRef State, + const char *NL, + const LocationContext *LCtx, + unsigned int Space = 0, + bool IsDot = false) { + using KeyT = std::pair<const CXXConstructExpr *, const LocationContext *>; - if (LCtx && !State->get<ObjectsUnderConstruction>().isEmpty()) { - ++Space; - Out << '[' << NL; - LCtx->printJson(Out, NL, Space, IsDot, [&](const LocationContext *LC) { - printObjectsUnderConstructionJson(Out, State, NL, LC, Space, IsDot); - }); + const auto &Context = LCtx->getAnalysisDeclContext()->getASTContext(); + PrintingPolicy PP = Context.getPrintingPolicy(); - --Space; - Indent(Out, Space, IsDot) << "]," << NL; // End of "constructing_objects". - } else { - Out << "null," << NL; + ++Space; + bool HasItem = false; + + // Store the last key. + KeyT LastKey; + for (const auto &I : State->get<PendingInitLoop>()) { + const KeyT &Key = I.first; + if (Key.second != LCtx) + continue; + + if (!HasItem) { + Out << '[' << NL; + HasItem = true; + } + + LastKey = Key; } - Indent(Out, Space, IsDot) << "\"index_of_element\": "; - if (LCtx && !State->get<IndexOfElementToConstruct>().isEmpty()) { - ++Space; + for (const auto &I : State->get<PendingInitLoop>()) { + const KeyT &Key = I.first; + unsigned Value = I.second; + if (Key.second != LCtx) + continue; + + Indent(Out, Space, IsDot) << "{ "; + + const CXXConstructExpr *E = Key.first; + Out << "\"stmt_id\": " << E->getID(Context); + + Out << ", \"kind\": null"; + Out << ", \"pretty\": "; + Out << '\"' << E->getStmtClassName() << ' ' + << E->getSourceRange().printToString(Context.getSourceManager()) << " '" + << QualType::getAsString(E->getType().split(), PP); + Out << "'\""; + + Out << ", \"value\": \"Flattened size: " << Value << "\"}"; + + if (Key != LastKey) + Out << ','; + Out << NL; + } + + if (HasItem) + Indent(Out, --Space, IsDot) << ']'; // End of "location_context". + else { + Out << "null "; + } +} + +static void +printPendingArrayDestructionsJson(raw_ostream &Out, ProgramStateRef State, + const char *NL, const LocationContext *LCtx, + unsigned int Space = 0, bool IsDot = false) { + using KeyT = const LocationContext *; + + ++Space; + bool HasItem = false; + + // Store the last key. + KeyT LastKey = nullptr; + for (const auto &I : State->get<PendingArrayDestruction>()) { + const KeyT &Key = I.first; + if (Key != LCtx) + continue; + + if (!HasItem) { + Out << '[' << NL; + HasItem = true; + } + + LastKey = Key; + } - auto &Context = getContext(); + for (const auto &I : State->get<PendingArrayDestruction>()) { + const KeyT &Key = I.first; + if (Key != LCtx) + continue; + + Indent(Out, Space, IsDot) << "{ "; + + Out << "\"stmt_id\": null"; + Out << ", \"kind\": null"; + Out << ", \"pretty\": \"Current index: \""; + Out << ", \"value\": \"" << I.second << "\" }"; + + if (Key != LastKey) + Out << ','; + Out << NL; + } + + if (HasItem) + Indent(Out, --Space, IsDot) << ']'; // End of "location_context". + else { + Out << "null "; + } +} + +/// A helper function to generalize program state trait printing. +/// The function invokes Printer as 'Printer(Out, State, NL, LC, Space, IsDot, +/// std::forward<Args>(args)...)'. \n One possible type for Printer is +/// 'void()(raw_ostream &, ProgramStateRef, const char *, const LocationContext +/// *, unsigned int, bool, ...)' \n \param Trait The state trait to be printed. +/// \param Printer A void function that prints Trait. +/// \param Args An additional parameter pack that is passed to Print upon +/// invocation. +template <typename Trait, typename Printer, typename... Args> +static void printStateTraitWithLocationContextJson( + raw_ostream &Out, ProgramStateRef State, const LocationContext *LCtx, + const char *NL, unsigned int Space, bool IsDot, + const char *jsonPropertyName, Printer printer, Args &&...args) { + + using RequiredType = + void (*)(raw_ostream &, ProgramStateRef, const char *, + const LocationContext *, unsigned int, bool, Args &&...); + + // Try to do as much compile time checking as possible. + // FIXME: check for invocable instead of function? + static_assert(std::is_function_v<std::remove_pointer_t<Printer>>, + "Printer is not a function!"); + static_assert(std::is_convertible_v<Printer, RequiredType>, + "Printer doesn't have the required type!"); + + if (LCtx && !State->get<Trait>().isEmpty()) { + Indent(Out, Space, IsDot) << '\"' << jsonPropertyName << "\": "; + ++Space; Out << '[' << NL; LCtx->printJson(Out, NL, Space, IsDot, [&](const LocationContext *LC) { - printIndicesOfElementsToConstructJson(Out, State, NL, LC, Context, Space, - IsDot); + printer(Out, State, NL, LC, Space, IsDot, std::forward<Args>(args)...); }); --Space; - Indent(Out, Space, IsDot) << "]," << NL; // End of "index_of_element". - } else { - Out << "null," << NL; + Indent(Out, Space, IsDot) << "]," << NL; // End of "jsonPropertyName". } +} + +void ExprEngine::printJson(raw_ostream &Out, ProgramStateRef State, + const LocationContext *LCtx, const char *NL, + unsigned int Space, bool IsDot) const { + + printStateTraitWithLocationContextJson<ObjectsUnderConstruction>( + Out, State, LCtx, NL, Space, IsDot, "constructing_objects", + printObjectsUnderConstructionJson); + printStateTraitWithLocationContextJson<IndexOfElementToConstruct>( + Out, State, LCtx, NL, Space, IsDot, "index_of_element", + printIndicesOfElementsToConstructJson); + printStateTraitWithLocationContextJson<PendingInitLoop>( + Out, State, LCtx, NL, Space, IsDot, "pending_init_loops", + printPendingInitLoopJson); + printStateTraitWithLocationContextJson<PendingArrayDestruction>( + Out, State, LCtx, NL, Space, IsDot, "pending_destructors", + printPendingArrayDestructionsJson); getCheckerManager().runCheckersForPrintStateJson(Out, State, NL, Space, IsDot); @@ -1034,7 +1199,7 @@ void ExprEngine::ProcessInitializer(const CFGInitializer CFGInit, SVal LValue = State->getSVal(Init, stackFrame); if (!Field->getType()->isReferenceType()) - if (Optional<Loc> LValueLoc = LValue.getAs<Loc>()) + if (std::optional<Loc> LValueLoc = LValue.getAs<Loc>()) InitVal = State->getSVal(*LValueLoc); // If we fail to get the value for some reason, use a symbolic value. @@ -1071,6 +1236,43 @@ void ExprEngine::ProcessInitializer(const CFGInitializer CFGInit, Engine.enqueue(Dst, currBldrCtx->getBlock(), currStmtIdx); } +std::pair<ProgramStateRef, uint64_t> +ExprEngine::prepareStateForArrayDestruction(const ProgramStateRef State, + const MemRegion *Region, + const QualType &ElementTy, + const LocationContext *LCtx, + SVal *ElementCountVal) { + assert(Region != nullptr && "Not-null region expected"); + + QualType Ty = ElementTy.getDesugaredType(getContext()); + while (const auto *NTy = dyn_cast<ArrayType>(Ty)) + Ty = NTy->getElementType().getDesugaredType(getContext()); + + auto ElementCount = getDynamicElementCount(State, Region, svalBuilder, Ty); + + if (ElementCountVal) + *ElementCountVal = ElementCount; + + // Note: the destructors are called in reverse order. + unsigned Idx = 0; + if (auto OptionalIdx = getPendingArrayDestruction(State, LCtx)) { + Idx = *OptionalIdx; + } else { + // The element count is either unknown, or an SVal that's not an integer. + if (!ElementCount.isConstant()) + return {State, 0}; + + Idx = ElementCount.getAsInteger()->getLimitedValue(); + } + + if (Idx == 0) + return {State, 0}; + + --Idx; + + return {setPendingArrayDestruction(State, LCtx, Idx), Idx}; +} + void ExprEngine::ProcessImplicitDtor(const CFGImplicitDtor D, ExplodedNode *Pred) { ExplodedNodeSet Dst; @@ -1120,11 +1322,14 @@ void ExprEngine::ProcessNewAllocator(const CXXNewExpr *NE, void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor Dtor, ExplodedNode *Pred, ExplodedNodeSet &Dst) { + const auto *DtorDecl = Dtor.getDestructorDecl(getContext()); const VarDecl *varDecl = Dtor.getVarDecl(); QualType varType = varDecl->getType(); ProgramStateRef state = Pred->getState(); - SVal dest = state->getLValue(varDecl, Pred->getLocationContext()); + const LocationContext *LCtx = Pred->getLocationContext(); + + SVal dest = state->getLValue(varDecl, LCtx); const MemRegion *Region = dest.castAs<loc::MemRegionVal>().getRegion(); if (varType->isReferenceType()) { @@ -1140,14 +1345,46 @@ void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor Dtor, varType = cast<TypedValueRegion>(Region)->getValueType(); } - // FIXME: We need to run the same destructor on every element of the array. - // This workaround will just run the first destructor (which will still - // invalidate the entire array). + unsigned Idx = 0; + if (isa<ArrayType>(varType)) { + SVal ElementCount; + std::tie(state, Idx) = prepareStateForArrayDestruction( + state, Region, varType, LCtx, &ElementCount); + + if (ElementCount.isConstant()) { + uint64_t ArrayLength = ElementCount.getAsInteger()->getLimitedValue(); + assert(ArrayLength && + "An automatic dtor for a 0 length array shouldn't be triggered!"); + + // Still handle this case if we don't have assertions enabled. + if (!ArrayLength) { + static SimpleProgramPointTag PT( + "ExprEngine", "Skipping automatic 0 length array destruction, " + "which shouldn't be in the CFG."); + PostImplicitCall PP(DtorDecl, varDecl->getLocation(), LCtx, &PT); + NodeBuilder Bldr(Pred, Dst, *currBldrCtx); + Bldr.generateSink(PP, Pred->getState(), Pred); + return; + } + } + } + EvalCallOptions CallOpts; Region = makeElementRegion(state, loc::MemRegionVal(Region), varType, - CallOpts.IsArrayCtorOrDtor) + CallOpts.IsArrayCtorOrDtor, Idx) .getAsRegion(); + NodeBuilder Bldr(Pred, Dst, getBuilderContext()); + + static SimpleProgramPointTag PT("ExprEngine", + "Prepare for object destruction"); + PreImplicitCall PP(DtorDecl, varDecl->getLocation(), LCtx, &PT); + Pred = Bldr.generateNode(PP, state, Pred); + + if (!Pred) + return; + Bldr.takeNodes(Pred); + VisitCXXDestructor(varType, Region, Dtor.getTriggerStmt(), /*IsBase=*/false, Pred, Dst, CallOpts); } @@ -1175,20 +1412,54 @@ void ExprEngine::ProcessDeleteDtor(const CFGDeleteDtor Dtor, return; } + auto getDtorDecl = [](const QualType &DTy) { + const CXXRecordDecl *RD = DTy->getAsCXXRecordDecl(); + return RD->getDestructor(); + }; + + unsigned Idx = 0; EvalCallOptions CallOpts; const MemRegion *ArgR = ArgVal.getAsRegion(); + if (DE->isArrayForm()) { - // FIXME: We need to run the same destructor on every element of the array. - // This workaround will just run the first destructor (which will still - // invalidate the entire array). CallOpts.IsArrayCtorOrDtor = true; // Yes, it may even be a multi-dimensional array. while (const auto *AT = getContext().getAsArrayType(DTy)) DTy = AT->getElementType(); - if (ArgR) - ArgR = getStoreManager().GetElementZeroRegion(cast<SubRegion>(ArgR), DTy); + + if (ArgR) { + SVal ElementCount; + std::tie(State, Idx) = prepareStateForArrayDestruction( + State, ArgR, DTy, LCtx, &ElementCount); + + // If we're about to destruct a 0 length array, don't run any of the + // destructors. + if (ElementCount.isConstant() && + ElementCount.getAsInteger()->getLimitedValue() == 0) { + + static SimpleProgramPointTag PT( + "ExprEngine", "Skipping 0 length array delete destruction"); + PostImplicitCall PP(getDtorDecl(DTy), DE->getBeginLoc(), LCtx, &PT); + NodeBuilder Bldr(Pred, Dst, *currBldrCtx); + Bldr.generateNode(PP, Pred->getState(), Pred); + return; + } + + ArgR = State->getLValue(DTy, svalBuilder.makeArrayIndex(Idx), ArgVal) + .getAsRegion(); + } } + NodeBuilder Bldr(Pred, Dst, getBuilderContext()); + static SimpleProgramPointTag PT("ExprEngine", + "Prepare for object destruction"); + PreImplicitCall PP(getDtorDecl(DTy), DE->getBeginLoc(), LCtx, &PT); + Pred = Bldr.generateNode(PP, State, Pred); + + if (!Pred) + return; + Bldr.takeNodes(Pred); + VisitCXXDestructor(DTy, ArgR, DE, /*IsBase=*/false, Pred, Dst, CallOpts); } @@ -1214,6 +1485,7 @@ void ExprEngine::ProcessBaseDtor(const CFGBaseDtor D, void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D, ExplodedNode *Pred, ExplodedNodeSet &Dst) { + const auto *DtorDecl = D.getDestructorDecl(getContext()); const FieldDecl *Member = D.getFieldDecl(); QualType T = Member->getType(); ProgramStateRef State = Pred->getState(); @@ -1225,11 +1497,44 @@ void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D, Loc ThisLoc = State->getSVal(ThisStorageLoc).castAs<Loc>(); SVal FieldVal = State->getLValue(Member, ThisLoc); - // FIXME: We need to run the same destructor on every element of the array. - // This workaround will just run the first destructor (which will still - // invalidate the entire array). + unsigned Idx = 0; + if (isa<ArrayType>(T)) { + SVal ElementCount; + std::tie(State, Idx) = prepareStateForArrayDestruction( + State, FieldVal.getAsRegion(), T, LCtx, &ElementCount); + + if (ElementCount.isConstant()) { + uint64_t ArrayLength = ElementCount.getAsInteger()->getLimitedValue(); + assert(ArrayLength && + "A member dtor for a 0 length array shouldn't be triggered!"); + + // Still handle this case if we don't have assertions enabled. + if (!ArrayLength) { + static SimpleProgramPointTag PT( + "ExprEngine", "Skipping member 0 length array destruction, which " + "shouldn't be in the CFG."); + PostImplicitCall PP(DtorDecl, Member->getLocation(), LCtx, &PT); + NodeBuilder Bldr(Pred, Dst, *currBldrCtx); + Bldr.generateSink(PP, Pred->getState(), Pred); + return; + } + } + } + EvalCallOptions CallOpts; - FieldVal = makeElementRegion(State, FieldVal, T, CallOpts.IsArrayCtorOrDtor); + FieldVal = + makeElementRegion(State, FieldVal, T, CallOpts.IsArrayCtorOrDtor, Idx); + + NodeBuilder Bldr(Pred, Dst, getBuilderContext()); + + static SimpleProgramPointTag PT("ExprEngine", + "Prepare for object destruction"); + PreImplicitCall PP(DtorDecl, Member->getLocation(), LCtx, &PT); + Pred = Bldr.generateNode(PP, State, Pred); + + if (!Pred) + return; + Bldr.takeNodes(Pred); VisitCXXDestructor(T, FieldVal.getAsRegion(), CurDtor->getBody(), /*IsBase=*/false, Pred, Dst, CallOpts); @@ -1243,9 +1548,8 @@ void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D, const LocationContext *LC = Pred->getLocationContext(); const MemRegion *MR = nullptr; - if (Optional<SVal> V = - getObjectUnderConstruction(State, D.getBindTemporaryExpr(), - Pred->getLocationContext())) { + if (std::optional<SVal> V = getObjectUnderConstruction( + State, D.getBindTemporaryExpr(), Pred->getLocationContext())) { // FIXME: Currently we insert temporary destructors for default parameters, // but we don't insert the constructors, so the entry in // ObjectsUnderConstruction may be missing. @@ -1280,15 +1584,31 @@ void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D, EvalCallOptions CallOpts; CallOpts.IsTemporaryCtorOrDtor = true; if (!MR) { - // If we have no MR, we still need to unwrap the array to avoid destroying - // the whole array at once. Regardless, we'd eventually need to model array - // destructors properly, element-by-element. + // FIXME: If we have no MR, we still need to unwrap the array to avoid + // destroying the whole array at once. + // + // For this case there is no universal solution as there is no way to + // directly create an array of temporary objects. There are some expressions + // however which can create temporary objects and have an array type. + // + // E.g.: std::initializer_list<S>{S(), S()}; + // + // The expression above has a type of 'const struct S[2]' but it's a single + // 'std::initializer_list<>'. The destructors of the 2 temporary 'S()' + // objects will be called anyway, because they are 2 separate objects in 2 + // separate clusters, i.e.: not an array. + // + // Now the 'std::initializer_list<>' is not an array either even though it + // has the type of an array. The point is, we only want to invoke the + // destructor for the initializer list once not twice or so. while (const ArrayType *AT = getContext().getAsArrayType(T)) { T = AT->getElementType(); - CallOpts.IsArrayCtorOrDtor = true; + + // FIXME: Enable this flag once we handle this case properly. + // CallOpts.IsArrayCtorOrDtor = true; } } else { - // We'd eventually need to makeElementRegion() trick here, + // FIXME: We'd eventually need to makeElementRegion() trick here, // but for now we don't have the respective construction contexts, // so MR would always be null in this case. Do nothing for now. } @@ -1426,6 +1746,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::OMPTaskyieldDirectiveClass: case Stmt::OMPBarrierDirectiveClass: case Stmt::OMPTaskwaitDirectiveClass: + case Stmt::OMPErrorDirectiveClass: case Stmt::OMPTaskgroupDirectiveClass: case Stmt::OMPFlushDirectiveClass: case Stmt::OMPDepobjDirectiveClass: @@ -1579,6 +1900,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::ConceptSpecializationExprClass: case Stmt::CXXRewrittenBinaryOperatorClass: case Stmt::RequiresExprClass: + case Expr::CXXParenListInitExprClass: // Fall through. // Cases we intentionally don't evaluate, since they don't need @@ -1638,7 +1960,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, IsTemporary = true; } - Optional<SVal> ConstantVal = svalBuilder.getConstantVal(ArgE); + std::optional<SVal> ConstantVal = svalBuilder.getConstantVal(ArgE); if (!ConstantVal) ConstantVal = UnknownVal(); @@ -1793,7 +2115,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, } } // FALLTHROUGH - LLVM_FALLTHROUGH; + [[fallthrough]]; } case Stmt::CallExprClass: @@ -2104,7 +2426,7 @@ bool ExprEngine::replayWithoutInlining(ExplodedNode *N, continue; if (L.getAs<CallEnter>()) continue; - if (Optional<StmtPoint> SP = L.getAs<StmtPoint>()) + if (std::optional<StmtPoint> SP = L.getAs<StmtPoint>()) if (SP->getStmt() == CE) continue; break; @@ -2117,8 +2439,9 @@ bool ExprEngine::replayWithoutInlining(ExplodedNode *N, // Build an Epsilon node from which we will restart the analyzes. // Note that CE is permitted to be NULL! - ProgramPoint NewNodeLoc = - EpsilonPoint(BeforeProcessingCall->getLocationContext(), CE); + static SimpleProgramPointTag PT("ExprEngine", "Replay without inlining"); + ProgramPoint NewNodeLoc = EpsilonPoint( + BeforeProcessingCall->getLocationContext(), CE, nullptr, &PT); // Add the special flag to GDM to signal retrying with no inlining. // Note, changing the state ensures that we are not going to cache out. ProgramStateRef NewNodeState = BeforeProcessingCall->getState(); @@ -2310,7 +2633,7 @@ static const Stmt *ResolveCondition(const Stmt *Condition, CFGBlock::const_reverse_iterator I = B->rbegin(), E = B->rend(); for (; I != E; ++I) { CFGElement Elem = *I; - Optional<CFGStmt> CS = Elem.getAs<CFGStmt>(); + std::optional<CFGStmt> CS = Elem.getAs<CFGStmt>(); if (!CS) continue; const Stmt *LastStmt = CS->getStmt(); @@ -2348,9 +2671,9 @@ bool ExprEngine::hasMoreIteration(ProgramStateRef State, } /// Split the state on whether there are any more iterations left for this loop. -/// Returns a (HasMoreIteration, HasNoMoreIteration) pair, or None when the -/// acquisition of the loop condition value failed. -static Optional<std::pair<ProgramStateRef, ProgramStateRef>> +/// Returns a (HasMoreIteration, HasNoMoreIteration) pair, or std::nullopt when +/// the acquisition of the loop condition value failed. +static std::optional<std::pair<ProgramStateRef, ProgramStateRef>> assumeCondition(const Stmt *Condition, ExplodedNode *N) { ProgramStateRef State = N->getState(); if (const auto *ObjCFor = dyn_cast<ObjCForCollectionStmt>(Condition)) { @@ -2389,7 +2712,7 @@ assumeCondition(const Stmt *Condition, ExplodedNode *N) { // If the condition is still unknown, give up. if (X.isUnknownOrUndef()) - return None; + return std::nullopt; DefinedSVal V = X.castAs<DefinedSVal>(); @@ -2514,7 +2837,7 @@ void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &builder) { using iterator = IndirectGotoNodeBuilder::iterator; - if (Optional<loc::GotoLabel> LV = V.getAs<loc::GotoLabel>()) { + if (std::optional<loc::GotoLabel> LV = V.getAs<loc::GotoLabel>()) { const LabelDecl *L = LV->getLabel(); for (iterator I = builder.begin(), E = builder.end(); I != E; ++I) { @@ -2666,7 +2989,7 @@ void ExprEngine::processSwitch(SwitchNodeBuilder& builder) { V2 = V1; ProgramStateRef StateCase; - if (Optional<NonLoc> NL = CondV.getAs<NonLoc>()) + if (std::optional<NonLoc> NL = CondV.getAs<NonLoc>()) std::tie(StateCase, DefaultSt) = DefaultSt->assumeInclusiveRange(*NL, V1, V2); else // UnknownVal @@ -2725,14 +3048,14 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D, const Decl *D = LocCtxt->getDecl(); const auto *MD = dyn_cast_or_null<CXXMethodDecl>(D); const auto *DeclRefEx = dyn_cast<DeclRefExpr>(Ex); - Optional<std::pair<SVal, QualType>> VInfo; + std::optional<std::pair<SVal, QualType>> VInfo; if (AMgr.options.ShouldInlineLambdas && DeclRefEx && DeclRefEx->refersToEnclosingVariableOrCapture() && MD && MD->getParent()->isLambda()) { // Lookup the field of the lambda. const CXXRecordDecl *CXXRec = MD->getParent(); - llvm::DenseMap<const VarDecl *, FieldDecl *> LambdaCaptureFields; + llvm::DenseMap<const ValueDecl *, FieldDecl *> LambdaCaptureFields; FieldDecl *LambdaThisCaptureField; CXXRec->getCaptureFields(LambdaCaptureFields, LambdaThisCaptureField); @@ -2839,6 +3162,12 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D, return; } + if (const auto *TPO = dyn_cast<TemplateParamObjectDecl>(D)) { + // FIXME: We should meaningfully implement this. + (void)TPO; + return; + } + llvm_unreachable("Support for this Decl not implemented."); } @@ -2857,7 +3186,7 @@ void ExprEngine::VisitArrayInitLoopExpr(const ArrayInitLoopExpr *Ex, for (auto *Node : CheckerPreStmt) { // The constructor visitior has already taken care of everything. - if (auto *CE = dyn_cast<CXXConstructExpr>(Ex->getSubExpr())) + if (isa<CXXConstructExpr>(Ex->getSubExpr())) break; const LocationContext *LCtx = Node->getLocationContext(); @@ -3035,6 +3364,14 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred, SVal baseExprVal = MR ? loc::MemRegionVal(MR) : state->getSVal(BaseExpr, LCtx); + // FIXME: Copied from RegionStoreManager::bind() + if (const auto *SR = + dyn_cast_or_null<SymbolicRegion>(baseExprVal.getAsRegion())) { + QualType T = SR->getPointeeStaticType(); + baseExprVal = + loc::MemRegionVal(getStoreManager().GetElementZeroRegion(SR, T)); + } + const auto *field = cast<FieldDecl>(Member); SVal L = state->getLValue(field, baseExprVal); @@ -3124,7 +3461,8 @@ ProgramStateRef ExprEngine::processPointerEscapedOnBind( for (const std::pair<SVal, SVal> &LocAndVal : LocAndVals) { // Cases (1) and (2). const MemRegion *MR = LocAndVal.first.getAsRegion(); - if (!MR || !MR->hasStackStorage()) { + if (!MR || + !isa<StackSpaceRegion, StaticGlobalSpaceRegion>(MR->getMemorySpace())) { Escaped.push_back(LocAndVal.second); continue; } @@ -3248,7 +3586,7 @@ void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE, Val, LC, /* notifyChanges = */ !atDeclInit); const MemRegion *LocReg = nullptr; - if (Optional<loc::MemRegionVal> LocRegVal = + if (std::optional<loc::MemRegionVal> LocRegVal = location.getAs<loc::MemRegionVal>()) { LocReg = LocRegVal->getRegion(); } @@ -3389,7 +3727,7 @@ void ExprEngine::evalEagerlyAssumeBinOpBifurcation(ExplodedNodeSet &Dst, ProgramStateRef state = Pred->getState(); SVal V = state->getSVal(Ex, Pred->getLocationContext()); - Optional<nonloc::SymbolVal> SEV = V.getAs<nonloc::SymbolVal>(); + std::optional<nonloc::SymbolVal> SEV = V.getAs<nonloc::SymbolVal>(); if (SEV && SEV->isExpression()) { const std::pair<const ProgramPointTag *, const ProgramPointTag*> &tags = geteagerlyAssumeBinOpBifurcationTags(); @@ -3430,7 +3768,7 @@ void ExprEngine::VisitGCCAsmStmt(const GCCAsmStmt *A, ExplodedNode *Pred, SVal X = state->getSVal(O, Pred->getLocationContext()); assert(!isa<NonLoc>(X)); // Should be an Lval, or unknown, undef. - if (Optional<Loc> LV = X.getAs<Loc>()) + if (std::optional<Loc> LV = X.getAs<Loc>()) state = state->bindLoc(*LV, UnknownVal(), Pred->getLocationContext()); } @@ -3522,7 +3860,7 @@ struct DOTGraphTraits<ExplodedGraph*> : public DefaultDOTGraphTraits { OtherNode->getLocation().printJson(Out, /*NL=*/"\\l"); Out << ", \"tag\": "; if (const ProgramPointTag *Tag = OtherNode->getLocation().getTag()) - Out << '\"' << Tag->getTagDescription() << "\""; + Out << '\"' << Tag->getTagDescription() << '\"'; else Out << "null"; Out << ", \"node_id\": " << OtherNode->getID() << diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp index 43e298f3de08..6652c065e04f 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -14,6 +14,7 @@ #include "clang/AST/DeclCXX.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include <optional> using namespace clang; using namespace ento; @@ -446,10 +447,10 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, // Check if the value being cast does not evaluates to 0. if (!val.isZeroConstant()) - if (Optional<SVal> V = + if (std::optional<SVal> V = StateMgr.getStoreManager().evalBaseToDerived(val, T)) { - val = *V; - Failed = false; + val = *V; + Failed = false; } if (Failed) { @@ -484,7 +485,7 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, resultType = getContext().getPointerType(resultType); if (!val.isConstant()) { - Optional<SVal> V = getStoreManager().evalBaseToDerived(val, T); + std::optional<SVal> V = getStoreManager().evalBaseToDerived(val, T); val = V ? *V : UnknownVal(); } @@ -524,7 +525,7 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, } // Explicitly proceed with default handler for this case cascade. } - LLVM_FALLTHROUGH; + [[fallthrough]]; // Various C++ casts that are not handled yet. case CK_ToUnion: case CK_MatrixCast: @@ -823,7 +824,7 @@ void ExprEngine::VisitGuardedExpr(const Expr *Ex, SVal V; for (CFGElement CE : llvm::reverse(*SrcBlock)) { - if (Optional<CFGStmt> CS = CE.getAs<CFGStmt>()) { + if (std::optional<CFGStmt> CS = CE.getAs<CFGStmt>()) { const Expr *ValEx = cast<Expr>(CS->getStmt()); ValEx = ValEx->IgnoreParens(); @@ -1001,7 +1002,7 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, ExplodedNode *Pred, } case UO_Plus: assert(!U->isGLValue()); - LLVM_FALLTHROUGH; + [[fallthrough]]; case UO_Deref: case UO_Extension: { handleUOExtension(I, U, Bldr); @@ -1043,16 +1044,15 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, ExplodedNode *Pred, // Note: technically we do "E == 0", but this is the same in the // transfer functions as "0 == E". SVal Result; - if (Optional<Loc> LV = V.getAs<Loc>()) { - Loc X = svalBuilder.makeNullWithType(Ex->getType()); - Result = evalBinOp(state, BO_EQ, *LV, X, U->getType()); + if (std::optional<Loc> LV = V.getAs<Loc>()) { + Loc X = svalBuilder.makeNullWithType(Ex->getType()); + Result = evalBinOp(state, BO_EQ, *LV, X, U->getType()); } else if (Ex->getType()->isFloatingType()) { - // FIXME: handle floating point types. - Result = UnknownVal(); + // FIXME: handle floating point types. + Result = UnknownVal(); } else { - nonloc::ConcreteInt X(getBasicVals().getValue(0, Ex->getType())); - Result = evalBinOp(state, BO_EQ, V.castAs<NonLoc>(), X, - U->getType()); + nonloc::ConcreteInt X(getBasicVals().getValue(0, Ex->getType())); + Result = evalBinOp(state, BO_EQ, V.castAs<NonLoc>(), X, U->getType()); } state = state->BindExpr(U, LCtx, Result); diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index 04e00274b2a7..6eb37287b136 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -10,15 +10,17 @@ // //===----------------------------------------------------------------------===// -#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" -#include "clang/Analysis/ConstructionContext.h" #include "clang/AST/DeclCXX.h" -#include "clang/AST/StmtCXX.h" #include "clang/AST/ParentMap.h" +#include "clang/AST/StmtCXX.h" +#include "clang/Analysis/ConstructionContext.h" #include "clang/Basic/PrettyStackTrace.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include <optional> using namespace clang; using namespace ento; @@ -74,7 +76,7 @@ void ExprEngine::performTrivialCopy(NodeBuilder &Bldr, ExplodedNode *Pred, // If the value being copied is not unknown, load from its location to get // an aggregate rvalue. - if (Optional<Loc> L = V.getAs<Loc>()) + if (std::optional<Loc> L = V.getAs<Loc>()) V = Pred->getState()->getSVal(*L); else assert(V.isUnknownOrUndef()); @@ -111,9 +113,15 @@ SVal ExprEngine::makeElementRegion(ProgramStateRef State, SVal LValue, return LValue; } +// In case when the prvalue is returned from the function (kind is one of +// SimpleReturnedValueKind, CXX17ElidedCopyReturnedValueKind), then +// it's materialization happens in context of the caller. +// We pass BldrCtx explicitly, as currBldrCtx always refers to callee's context. SVal ExprEngine::computeObjectUnderConstruction( - const Expr *E, ProgramStateRef State, const LocationContext *LCtx, - const ConstructionContext *CC, EvalCallOptions &CallOpts, unsigned Idx) { + const Expr *E, ProgramStateRef State, const NodeBuilderContext *BldrCtx, + const LocationContext *LCtx, const ConstructionContext *CC, + EvalCallOptions &CallOpts, unsigned Idx) { + SValBuilder &SVB = getSValBuilder(); MemRegionManager &MRMgr = SVB.getRegionManager(); ASTContext &ACtx = SVB.getContext(); @@ -171,13 +179,14 @@ SVal ExprEngine::computeObjectUnderConstruction( if (const SubRegion *MR = dyn_cast_or_null<SubRegion>(V.getAsRegion())) { if (NE->isArray()) { - // TODO: In fact, we need to call the constructor for every - // allocated element, not just the first one! CallOpts.IsArrayCtorOrDtor = true; - auto R = MRMgr.getElementRegion(NE->getType()->getPointeeType(), - svalBuilder.makeArrayIndex(Idx), MR, - SVB.getContext()); + auto Ty = NE->getType()->getPointeeType(); + while (const auto *AT = getContext().getAsArrayType(Ty)) + Ty = AT->getElementType(); + + auto R = MRMgr.getElementRegion(Ty, svalBuilder.makeArrayIndex(Idx), + MR, SVB.getContext()); return loc::MemRegionVal(R); } @@ -209,8 +218,11 @@ SVal ExprEngine::computeObjectUnderConstruction( CallerLCtx = CallerLCtx->getParent(); assert(!isa<BlockInvocationContext>(CallerLCtx)); } + + NodeBuilderContext CallerBldrCtx(getCoreEngine(), + SFC->getCallSiteBlock(), CallerLCtx); return computeObjectUnderConstruction( - cast<Expr>(SFC->getCallSite()), State, CallerLCtx, + cast<Expr>(SFC->getCallSite()), State, &CallerBldrCtx, CallerLCtx, RTC->getConstructionContext(), CallOpts); } else { // We are on the top frame of the analysis. We do not know where is the @@ -250,7 +262,7 @@ SVal ExprEngine::computeObjectUnderConstruction( EvalCallOptions PreElideCallOpts = CallOpts; SVal V = computeObjectUnderConstruction( - TCC->getConstructorAfterElision(), State, LCtx, + TCC->getConstructorAfterElision(), State, BldrCtx, LCtx, TCC->getConstructionContextAfterElision(), CallOpts); // FIXME: This definition of "copy elision has not failed" is unreliable. @@ -263,7 +275,7 @@ SVal ExprEngine::computeObjectUnderConstruction( // a simple temporary. CallOpts = PreElideCallOpts; CallOpts.IsElidableCtorThatHasNotBeenElided = true; - LLVM_FALLTHROUGH; + [[fallthrough]]; } case ConstructionContext::SimpleTemporaryObjectKind: { const auto *TCC = cast<TemporaryObjectConstructionContext>(CC); @@ -316,13 +328,13 @@ SVal ExprEngine::computeObjectUnderConstruction( unsigned Idx = ACC->getIndex(); CallEventManager &CEMgr = getStateManager().getCallEventManager(); - auto getArgLoc = [&](CallEventRef<> Caller) -> Optional<SVal> { + auto getArgLoc = [&](CallEventRef<> Caller) -> std::optional<SVal> { const LocationContext *FutureSFC = - Caller->getCalleeStackFrame(currBldrCtx->blockCount()); + Caller->getCalleeStackFrame(BldrCtx->blockCount()); // Return early if we are unable to reliably foresee // the future stack frame. if (!FutureSFC) - return None; + return std::nullopt; // This should be equivalent to Caller->getDecl() for now, but // FutureSFC->getDecl() is likely to support better stuff (like @@ -331,22 +343,22 @@ SVal ExprEngine::computeObjectUnderConstruction( // FIXME: Support for variadic arguments is not implemented here yet. if (CallEvent::isVariadic(CalleeD)) - return None; + return std::nullopt; // Operator arguments do not correspond to operator parameters // because this-argument is implemented as a normal argument in // operator call expressions but not in operator declarations. const TypedValueRegion *TVR = Caller->getParameterLocation( - *Caller->getAdjustedParameterIndex(Idx), currBldrCtx->blockCount()); + *Caller->getAdjustedParameterIndex(Idx), BldrCtx->blockCount()); if (!TVR) - return None; + return std::nullopt; return loc::MemRegionVal(TVR); }; if (const auto *CE = dyn_cast<CallExpr>(E)) { CallEventRef<> Caller = CEMgr.getSimpleCall(CE, State, LCtx); - if (Optional<SVal> V = getArgLoc(Caller)) + if (std::optional<SVal> V = getArgLoc(Caller)) return *V; else break; @@ -355,13 +367,13 @@ SVal ExprEngine::computeObjectUnderConstruction( // constructor because we won't need it. CallEventRef<> Caller = CEMgr.getCXXConstructorCall(CCE, /*Target=*/nullptr, State, LCtx); - if (Optional<SVal> V = getArgLoc(Caller)) + if (std::optional<SVal> V = getArgLoc(Caller)) return *V; else break; } else if (const auto *ME = dyn_cast<ObjCMessageExpr>(E)) { CallEventRef<> Caller = CEMgr.getObjCMethodCall(ME, State, LCtx); - if (Optional<SVal> V = getArgLoc(Caller)) + if (std::optional<SVal> V = getArgLoc(Caller)) return *V; else break; @@ -455,7 +467,7 @@ ProgramStateRef ExprEngine::updateObjectsUnderConstruction( } // If we decided not to elide the constructor, proceed as if // it's a simple temporary. - LLVM_FALLTHROUGH; + [[fallthrough]]; } case ConstructionContext::SimpleTemporaryObjectKind: { const auto *TCC = cast<TemporaryObjectConstructionContext>(CC); @@ -524,16 +536,32 @@ bindRequiredArrayElementToEnvironment(ProgramStateRef State, // | `-DeclRefExpr // `-ArrayInitIndexExpr // + // The resulting expression for a multidimensional array. + // ArrayInitLoopExpr <-- we're here + // |-OpaqueValueExpr + // | `-DeclRefExpr <-- match this + // `-ArrayInitLoopExpr + // |-OpaqueValueExpr + // | `-ArraySubscriptExpr + // | |-ImplicitCastExpr + // | | `-OpaqueValueExpr + // | | `-DeclRefExpr + // | `-ArrayInitIndexExpr + // `-CXXConstructExpr <-- extract this + // ` ... + + const auto *OVESrc = AILE->getCommonExpr()->getSourceExpr(); + // HACK: There is no way we can put the index of the array element into the // CFG unless we unroll the loop, so we manually select and bind the required // parameter to the environment. - const auto *CE = cast<CXXConstructExpr>(AILE->getSubExpr()); - const auto *OVESrc = AILE->getCommonExpr()->getSourceExpr(); + const auto *CE = + cast<CXXConstructExpr>(extractElementInitializerFromNestedAILE(AILE)); SVal Base = UnknownVal(); if (const auto *ME = dyn_cast<MemberExpr>(OVESrc)) Base = State->getSVal(ME, LCtx); - else if (const auto *DRE = cast<DeclRefExpr>(OVESrc)) + else if (const auto *DRE = dyn_cast<DeclRefExpr>(OVESrc)) Base = State->getLValue(cast<VarDecl>(DRE->getDecl()), LCtx); else llvm_unreachable("ArrayInitLoopExpr contains unexpected source expression"); @@ -556,18 +584,18 @@ void ExprEngine::handleConstructor(const Expr *E, SVal Target = UnknownVal(); if (CE) { - if (Optional<SVal> ElidedTarget = + if (std::optional<SVal> ElidedTarget = getObjectUnderConstruction(State, CE, LCtx)) { - // We've previously modeled an elidable constructor by pretending that it - // in fact constructs into the correct target. This constructor can - // therefore be skipped. - Target = *ElidedTarget; - StmtNodeBuilder Bldr(Pred, destNodes, *currBldrCtx); - State = finishObjectConstruction(State, CE, LCtx); - if (auto L = Target.getAs<Loc>()) - State = State->BindExpr(CE, LCtx, State->getSVal(*L, CE->getType())); - Bldr.generateNode(CE, Pred, State); - return; + // We've previously modeled an elidable constructor by pretending that + // it in fact constructs into the correct target. This constructor can + // therefore be skipped. + Target = *ElidedTarget; + StmtNodeBuilder Bldr(Pred, destNodes, *currBldrCtx); + State = finishObjectConstruction(State, CE, LCtx); + if (auto L = Target.getAs<Loc>()) + State = State->BindExpr(CE, LCtx, State->getSVal(*L, CE->getType())); + Bldr.generateNode(CE, Pred, State); + return; } } @@ -589,6 +617,27 @@ void ExprEngine::handleConstructor(const Expr *E, unsigned Idx = 0; if (CE->getType()->isArrayType() || AILE) { + + auto isZeroSizeArray = [&] { + uint64_t Size = 1; + + if (const auto *CAT = dyn_cast<ConstantArrayType>(CE->getType())) + Size = getContext().getConstantArrayElementCount(CAT); + else if (AILE) + Size = getContext().getArrayInitLoopExprElementCount(AILE); + + return Size == 0; + }; + + // No element construction will happen in a 0 size array. + if (isZeroSizeArray()) { + StmtNodeBuilder Bldr(Pred, destNodes, *currBldrCtx); + static SimpleProgramPointTag T{"ExprEngine", + "Skipping 0 size array construction"}; + Bldr.generateNode(CE, Pred, State, &T); + return; + } + Idx = getIndexOfElementToConstruct(State, CE, LCtx).value_or(0u); State = setIndexOfElementToConstruct(State, CE, LCtx, Idx + 1); } @@ -596,16 +645,17 @@ void ExprEngine::handleConstructor(const Expr *E, if (AILE) { // Only set this once even though we loop through it multiple times. if (!getPendingInitLoop(State, CE, LCtx)) - State = setPendingInitLoop(State, CE, LCtx, - AILE->getArraySize().getLimitedValue()); + State = setPendingInitLoop( + State, CE, LCtx, + getContext().getArrayInitLoopExprElementCount(AILE)); State = bindRequiredArrayElementToEnvironment( State, AILE, LCtx, svalBuilder.makeArrayIndex(Idx)); } // The target region is found from construction context. - std::tie(State, Target) = - handleConstructionContext(CE, State, LCtx, CC, CallOpts, Idx); + std::tie(State, Target) = handleConstructionContext( + CE, State, currBldrCtx, LCtx, CC, CallOpts, Idx); break; } case CXXConstructExpr::CK_VirtualBase: { @@ -620,7 +670,7 @@ void ExprEngine::handleConstructor(const Expr *E, ("This virtual base should have already been initialized by " "the most derived class!")); (void)OuterCtor; - LLVM_FALLTHROUGH; + [[fallthrough]]; } case CXXConstructExpr::CK_NonVirtualBase: // In C++17, classes with non-virtual bases may be aggregates, so they would @@ -640,7 +690,7 @@ void ExprEngine::handleConstructor(const Expr *E, CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion = true; break; } - LLVM_FALLTHROUGH; + [[fallthrough]]; case CXXConstructExpr::CK_Delegating: { const CXXMethodDecl *CurCtor = cast<CXXMethodDecl>(LCtx->getDecl()); Loc ThisPtr = getSValBuilder().getCXXThis(CurCtor, @@ -905,6 +955,11 @@ void ExprEngine::VisitCXXNewAllocatorCall(const CXXNewExpr *CNE, // skip it for now. ProgramStateRef State = I->getState(); SVal RetVal = State->getSVal(CNE, LCtx); + // [basic.stc.dynamic.allocation] (on the return value of an allocation + // function): + // "The order, contiguity, and initial value of storage allocated by + // successive calls to an allocation function are unspecified." + State = State->bindDefaultInitial(RetVal, UndefinedVal{}, LCtx); // If this allocation function is not declared as non-throwing, failures // /must/ be signalled by exceptions, and thus the return value will never @@ -1143,20 +1198,26 @@ void ExprEngine::VisitLambdaExpr(const LambdaExpr *LE, ExplodedNode *Pred, SVal InitVal; if (!FieldForCapture->hasCapturedVLAType()) { - Expr *InitExpr = *i; - - if (const auto AILE = dyn_cast<ArrayInitLoopExpr>(InitExpr)) { - // If the AILE initializes a POD array, we need to keep it as the - // InitExpr. - if (dyn_cast<CXXConstructExpr>(AILE->getSubExpr())) - InitExpr = AILE->getSubExpr(); - } + const Expr *InitExpr = *i; assert(InitExpr && "Capture missing initialization expression"); - if (dyn_cast<CXXConstructExpr>(InitExpr)) { - InitVal = *getObjectUnderConstruction(State, {LE, Idx}, LocCtxt); - InitVal = State->getSVal(InitVal.getAsRegion()); + // Capturing a 0 length array is a no-op, so we ignore it to get a more + // accurate analysis. If it's not ignored, it would set the default + // binding of the lambda to 'Unknown', which can lead to falsely detecting + // 'Uninitialized' values as 'Unknown' and not reporting a warning. + const auto FTy = FieldForCapture->getType(); + if (FTy->isConstantArrayType() && + getContext().getConstantArrayElementCount( + getContext().getAsConstantArrayType(FTy)) == 0) + continue; + + // With C++17 copy elision the InitExpr can be anything, so instead of + // pattern matching all cases, we simple check if the current field is + // under construction or not, regardless what it's InitExpr is. + if (const auto OUC = + getObjectUnderConstruction(State, {LE, Idx}, LocCtxt)) { + InitVal = State->getSVal(OUC->getAsRegion()); State = finishObjectConstruction(State, {LE, Idx}, LocCtxt); } else diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index 8fb2ce9cd18f..54528475cb31 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -25,6 +25,7 @@ #include "llvm/Support/Casting.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/SaveAndRestore.h" +#include <optional> using namespace clang; using namespace ento; @@ -85,10 +86,10 @@ static std::pair<const Stmt*, const ProgramPoint &PP = Node->getLocation(); if (PP.getStackFrame() == SF) { - if (Optional<StmtPoint> SP = PP.getAs<StmtPoint>()) { + if (std::optional<StmtPoint> SP = PP.getAs<StmtPoint>()) { S = SP->getStmt(); break; - } else if (Optional<CallExitEnd> CEE = PP.getAs<CallExitEnd>()) { + } else if (std::optional<CallExitEnd> CEE = PP.getAs<CallExitEnd>()) { S = CEE->getCalleeContext()->getCallSite(); if (S) break; @@ -96,17 +97,17 @@ static std::pair<const Stmt*, // If there is no statement, this is an implicitly-generated call. // We'll walk backwards over it and then continue the loop to find // an actual statement. - Optional<CallEnter> CE; + std::optional<CallEnter> CE; do { Node = Node->getFirstPred(); CE = Node->getLocationAs<CallEnter>(); } while (!CE || CE->getCalleeContext() != CEE->getCalleeContext()); // Continue searching the graph. - } else if (Optional<BlockEdge> BE = PP.getAs<BlockEdge>()) { + } else if (std::optional<BlockEdge> BE = PP.getAs<BlockEdge>()) { Blk = BE->getSrc(); } - } else if (Optional<CallEnter> CE = PP.getAs<CallEnter>()) { + } else if (std::optional<CallEnter> CE = PP.getAs<CallEnter>()) { // If we reached the CallEnter for this function, it has no statements. if (CE->getCalleeContext() == SF) break; @@ -195,6 +196,53 @@ static bool wasDifferentDeclUsedForInlining(CallEventRef<> Call, return RuntimeCallee->getCanonicalDecl() != StaticDecl->getCanonicalDecl(); } +// Returns the number of elements in the array currently being destructed. +// If the element count is not found 0 will be returned. +static unsigned getElementCountOfArrayBeingDestructed( + const CallEvent &Call, const ProgramStateRef State, SValBuilder &SVB) { + assert(isa<CXXDestructorCall>(Call) && + "The call event is not a destructor call!"); + + const auto &DtorCall = cast<CXXDestructorCall>(Call); + + auto ThisVal = DtorCall.getCXXThisVal(); + + if (auto ThisElementRegion = dyn_cast<ElementRegion>(ThisVal.getAsRegion())) { + auto ArrayRegion = ThisElementRegion->getAsArrayOffset().getRegion(); + auto ElementType = ThisElementRegion->getElementType(); + + auto ElementCount = + getDynamicElementCount(State, ArrayRegion, SVB, ElementType); + + if (!ElementCount.isConstant()) + return 0; + + return ElementCount.getAsInteger()->getLimitedValue(); + } + + return 0; +} + +ProgramStateRef ExprEngine::removeStateTraitsUsedForArrayEvaluation( + ProgramStateRef State, const CXXConstructExpr *E, + const LocationContext *LCtx) { + + assert(LCtx && "Location context must be provided!"); + + if (E) { + if (getPendingInitLoop(State, E, LCtx)) + State = removePendingInitLoop(State, E, LCtx); + + if (getIndexOfElementToConstruct(State, E, LCtx)) + State = removeIndexOfElementToConstruct(State, E, LCtx); + } + + if (getPendingArrayDestruction(State, LCtx)) + State = removePendingArrayDestruction(State, LCtx); + + return State; +} + /// The call exit is simulated with a sequence of nodes, which occur between /// CallExitBegin and CallExitEnd. The following operations occur between the /// two program points: @@ -234,6 +282,16 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) { // but we want to evaluate it as many times as many elements the array has. bool ShouldRepeatCall = false; + if (const auto *DtorDecl = + dyn_cast_or_null<CXXDestructorDecl>(Call->getDecl())) { + if (auto Idx = getPendingArrayDestruction(state, callerCtx)) { + ShouldRepeatCall = *Idx > 0; + + auto ThisVal = svalBuilder.getCXXThis(DtorDecl->getParent(), calleeCtx); + state = state->killBinding(ThisVal); + } + } + // If the callee returns an expression, bind its value to CallExpr. if (CE) { if (const ReturnStmt *RS = dyn_cast_or_null<ReturnStmt>(LastSt)) { @@ -264,14 +322,6 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) { state = state->BindExpr(CCE, callerCtx, ThisV); ShouldRepeatCall = shouldRepeatCtorCall(state, CCE, callerCtx); - - if (!ShouldRepeatCall) { - if (getIndexOfElementToConstruct(state, CCE, callerCtx)) - state = removeIndexOfElementToConstruct(state, CCE, callerCtx); - - if (getPendingInitLoop(state, CCE, callerCtx)) - state = removePendingInitLoop(state, CCE, callerCtx); - } } if (const auto *CNE = dyn_cast<CXXNewExpr>(CE)) { @@ -290,6 +340,11 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) { } } + if (!ShouldRepeatCall) { + state = removeStateTraitsUsedForArrayEvaluation( + state, dyn_cast_or_null<CXXConstructExpr>(CE), callerCtx); + } + // Step 3: BindedRetNode -> CleanedNodes // If we can find a statement and a block in the inlined function, run remove // dead bindings before returning from the call. This is important to ensure @@ -337,9 +392,8 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) { // result onto the work list. // CEENode -> Dst -> WorkList NodeBuilderContext Ctx(Engine, calleeCtx->getCallSiteBlock(), CEENode); - SaveAndRestore<const NodeBuilderContext*> NBCSave(currBldrCtx, - &Ctx); - SaveAndRestore<unsigned> CBISave(currStmtIdx, calleeCtx->getIndex()); + SaveAndRestore<const NodeBuilderContext *> NBCSave(currBldrCtx, &Ctx); + SaveAndRestore CBISave(currStmtIdx, calleeCtx->getIndex()); CallEventRef<> UpdatedCall = Call.cloneWithState(CEEState); @@ -586,8 +640,7 @@ ProgramStateRef ExprEngine::finishArgumentConstruction(ProgramStateRef State, const LocationContext *LC = Call.getLocationContext(); for (unsigned CallI = 0, CallN = Call.getNumArgs(); CallI != CallN; ++CallI) { unsigned I = Call.getASTArgumentIndex(CallI); - if (Optional<SVal> V = - getObjectUnderConstruction(State, {E, I}, LC)) { + if (std::optional<SVal> V = getObjectUnderConstruction(State, {E, I}, LC)) { SVal VV = *V; (void)VV; assert(cast<VarRegion>(VV.castAs<loc::MemRegionVal>().getRegion()) @@ -720,9 +773,9 @@ ProgramStateRef ExprEngine::bindReturnValue(const CallEvent &Call, SVal Target; assert(RTC->getStmt() == Call.getOriginExpr()); EvalCallOptions CallOpts; // FIXME: We won't really need those. - std::tie(State, Target) = - handleConstructionContext(Call.getOriginExpr(), State, LCtx, - RTC->getConstructionContext(), CallOpts); + std::tie(State, Target) = handleConstructionContext( + Call.getOriginExpr(), State, currBldrCtx, LCtx, + RTC->getConstructionContext(), CallOpts); const MemRegion *TargetR = Target.getAsRegion(); assert(TargetR); // Invalidate the region so that it didn't look uninitialized. If this is @@ -762,6 +815,11 @@ ProgramStateRef ExprEngine::bindReturnValue(const CallEvent &Call, svalBuilder.evalBinOp(State, BO_Mul, ElementCount, ElementSize, svalBuilder.getArrayIndexType()); + // FIXME: This line is to prevent a crash. For more details please check + // issue #56264. + if (Size.isUndef()) + Size = UnknownVal(); + State = setDynamicExtent(State, MR, Size.castAs<DefinedOrUnknownSVal>(), svalBuilder); } else { @@ -813,11 +871,6 @@ ExprEngine::mayInlineCallKind(const CallEvent &Call, const ExplodedNode *Pred, !Opts.MayInlineCXXAllocator) return CIP_DisallowedOnce; - // FIXME: We don't handle constructors or destructors for arrays properly. - // Even once we do, we still need to be careful about implicitly-generated - // initializers for array fields in default move/copy constructors. - // We still allow construction into ElementRegion targets when they don't - // represent array elements. if (CallOpts.IsArrayCtorOrDtor) { if (!shouldInlineArrayConstruction(Pred->getState(), CtorExpr, CurLC)) return CIP_DisallowedOnce; @@ -872,9 +925,12 @@ ExprEngine::mayInlineCallKind(const CallEvent &Call, const ExplodedNode *Pred, assert(ADC->getCFGBuildOptions().AddImplicitDtors && "No CFG destructors"); (void)ADC; - // FIXME: We don't handle destructors for arrays properly. - if (CallOpts.IsArrayCtorOrDtor) - return CIP_DisallowedOnce; + if (CallOpts.IsArrayCtorOrDtor) { + if (!shouldInlineArrayDestruction(getElementCountOfArrayBeingDestructed( + Call, Pred->getState(), svalBuilder))) { + return CIP_DisallowedOnce; + } + } // Allow disabling temporary destructor inlining with a separate option. if (CallOpts.IsTemporaryCtorOrDtor && @@ -889,7 +945,7 @@ ExprEngine::mayInlineCallKind(const CallEvent &Call, const ExplodedNode *Pred, break; } case CE_CXXDeallocator: - LLVM_FALLTHROUGH; + [[fallthrough]]; case CE_CXXAllocator: if (Opts.MayInlineCXXAllocator) break; @@ -1034,9 +1090,9 @@ bool ExprEngine::shouldInlineCall(const CallEvent &Call, const Decl *D, return false; // Check if this function has been marked as non-inlinable. - Optional<bool> MayInline = Engine.FunctionSummaries->mayInline(D); + std::optional<bool> MayInline = Engine.FunctionSummaries->mayInline(D); if (MayInline) { - if (!MayInline.value()) + if (!*MayInline) return false; } else { @@ -1091,22 +1147,36 @@ bool ExprEngine::shouldInlineArrayConstruction(const ProgramStateRef State, if (!CE) return false; - auto Type = CE->getType(); - // FIXME: Handle other arrays types. - if (const auto *CAT = dyn_cast<ConstantArrayType>(Type)) { - unsigned Size = getContext().getConstantArrayElementCount(CAT); + if (const auto *CAT = dyn_cast<ConstantArrayType>(CE->getType())) { + unsigned ArrSize = getContext().getConstantArrayElementCount(CAT); - return Size <= AMgr.options.maxBlockVisitOnPath; + // This might seem conter-intuitive at first glance, but the functions are + // closely related. Reasoning about destructors depends only on the type + // of the expression that initialized the memory region, which is the + // CXXConstructExpr. So to avoid code repetition, the work is delegated + // to the function that reasons about destructor inlining. Also note that + // if the constructors of the array elements are inlined, the destructors + // can also be inlined and if the destructors can be inline, it's safe to + // inline the constructors. + return shouldInlineArrayDestruction(ArrSize); } // Check if we're inside an ArrayInitLoopExpr, and it's sufficiently small. if (auto Size = getPendingInitLoop(State, CE, LCtx)) - return *Size <= AMgr.options.maxBlockVisitOnPath; + return shouldInlineArrayDestruction(*Size); return false; } +bool ExprEngine::shouldInlineArrayDestruction(uint64_t Size) { + + uint64_t maxAllowedSize = AMgr.options.maxBlockVisitOnPath; + + // Declaring a 0 element array is also possible. + return Size <= maxAllowedSize && Size > 0; +} + bool ExprEngine::shouldRepeatCtorCall(ProgramStateRef State, const CXXConstructExpr *E, const LocationContext *LCtx) { @@ -1189,7 +1259,12 @@ void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred, } } - // If we can't inline it, handle the return value and invalidate the regions. + // 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()); + + // Also handle the return value and invalidate the regions. conservativeEvalCall(*Call, Bldr, Pred, State); } diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp index 5a55e81497b0..25c36e9aea24 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp @@ -206,7 +206,7 @@ void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME, ExplodedNodeSet dstPostCheckers; getCheckerManager().runCheckersForObjCMessageNil(dstPostCheckers, Pred, *Msg, *this); - for (auto I : dstPostCheckers) + for (auto *I : dstPostCheckers) finishArgumentConstruction(Dst, I, *Msg); return; } @@ -270,7 +270,7 @@ void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME, // If there were constructors called for object-type arguments, clean them up. ExplodedNodeSet dstArgCleanup; - for (auto I : dstEval) + for (auto *I : dstEval) finishArgumentConstruction(dstArgCleanup, I, *Msg); ExplodedNodeSet dstPostvisit; diff --git a/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp b/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp index 506d61d94d5f..a80352816be6 100644 --- a/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp +++ b/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp @@ -17,6 +17,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/LoopUnrolling.h" +#include <optional> using namespace clang; using namespace ento; @@ -24,6 +25,7 @@ using namespace clang::ast_matchers; static const int MAXIMUM_STEP_UNROLLED = 128; +namespace { struct LoopState { private: enum Kind { Normal, Unrolled } K; @@ -56,6 +58,7 @@ public: ID.AddInteger(maxStep); } }; +} // namespace // The tracked stack of loops. The stack indicates that which loops the // simulated element contained by. The loops are marked depending if we decided @@ -175,7 +178,7 @@ static bool isCapturedByReference(ExplodedNode *N, const DeclRefExpr *DR) { const CXXRecordDecl *LambdaCXXRec = MD->getParent(); // Lookup the fields of the lambda - llvm::DenseMap<const VarDecl *, FieldDecl *> LambdaCaptureFields; + llvm::DenseMap<const ValueDecl *, FieldDecl *> LambdaCaptureFields; FieldDecl *LambdaThisCaptureField; LambdaCXXRec->getCaptureFields(LambdaCaptureFields, LambdaThisCaptureField); @@ -284,7 +287,7 @@ bool madeNewBranch(ExplodedNode *N, const Stmt *LoopStmt) { return true; ProgramPoint P = N->getLocation(); - if (Optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) + if (std::optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) S = BE->getBlock()->getTerminatorStmt(); if (S == LoopStmt) diff --git a/clang/lib/StaticAnalyzer/Core/MemRegion.cpp b/clang/lib/StaticAnalyzer/Core/MemRegion.cpp index 81c11099e93f..0c126a632f74 100644 --- a/clang/lib/StaticAnalyzer/Core/MemRegion.cpp +++ b/clang/lib/StaticAnalyzer/Core/MemRegion.cpp @@ -35,7 +35,6 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" #include "llvm/ADT/APInt.h" #include "llvm/ADT/FoldingSet.h" -#include "llvm/ADT/Optional.h" #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" @@ -51,6 +50,7 @@ #include <cstdint> #include <functional> #include <iterator> +#include <optional> #include <string> #include <tuple> #include <utility> @@ -790,18 +790,30 @@ DefinedOrUnknownSVal MemRegionManager::getStaticSize(const MemRegion *MR, return true; if (const auto *CAT = dyn_cast<ConstantArrayType>(AT)) { + using FAMKind = LangOptions::StrictFlexArraysLevelKind; + const FAMKind StrictFlexArraysLevel = + Ctx.getLangOpts().getStrictFlexArraysLevel(); + const AnalyzerOptions &Opts = SVB.getAnalyzerOptions(); const llvm::APInt &Size = CAT->getSize(); - if (Size.isZero()) - return true; - - if (getContext().getLangOpts().StrictFlexArrays >= 2) - return false; - const AnalyzerOptions &Opts = SVB.getAnalyzerOptions(); - // FIXME: this option is probably redundant with -fstrict-flex-arrays=1. - if (Opts.ShouldConsiderSingleElementArraysAsFlexibleArrayMembers && - Size.isOne()) + if (StrictFlexArraysLevel <= FAMKind::ZeroOrIncomplete && Size.isZero()) return true; + + // The "-fstrict-flex-arrays" should have precedence over + // consider-single-element-arrays-as-flexible-array-members + // analyzer-config when checking single element arrays. + if (StrictFlexArraysLevel == FAMKind::Default) { + // FIXME: After clang-17 released, we should remove this branch. + if (Opts.ShouldConsiderSingleElementArraysAsFlexibleArrayMembers && + Size.isOne()) + return true; + } else { + // -fstrict-flex-arrays was specified, since it's not the default, so + // ignore analyzer-config. + if (StrictFlexArraysLevel <= FAMKind::OneZeroOrIncomplete && + Size.isOne()) + return true; + } } return false; }; @@ -1029,7 +1041,7 @@ const VarRegion *MemRegionManager::getVarRegion(const VarDecl *D, T = getContext().VoidTy; if (!T->getAs<FunctionType>()) { FunctionProtoType::ExtProtoInfo Ext; - T = getContext().getFunctionType(T, None, Ext); + T = getContext().getFunctionType(T, std::nullopt, Ext); } T = getContext().getBlockPointerType(T); @@ -1076,14 +1088,18 @@ MemRegionManager::getBlockDataRegion(const BlockCodeRegion *BC, sReg = getGlobalsRegion(MemRegion::GlobalImmutableSpaceRegionKind); } else { - if (LC) { + bool IsArcManagedBlock = Ctx.getLangOpts().ObjCAutoRefCount; + + // ARC managed blocks can be initialized on stack or directly in heap + // depending on the implementations. So we initialize them with + // UnknownRegion. + if (!IsArcManagedBlock && LC) { // FIXME: Once we implement scope handling, we want the parent region // to be the scope. const StackFrameContext *STC = LC->getStackFrame(); assert(STC); sReg = getStackLocalsRegion(STC); - } - else { + } else { // We allow 'LC' to be NULL for cases where want BlockDataRegions // without context-sensitivity. sReg = getUnknownRegion(); @@ -1286,8 +1302,8 @@ bool MemRegion::hasGlobalsOrParametersStorage() const { return isa<StackArgumentsSpaceRegion, GlobalsSpaceRegion>(getMemorySpace()); } -// getBaseRegion strips away all elements and fields, and get the base region -// of them. +// Strips away all elements and fields. +// Returns the base region of them. const MemRegion *MemRegion::getBaseRegion() const { const MemRegion *R = this; while (true) { @@ -1307,8 +1323,7 @@ const MemRegion *MemRegion::getBaseRegion() const { return R; } -// getgetMostDerivedObjectRegion gets the region of the root class of a C++ -// class hierarchy. +// Returns the region of the root class of a C++ class hierarchy. const MemRegion *MemRegion::getMostDerivedObjectRegion() const { const MemRegion *R = this; while (const auto *BR = dyn_cast<CXXBaseObjectRegion>(R)) @@ -1482,7 +1497,7 @@ static RegionOffset calculateOffset(const MemRegion *R) { // If our base region is symbolic, we don't know what type it really is. // Pretend the type of the symbol is the true dynamic type. // (This will at least be self-consistent for the life of the symbol.) - Ty = SR->getSymbol()->getType()->getPointeeType(); + Ty = SR->getPointeeStaticType(); RootIsSymbolic = true; } @@ -1539,7 +1554,7 @@ static RegionOffset calculateOffset(const MemRegion *R) { } SVal Index = ER->getIndex(); - if (Optional<nonloc::ConcreteInt> CI = + if (std::optional<nonloc::ConcreteInt> CI = Index.getAs<nonloc::ConcreteInt>()) { // Don't bother calculating precise offsets if we already have a // symbolic offset somewhere in the chain. diff --git a/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp b/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp index d35646bfba91..a3b08d4581a5 100644 --- a/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp +++ b/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp @@ -28,6 +28,7 @@ #include "llvm/ADT/Statistic.h" #include "llvm/Support/Casting.h" #include <memory> +#include <optional> using namespace clang; using namespace ento; @@ -165,7 +166,7 @@ static void printCoverage(const PathDiagnostic *D, FIDMap &FM, llvm::raw_fd_ostream &o); -static Optional<StringRef> getExpandedMacro( +static std::optional<StringRef> getExpandedMacro( SourceLocation MacroLoc, const cross_tu::CrossTranslationUnitContext &CTU, const MacroExpansionContext &MacroExpansions, const SourceManager &SM); @@ -384,9 +385,9 @@ void PlistPrinter::ReportMacroExpansions(raw_ostream &o, unsigned indent) { SourceLocation MacroExpansionLoc = P->getLocation().asLocation().getExpansionLoc(); - const Optional<StringRef> MacroName = + const std::optional<StringRef> MacroName = MacroExpansions.getOriginalText(MacroExpansionLoc); - const Optional<StringRef> ExpansionText = + const std::optional<StringRef> ExpansionText = getExpandedMacro(MacroExpansionLoc, CTU, MacroExpansions, SM); if (!MacroName || !ExpansionText) @@ -407,11 +408,11 @@ void PlistPrinter::ReportMacroExpansions(raw_ostream &o, unsigned indent) { // Output the macro name. Indent(o, indent) << "<key>name</key>"; - EmitString(o, MacroName.value()) << '\n'; + EmitString(o, *MacroName) << '\n'; // Output what it expands into. Indent(o, indent) << "<key>expansion</key>"; - EmitString(o, ExpansionText.value()) << '\n'; + EmitString(o, *ExpansionText) << '\n'; // Finish up. --indent; @@ -825,7 +826,7 @@ void PlistDiagnostics::FlushDiagnosticsImpl( // Definitions of helper functions and methods for expanding macros. //===----------------------------------------------------------------------===// -static Optional<StringRef> +static std::optional<StringRef> getExpandedMacro(SourceLocation MacroExpansionLoc, const cross_tu::CrossTranslationUnitContext &CTU, const MacroExpansionContext &MacroExpansions, diff --git a/clang/lib/StaticAnalyzer/Core/ProgramState.cpp b/clang/lib/StaticAnalyzer/Core/ProgramState.cpp index a6d0e242924b..90ebbaad2bf3 100644 --- a/clang/lib/StaticAnalyzer/Core/ProgramState.cpp +++ b/clang/lib/StaticAnalyzer/Core/ProgramState.cpp @@ -19,6 +19,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "llvm/Support/raw_ostream.h" +#include <optional> using namespace clang; using namespace ento; @@ -216,8 +217,6 @@ ProgramState::invalidateRegionsImpl(ValueList Values, } ProgramStateRef ProgramState::killBinding(Loc LV) const { - assert(!isa<loc::MemRegionVal>(LV) && "Use invalidateRegion instead."); - Store OldStore = getStore(); const StoreRef &newStore = getStateManager().StoreMgr->killBinding(OldStore, LV); @@ -314,7 +313,7 @@ ProgramStateRef ProgramState::BindExpr(const Stmt *S, return getStateManager().getPersistentState(NewSt); } -LLVM_NODISCARD std::pair<ProgramStateRef, ProgramStateRef> +[[nodiscard]] std::pair<ProgramStateRef, ProgramStateRef> ProgramState::assumeInBoundDual(DefinedOrUnknownSVal Idx, DefinedOrUnknownSVal UpperBound, QualType indexTy) const { @@ -580,20 +579,20 @@ bool ScanReachableSymbols::scan(const SymExpr *sym) { } bool ScanReachableSymbols::scan(SVal val) { - if (Optional<loc::MemRegionVal> X = val.getAs<loc::MemRegionVal>()) + if (std::optional<loc::MemRegionVal> X = val.getAs<loc::MemRegionVal>()) return scan(X->getRegion()); - if (Optional<nonloc::LazyCompoundVal> X = + if (std::optional<nonloc::LazyCompoundVal> X = val.getAs<nonloc::LazyCompoundVal>()) return scan(*X); - if (Optional<nonloc::LocAsInteger> X = val.getAs<nonloc::LocAsInteger>()) + if (std::optional<nonloc::LocAsInteger> X = val.getAs<nonloc::LocAsInteger>()) return scan(X->getLoc()); if (SymbolRef Sym = val.getAsSymbol()) return scan(Sym); - if (Optional<nonloc::CompoundVal> X = val.getAs<nonloc::CompoundVal>()) + if (std::optional<nonloc::CompoundVal> X = val.getAs<nonloc::CompoundVal>()) return scan(*X); return true; diff --git a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index 2d4dfae1e750..1017dff2b0f3 100644 --- a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -26,6 +26,7 @@ #include "llvm/Support/raw_ostream.h" #include <algorithm> #include <iterator> +#include <optional> using namespace clang; using namespace ento; @@ -913,20 +914,20 @@ namespace { class EquivalenceClass : public llvm::FoldingSetNode { public: /// Find equivalence class for the given symbol in the given state. - LLVM_NODISCARD static inline EquivalenceClass find(ProgramStateRef State, - SymbolRef Sym); + [[nodiscard]] static inline EquivalenceClass find(ProgramStateRef State, + SymbolRef Sym); /// Merge classes for the given symbols and return a new state. - LLVM_NODISCARD static inline ProgramStateRef merge(RangeSet::Factory &F, - ProgramStateRef State, - SymbolRef First, - SymbolRef Second); + [[nodiscard]] static inline ProgramStateRef merge(RangeSet::Factory &F, + ProgramStateRef State, + SymbolRef First, + SymbolRef Second); // Merge this class with the given class and return a new state. - LLVM_NODISCARD inline ProgramStateRef + [[nodiscard]] inline ProgramStateRef merge(RangeSet::Factory &F, ProgramStateRef State, EquivalenceClass Other); /// Return a set of class members for the given state. - LLVM_NODISCARD inline SymbolSet getClassMembers(ProgramStateRef State) const; + [[nodiscard]] inline SymbolSet getClassMembers(ProgramStateRef State) const; /// Return true if the current class is trivial in the given state. /// A class is trivial if and only if there is not any member relations stored @@ -939,43 +940,42 @@ public: /// members and then during the removal of dead symbols we remove one of its /// members. In this case, the class is still non-trivial (it still has the /// mappings in ClassMembers), even though it has only one member. - LLVM_NODISCARD inline bool isTrivial(ProgramStateRef State) const; + [[nodiscard]] inline bool isTrivial(ProgramStateRef State) const; /// Return true if the current class is trivial and its only member is dead. - LLVM_NODISCARD inline bool isTriviallyDead(ProgramStateRef State, - SymbolReaper &Reaper) const; + [[nodiscard]] inline bool isTriviallyDead(ProgramStateRef State, + SymbolReaper &Reaper) const; - LLVM_NODISCARD static inline ProgramStateRef + [[nodiscard]] static inline ProgramStateRef markDisequal(RangeSet::Factory &F, ProgramStateRef State, SymbolRef First, SymbolRef Second); - LLVM_NODISCARD static inline ProgramStateRef + [[nodiscard]] static inline ProgramStateRef markDisequal(RangeSet::Factory &F, ProgramStateRef State, EquivalenceClass First, EquivalenceClass Second); - LLVM_NODISCARD inline ProgramStateRef + [[nodiscard]] inline ProgramStateRef markDisequal(RangeSet::Factory &F, ProgramStateRef State, EquivalenceClass Other) const; - LLVM_NODISCARD static inline ClassSet - getDisequalClasses(ProgramStateRef State, SymbolRef Sym); - LLVM_NODISCARD inline ClassSet - getDisequalClasses(ProgramStateRef State) const; - LLVM_NODISCARD inline ClassSet + [[nodiscard]] static inline ClassSet getDisequalClasses(ProgramStateRef State, + SymbolRef Sym); + [[nodiscard]] inline ClassSet getDisequalClasses(ProgramStateRef State) const; + [[nodiscard]] inline ClassSet getDisequalClasses(DisequalityMapTy Map, ClassSet::Factory &Factory) const; - LLVM_NODISCARD static inline Optional<bool> areEqual(ProgramStateRef State, - EquivalenceClass First, - EquivalenceClass Second); - LLVM_NODISCARD static inline Optional<bool> + [[nodiscard]] static inline std::optional<bool> + areEqual(ProgramStateRef State, EquivalenceClass First, + EquivalenceClass Second); + [[nodiscard]] static inline std::optional<bool> areEqual(ProgramStateRef State, SymbolRef First, SymbolRef Second); /// Remove one member from the class. - LLVM_NODISCARD ProgramStateRef removeMember(ProgramStateRef State, - const SymbolRef Old); + [[nodiscard]] ProgramStateRef removeMember(ProgramStateRef State, + const SymbolRef Old); /// Iterate over all symbols and try to simplify them. - LLVM_NODISCARD static inline ProgramStateRef simplify(SValBuilder &SVB, - RangeSet::Factory &F, - ProgramStateRef State, - EquivalenceClass Class); + [[nodiscard]] static inline ProgramStateRef simplify(SValBuilder &SVB, + RangeSet::Factory &F, + ProgramStateRef State, + EquivalenceClass Class); void dumpToStream(ProgramStateRef State, raw_ostream &os) const; LLVM_DUMP_METHOD void dump(ProgramStateRef State) const { @@ -983,10 +983,10 @@ public: } /// Check equivalence data for consistency. - LLVM_NODISCARD LLVM_ATTRIBUTE_UNUSED static bool + [[nodiscard]] LLVM_ATTRIBUTE_UNUSED static bool isClassDataConsistent(ProgramStateRef State); - LLVM_NODISCARD QualType getType() const { + [[nodiscard]] QualType getType() const { return getRepresentativeSymbol()->getType(); } @@ -1041,7 +1041,7 @@ private: // Constraint functions //===----------------------------------------------------------------------===// -LLVM_NODISCARD LLVM_ATTRIBUTE_UNUSED bool +[[nodiscard]] LLVM_ATTRIBUTE_UNUSED bool areFeasible(ConstraintRangeTy Constraints) { return llvm::none_of( Constraints, @@ -1050,24 +1050,24 @@ areFeasible(ConstraintRangeTy Constraints) { }); } -LLVM_NODISCARD inline const RangeSet *getConstraint(ProgramStateRef State, - EquivalenceClass Class) { +[[nodiscard]] inline const RangeSet *getConstraint(ProgramStateRef State, + EquivalenceClass Class) { return State->get<ConstraintRange>(Class); } -LLVM_NODISCARD inline const RangeSet *getConstraint(ProgramStateRef State, - SymbolRef Sym) { +[[nodiscard]] inline const RangeSet *getConstraint(ProgramStateRef State, + SymbolRef Sym) { return getConstraint(State, EquivalenceClass::find(State, Sym)); } -LLVM_NODISCARD ProgramStateRef setConstraint(ProgramStateRef State, - EquivalenceClass Class, - RangeSet Constraint) { +[[nodiscard]] ProgramStateRef setConstraint(ProgramStateRef State, + EquivalenceClass Class, + RangeSet Constraint) { return State->set<ConstraintRange>(Class, Constraint); } -LLVM_NODISCARD ProgramStateRef setConstraints(ProgramStateRef State, - ConstraintRangeTy Constraints) { +[[nodiscard]] ProgramStateRef setConstraints(ProgramStateRef State, + ConstraintRangeTy Constraints) { return State->set<ConstraintRange>(Constraints); } @@ -1084,7 +1084,7 @@ LLVM_NODISCARD ProgramStateRef setConstraints(ProgramStateRef State, /// \returns true if assuming this Sym to be true means equality of operands /// false if it means disequality of operands /// None otherwise -Optional<bool> meansEquality(const SymSymExpr *Sym) { +std::optional<bool> meansEquality(const SymSymExpr *Sym) { switch (Sym->getOpcode()) { case BO_Sub: // This case is: A - B != 0 -> disequality check. @@ -1096,7 +1096,7 @@ Optional<bool> meansEquality(const SymSymExpr *Sym) { // This case is: A != B != 0 -> diseqiality check. return false; default: - return llvm::None; + return std::nullopt; } } @@ -1105,8 +1105,8 @@ Optional<bool> meansEquality(const SymSymExpr *Sym) { //===----------------------------------------------------------------------===// template <class SecondTy, class... RestTy> -LLVM_NODISCARD inline RangeSet intersect(RangeSet::Factory &F, RangeSet Head, - SecondTy Second, RestTy... Tail); +[[nodiscard]] inline RangeSet intersect(RangeSet::Factory &F, RangeSet Head, + SecondTy Second, RestTy... Tail); template <class... RangeTy> struct IntersectionTraits; @@ -1118,7 +1118,7 @@ template <class... TailTy> struct IntersectionTraits<RangeSet, TailTy...> { template <> struct IntersectionTraits<> { // We ran out of types, and we didn't find any RangeSet, so the result should // be optional. - using Type = Optional<RangeSet>; + using Type = std::optional<RangeSet>; }; template <class OptionalOrPointer, class... TailTy> @@ -1128,32 +1128,33 @@ struct IntersectionTraits<OptionalOrPointer, TailTy...> { }; template <class EndTy> -LLVM_NODISCARD inline EndTy intersect(RangeSet::Factory &F, EndTy End) { - // If the list contains only RangeSet or Optional<RangeSet>, simply return - // that range set. +[[nodiscard]] inline EndTy intersect(RangeSet::Factory &F, EndTy End) { + // If the list contains only RangeSet or std::optional<RangeSet>, simply + // return that range set. return End; } -LLVM_NODISCARD LLVM_ATTRIBUTE_UNUSED inline Optional<RangeSet> +[[nodiscard]] LLVM_ATTRIBUTE_UNUSED inline std::optional<RangeSet> intersect(RangeSet::Factory &F, const RangeSet *End) { - // This is an extraneous conversion from a raw pointer into Optional<RangeSet> + // This is an extraneous conversion from a raw pointer into + // std::optional<RangeSet> if (End) { return *End; } - return llvm::None; + return std::nullopt; } template <class... RestTy> -LLVM_NODISCARD inline RangeSet intersect(RangeSet::Factory &F, RangeSet Head, - RangeSet Second, RestTy... Tail) { +[[nodiscard]] inline RangeSet intersect(RangeSet::Factory &F, RangeSet Head, + RangeSet Second, RestTy... Tail) { // Here we call either the <RangeSet,RangeSet,...> or <RangeSet,...> version // of the function and can be sure that the result is RangeSet. return intersect(F, F.intersect(Head, Second), Tail...); } template <class SecondTy, class... RestTy> -LLVM_NODISCARD inline RangeSet intersect(RangeSet::Factory &F, RangeSet Head, - SecondTy Second, RestTy... Tail) { +[[nodiscard]] inline RangeSet intersect(RangeSet::Factory &F, RangeSet Head, + SecondTy Second, RestTy... Tail) { if (Second) { // Here we call the <RangeSet,RangeSet,...> version of the function... return intersect(F, Head, *Second, Tail...); @@ -1165,11 +1166,12 @@ LLVM_NODISCARD inline RangeSet intersect(RangeSet::Factory &F, RangeSet Head, /// Main generic intersect function. /// It intersects all of the given range sets. If some of the given arguments -/// don't hold a range set (nullptr or llvm::None), the function will skip them. +/// don't hold a range set (nullptr or std::nullopt), the function will skip +/// them. /// /// Available representations for the arguments are: /// * RangeSet -/// * Optional<RangeSet> +/// * std::optional<RangeSet> /// * RangeSet * /// Pointer to a RangeSet is automatically assumed to be nullable and will get /// checked as well as the optional version. If this behaviour is undesired, @@ -1177,13 +1179,14 @@ LLVM_NODISCARD inline RangeSet intersect(RangeSet::Factory &F, RangeSet Head, /// /// Return type depends on the arguments' types. If we can be sure in compile /// time that there will be a range set as a result, the returning type is -/// simply RangeSet, in other cases we have to back off to Optional<RangeSet>. +/// simply RangeSet, in other cases we have to back off to +/// std::optional<RangeSet>. /// /// Please, prefer optional range sets to raw pointers. If the last argument is -/// a raw pointer and all previous arguments are None, it will cost one -/// additional check to convert RangeSet * into Optional<RangeSet>. +/// a raw pointer and all previous arguments are std::nullopt, it will cost one +/// additional check to convert RangeSet * into std::optional<RangeSet>. template <class HeadTy, class SecondTy, class... RestTy> -LLVM_NODISCARD inline +[[nodiscard]] inline typename IntersectionTraits<HeadTy, SecondTy, RestTy...>::Type intersect(RangeSet::Factory &F, HeadTy Head, SecondTy Second, RestTy... Tail) { @@ -1213,7 +1216,7 @@ public: } RangeSet VisitSymExpr(SymbolRef Sym) { - if (Optional<RangeSet> RS = getRangeForNegatedSym(Sym)) + if (std::optional<RangeSet> RS = getRangeForNegatedSym(Sym)) return *RS; // If we've reached this line, the actual type of the symbolic // expression is not supported for advanced inference. @@ -1223,7 +1226,7 @@ public: } RangeSet VisitUnarySymExpr(const UnarySymExpr *USE) { - if (Optional<RangeSet> RS = getRangeForNegatedUnarySym(USE)) + if (std::optional<RangeSet> RS = getRangeForNegatedUnarySym(USE)) return *RS; return infer(USE->getType()); } @@ -1333,18 +1336,7 @@ private: } RangeSet VisitBinaryOperator(RangeSet LHS, BinaryOperator::Opcode Op, - RangeSet RHS, QualType T) { - switch (Op) { - case BO_Or: - return VisitBinaryOperator<BO_Or>(LHS, RHS, T); - case BO_And: - return VisitBinaryOperator<BO_And>(LHS, RHS, T); - case BO_Rem: - return VisitBinaryOperator<BO_Rem>(LHS, RHS, T); - default: - return infer(T); - } - } + RangeSet RHS, QualType T); //===----------------------------------------------------------------------===// // Ranges and operators @@ -1362,11 +1354,11 @@ private: /// Try to convert given range into the given type. /// - /// It will return llvm::None only when the trivial conversion is possible. - llvm::Optional<Range> convert(const Range &Origin, APSIntType To) { + /// It will return std::nullopt only when the trivial conversion is possible. + std::optional<Range> convert(const Range &Origin, APSIntType To) { if (To.testInRange(Origin.From(), false) != APSIntType::RTR_Within || To.testInRange(Origin.To(), false) != APSIntType::RTR_Within) { - return llvm::None; + return std::nullopt; } return Range(ValueFactory.Convert(To, Origin.From()), ValueFactory.Convert(To, Origin.To())); @@ -1374,11 +1366,7 @@ private: template <BinaryOperator::Opcode Op> RangeSet VisitBinaryOperator(RangeSet LHS, RangeSet RHS, QualType T) { - // We should propagate information about unfeasbility of one of the - // operands to the resulting range. - if (LHS.isEmpty() || RHS.isEmpty()) { - return RangeFactory.getEmptySet(); - } + assert(!LHS.isEmpty() && !RHS.isEmpty()); Range CoarseLHS = fillGaps(LHS); Range CoarseRHS = fillGaps(RHS); @@ -1451,21 +1439,21 @@ private: } template <typename ProduceNegatedSymFunc> - Optional<RangeSet> getRangeForNegatedExpr(ProduceNegatedSymFunc F, - QualType T) { + std::optional<RangeSet> getRangeForNegatedExpr(ProduceNegatedSymFunc F, + QualType T) { // Do not negate if the type cannot be meaningfully negated. if (!T->isUnsignedIntegerOrEnumerationType() && !T->isSignedIntegerOrEnumerationType()) - return llvm::None; + return std::nullopt; if (SymbolRef NegatedSym = F()) if (const RangeSet *NegatedRange = getConstraint(State, NegatedSym)) return RangeFactory.negate(*NegatedRange); - return llvm::None; + return std::nullopt; } - Optional<RangeSet> getRangeForNegatedUnarySym(const UnarySymExpr *USE) { + std::optional<RangeSet> getRangeForNegatedUnarySym(const UnarySymExpr *USE) { // Just get the operand when we negate a symbol that is already negated. // -(-a) == a return getRangeForNegatedExpr( @@ -1477,7 +1465,7 @@ private: USE->getType()); } - Optional<RangeSet> getRangeForNegatedSymSym(const SymSymExpr *SSE) { + std::optional<RangeSet> getRangeForNegatedSymSym(const SymSymExpr *SSE) { return getRangeForNegatedExpr( [SSE, State = this->State]() -> SymbolRef { if (SSE->getOpcode() == BO_Sub) @@ -1488,7 +1476,7 @@ private: SSE->getType()); } - Optional<RangeSet> getRangeForNegatedSym(SymbolRef Sym) { + std::optional<RangeSet> getRangeForNegatedSym(SymbolRef Sym) { return getRangeForNegatedExpr( [Sym, State = this->State]() { return State->getSymbolManager().getUnarySymExpr(Sym, UO_Minus, @@ -1507,12 +1495,12 @@ private: // It covers all possible combinations (see CmpOpTable description). // Note that `x` and `y` can also stand for subexpressions, // not only for actual symbols. - Optional<RangeSet> getRangeForComparisonSymbol(const SymSymExpr *SSE) { + std::optional<RangeSet> getRangeForComparisonSymbol(const SymSymExpr *SSE) { const BinaryOperatorKind CurrentOP = SSE->getOpcode(); // We currently do not support <=> (C++20). if (!BinaryOperator::isComparisonOp(CurrentOP) || (CurrentOP == BO_Cmp)) - return llvm::None; + return std::nullopt; static const OperatorRelationsTable CmpOpTable{}; @@ -1582,16 +1570,16 @@ private: : getFalseRange(T); } - return llvm::None; + return std::nullopt; } - Optional<RangeSet> getRangeForEqualities(const SymSymExpr *Sym) { - Optional<bool> Equality = meansEquality(Sym); + std::optional<RangeSet> getRangeForEqualities(const SymSymExpr *Sym) { + std::optional<bool> Equality = meansEquality(Sym); if (!Equality) - return llvm::None; + return std::nullopt; - if (Optional<bool> AreEqual = + if (std::optional<bool> AreEqual = EquivalenceClass::areEqual(State, Sym->getLHS(), Sym->getRHS())) { // Here we cover two cases at once: // * if Sym is equality and its operands are known to be equal -> true @@ -1603,7 +1591,7 @@ private: return getFalseRange(Sym->getType()); } - return llvm::None; + return std::nullopt; } RangeSet getTrueRange(QualType T) { @@ -1626,6 +1614,57 @@ private: //===----------------------------------------------------------------------===// template <> +RangeSet SymbolicRangeInferrer::VisitBinaryOperator<BO_NE>(RangeSet LHS, + RangeSet RHS, + QualType T) { + assert(!LHS.isEmpty() && !RHS.isEmpty()); + + if (LHS.getAPSIntType() == RHS.getAPSIntType()) { + if (intersect(RangeFactory, LHS, RHS).isEmpty()) + return getTrueRange(T); + + } else { + // We can only lose information if we are casting smaller signed type to + // bigger unsigned type. For e.g., + // LHS (unsigned short): [2, USHRT_MAX] + // RHS (signed short): [SHRT_MIN, 0] + // + // Casting RHS to LHS type will leave us with overlapping values + // CastedRHS : [0, 0] U [SHRT_MAX + 1, USHRT_MAX] + // + // We can avoid this by checking if signed type's maximum value is lesser + // than unsigned type's minimum value. + + // If both have different signs then only we can get more information. + if (LHS.isUnsigned() != RHS.isUnsigned()) { + if (LHS.isUnsigned() && (LHS.getBitWidth() >= RHS.getBitWidth())) { + if (RHS.getMaxValue().isNegative() || + LHS.getAPSIntType().convert(RHS.getMaxValue()) < LHS.getMinValue()) + return getTrueRange(T); + + } else if (RHS.isUnsigned() && (LHS.getBitWidth() <= RHS.getBitWidth())) { + if (LHS.getMaxValue().isNegative() || + RHS.getAPSIntType().convert(LHS.getMaxValue()) < RHS.getMinValue()) + return getTrueRange(T); + } + } + + // Both RangeSets should be casted to bigger unsigned type. + APSIntType CastingType(std::max(LHS.getBitWidth(), RHS.getBitWidth()), + LHS.isUnsigned() || RHS.isUnsigned()); + + RangeSet CastedLHS = RangeFactory.castTo(LHS, CastingType); + RangeSet CastedRHS = RangeFactory.castTo(RHS, CastingType); + + if (intersect(RangeFactory, CastedLHS, CastedRHS).isEmpty()) + return getTrueRange(T); + } + + // In all other cases, the resulting range cannot be deduced. + return infer(T); +} + +template <> RangeSet SymbolicRangeInferrer::VisitBinaryOperator<BO_Or>(Range LHS, Range RHS, QualType T) { APSIntType ResultType = ValueFactory.getAPSIntType(T); @@ -1785,6 +1824,29 @@ RangeSet SymbolicRangeInferrer::VisitBinaryOperator<BO_Rem>(Range LHS, return {RangeFactory, ValueFactory.getValue(Min), ValueFactory.getValue(Max)}; } +RangeSet SymbolicRangeInferrer::VisitBinaryOperator(RangeSet LHS, + BinaryOperator::Opcode Op, + RangeSet RHS, QualType T) { + // We should propagate information about unfeasbility of one of the + // operands to the resulting range. + if (LHS.isEmpty() || RHS.isEmpty()) { + return RangeFactory.getEmptySet(); + } + + switch (Op) { + case BO_NE: + return VisitBinaryOperator<BO_NE>(LHS, RHS, T); + case BO_Or: + return VisitBinaryOperator<BO_Or>(LHS, RHS, T); + case BO_And: + return VisitBinaryOperator<BO_And>(LHS, RHS, T); + case BO_Rem: + return VisitBinaryOperator<BO_Rem>(LHS, RHS, T); + default: + return infer(T); + } +} + //===----------------------------------------------------------------------===// // Constraint manager implementation details //===----------------------------------------------------------------------===// @@ -1995,7 +2057,7 @@ public: class ConstraintAssignor : public ConstraintAssignorBase<ConstraintAssignor> { public: template <class ClassOrSymbol> - LLVM_NODISCARD static ProgramStateRef + [[nodiscard]] static ProgramStateRef assign(ProgramStateRef State, SValBuilder &Builder, RangeSet::Factory &F, ClassOrSymbol CoS, RangeSet NewConstraint) { if (!State || NewConstraint.isEmpty()) @@ -2037,7 +2099,7 @@ private: using Base = ConstraintAssignorBase<ConstraintAssignor>; /// Base method for handling new constraints for symbols. - LLVM_NODISCARD ProgramStateRef assign(SymbolRef Sym, RangeSet NewConstraint) { + [[nodiscard]] ProgramStateRef assign(SymbolRef Sym, RangeSet NewConstraint) { // All constraints are actually associated with equivalence classes, and // that's what we are going to do first. State = assign(EquivalenceClass::find(State, Sym), NewConstraint); @@ -2051,8 +2113,8 @@ private: } /// Base method for handling new constraints for classes. - LLVM_NODISCARD ProgramStateRef assign(EquivalenceClass Class, - RangeSet NewConstraint) { + [[nodiscard]] ProgramStateRef assign(EquivalenceClass Class, + RangeSet NewConstraint) { // There is a chance that we might need to update constraints for the // classes that are known to be disequal to Class. // @@ -2098,7 +2160,7 @@ private: return EquivalenceClass::merge(RangeFactory, State, LHS, RHS); } - LLVM_NODISCARD Optional<bool> interpreteAsBool(RangeSet Constraint) { + [[nodiscard]] std::optional<bool> interpreteAsBool(RangeSet Constraint) { assert(!Constraint.isEmpty() && "Empty ranges shouldn't get here"); if (Constraint.getConcreteValue()) @@ -2107,7 +2169,7 @@ private: if (!Constraint.containsZero()) return true; - return llvm::None; + return std::nullopt; } ProgramStateRef State; @@ -2115,7 +2177,6 @@ private: RangeSet::Factory &RangeFactory; }; - bool ConstraintAssignor::assignSymExprToConst(const SymExpr *Sym, const llvm::APSInt &Constraint) { llvm::SmallSet<EquivalenceClass, 4> SimplifiedClasses; @@ -2162,12 +2223,12 @@ bool ConstraintAssignor::assignSymSymExprToRangeSet(const SymSymExpr *Sym, if (!handleRemainderOp(Sym, Constraint)) return false; - Optional<bool> ConstraintAsBool = interpreteAsBool(Constraint); + std::optional<bool> ConstraintAsBool = interpreteAsBool(Constraint); if (!ConstraintAsBool) return true; - if (Optional<bool> Equality = meansEquality(Sym)) { + if (std::optional<bool> Equality = meansEquality(Sym)) { // Here we cover two cases: // * if Sym is equality and the new constraint is true -> Sym's operands // should be marked as equal @@ -2305,7 +2366,7 @@ EquivalenceClass::mergeImpl(RangeSet::Factory &RangeFactory, // // Intersection here makes perfect sense because both of these constraints // must hold for the whole new class. - if (Optional<RangeSet> NewClassConstraint = + if (std::optional<RangeSet> NewClassConstraint = intersect(RangeFactory, getConstraint(State, *this), getConstraint(State, Other))) { // NOTE: Essentially, NewClassConstraint should NEVER be infeasible because @@ -2503,16 +2564,16 @@ inline bool EquivalenceClass::addToDisequalityInfo( return true; } -inline Optional<bool> EquivalenceClass::areEqual(ProgramStateRef State, - SymbolRef FirstSym, - SymbolRef SecondSym) { +inline std::optional<bool> EquivalenceClass::areEqual(ProgramStateRef State, + SymbolRef FirstSym, + SymbolRef SecondSym) { return EquivalenceClass::areEqual(State, find(State, FirstSym), find(State, SecondSym)); } -inline Optional<bool> EquivalenceClass::areEqual(ProgramStateRef State, - EquivalenceClass First, - EquivalenceClass Second) { +inline std::optional<bool> EquivalenceClass::areEqual(ProgramStateRef State, + EquivalenceClass First, + EquivalenceClass Second) { // The same equivalence class => symbols are equal. if (First == Second) return true; @@ -2524,10 +2585,10 @@ inline Optional<bool> EquivalenceClass::areEqual(ProgramStateRef State, return false; // It is not clear. - return llvm::None; + return std::nullopt; } -LLVM_NODISCARD ProgramStateRef +[[nodiscard]] ProgramStateRef EquivalenceClass::removeMember(ProgramStateRef State, const SymbolRef Old) { SymbolSet ClsMembers = getClassMembers(State); @@ -2556,9 +2617,8 @@ EquivalenceClass::removeMember(ProgramStateRef State, const SymbolRef Old) { } // Re-evaluate an SVal with top-level `State->assume` logic. -LLVM_NODISCARD ProgramStateRef reAssume(ProgramStateRef State, - const RangeSet *Constraint, - SVal TheValue) { +[[nodiscard]] ProgramStateRef +reAssume(ProgramStateRef State, const RangeSet *Constraint, SVal TheValue) { if (!Constraint) return State; @@ -2587,7 +2647,7 @@ LLVM_NODISCARD ProgramStateRef reAssume(ProgramStateRef State, // class to this class. This way, we simplify not just the symbols but the // classes as well: we strive to keep the number of the classes to be the // absolute minimum. -LLVM_NODISCARD ProgramStateRef +[[nodiscard]] ProgramStateRef EquivalenceClass::simplify(SValBuilder &SVB, RangeSet::Factory &F, ProgramStateRef State, EquivalenceClass Class) { SymbolSet ClassMembers = Class.getClassMembers(State); @@ -2717,7 +2777,7 @@ bool EquivalenceClass::isClassDataConsistent(ProgramStateRef State) { //===----------------------------------------------------------------------===// bool RangeConstraintManager::canReasonAbout(SVal X) const { - Optional<nonloc::SymbolVal> SymVal = X.getAs<nonloc::SymbolVal>(); + std::optional<nonloc::SymbolVal> SymVal = X.getAs<nonloc::SymbolVal>(); if (SymVal && SymVal->isExpression()) { const SymExpr *SE = SymVal->getSymbol(); diff --git a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp index d8ece9f39a25..46948c12617c 100644 --- a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp +++ b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -28,8 +28,8 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "llvm/ADT/ImmutableMap.h" -#include "llvm/ADT/Optional.h" #include "llvm/Support/raw_ostream.h" +#include <optional> #include <utility> using namespace clang; @@ -212,11 +212,11 @@ public: removeBinding(R, BindingKey::Default); } - Optional<SVal> getDirectBinding(const MemRegion *R) const; + std::optional<SVal> getDirectBinding(const MemRegion *R) const; /// getDefaultBinding - Returns an SVal* representing an optional default /// binding associated with a region and its subregions. - Optional<SVal> getDefaultBinding(const MemRegion *R) const; + std::optional<SVal> getDefaultBinding(const MemRegion *R) const; /// Return the internal tree as a Store. Store asStore() const { @@ -262,12 +262,16 @@ public: typedef const RegionBindingsRef& RegionBindingsConstRef; -Optional<SVal> RegionBindingsRef::getDirectBinding(const MemRegion *R) const { - return Optional<SVal>::create(lookup(R, BindingKey::Direct)); +std::optional<SVal> +RegionBindingsRef::getDirectBinding(const MemRegion *R) const { + const SVal *V = lookup(R, BindingKey::Direct); + return V ? std::optional<SVal>(*V) : std::nullopt; } -Optional<SVal> RegionBindingsRef::getDefaultBinding(const MemRegion *R) const { - return Optional<SVal>::create(lookup(R, BindingKey::Default)); +std::optional<SVal> +RegionBindingsRef::getDefaultBinding(const MemRegion *R) const { + const SVal *V = lookup(R, BindingKey::Default); + return V ? std::optional<SVal>(*V) : std::nullopt; } RegionBindingsRef RegionBindingsRef::addBinding(BindingKey K, SVal V) const { @@ -421,10 +425,10 @@ public: RegionBindingsRef removeSubRegionBindings(RegionBindingsConstRef B, const SubRegion *R); - Optional<SVal> + std::optional<SVal> getConstantValFromConstArrayInitializer(RegionBindingsConstRef B, const ElementRegion *R); - Optional<SVal> + std::optional<SVal> getSValFromInitListExpr(const InitListExpr *ILE, const SmallVector<uint64_t, 2> &ConcreteOffsets, QualType ElemT); @@ -483,12 +487,11 @@ public: // Part of public interface to class. /// than using a Default binding at the base of the entire region. This is a /// heuristic attempting to avoid building long chains of LazyCompoundVals. /// - /// \returns The updated store bindings, or \c None if binding non-lazily - /// would be too expensive. - Optional<RegionBindingsRef> tryBindSmallStruct(RegionBindingsConstRef B, - const TypedValueRegion *R, - const RecordDecl *RD, - nonloc::LazyCompoundVal LCV); + /// \returns The updated store bindings, or \c std::nullopt if binding + /// non-lazily would be too expensive. + std::optional<RegionBindingsRef> + tryBindSmallStruct(RegionBindingsConstRef B, const TypedValueRegion *R, + const RecordDecl *RD, nonloc::LazyCompoundVal LCV); /// BindStruct - Bind a compound value to a structure. RegionBindingsRef bindStruct(RegionBindingsConstRef B, @@ -498,10 +501,9 @@ public: // Part of public interface to class. RegionBindingsRef bindVector(RegionBindingsConstRef B, const TypedValueRegion* R, SVal V); - Optional<RegionBindingsRef> tryBindSmallArray(RegionBindingsConstRef B, - const TypedValueRegion *R, - const ArrayType *AT, - nonloc::LazyCompoundVal LCV); + std::optional<RegionBindingsRef> + tryBindSmallArray(RegionBindingsConstRef B, const TypedValueRegion *R, + const ArrayType *AT, nonloc::LazyCompoundVal LCV); RegionBindingsRef bindArray(RegionBindingsConstRef B, const TypedValueRegion* R, @@ -548,7 +550,7 @@ public: // Part of public interface to class. return getBinding(getRegionBindings(S), L, T); } - Optional<SVal> getDefaultBinding(Store S, const MemRegion *R) override { + std::optional<SVal> getDefaultBinding(Store S, const MemRegion *R) override { RegionBindingsRef B = getRegionBindings(S); // Default bindings are always applied over a base region so look up the // base region's default binding, otherwise the lookup will fail when R @@ -589,10 +591,10 @@ public: // Part of public interface to class. /// /// Note that callers may need to specially handle LazyCompoundVals, which /// are returned as is in case the caller needs to treat them differently. - Optional<SVal> getBindingForDerivedDefaultValue(RegionBindingsConstRef B, - const MemRegion *superR, - const TypedValueRegion *R, - QualType Ty); + std::optional<SVal> + getBindingForDerivedDefaultValue(RegionBindingsConstRef B, + const MemRegion *superR, + const TypedValueRegion *R, QualType Ty); /// Get the state and region whose binding this region \p R corresponds to. /// @@ -608,6 +610,10 @@ public: // Part of public interface to class. /// The precise value of "interesting" is determined for the purposes of /// RegionStore's internal analysis. It must always contain all regions and /// symbols, but may omit constants and other kinds of SVal. + /// + /// In contrast to compound values, LazyCompoundVals are also added + /// to the 'interesting values' list in addition to the child interesting + /// values. const SValListTy &getInterestingValues(nonloc::LazyCompoundVal LCV); //===------------------------------------------------------------------===// @@ -857,7 +863,7 @@ collectSubRegionBindings(SmallVectorImpl<BindingPair> &Bindings, // Find the length (in bits) of the region being invalidated. uint64_t Length = UINT64_MAX; SVal Extent = Top->getMemRegionManager().getStaticSize(Top, SVB); - if (Optional<nonloc::ConcreteInt> ExtentCI = + if (std::optional<nonloc::ConcreteInt> ExtentCI = Extent.getAs<nonloc::ConcreteInt>()) { const llvm::APSInt &ExtentInt = ExtentCI->getValue(); assert(ExtentInt.isNonNegative() || ExtentInt.isUnsigned()); @@ -1029,15 +1035,14 @@ void InvalidateRegionsWorker::VisitBinding(SVal V) { } // Is it a LazyCompoundVal? All references get invalidated as well. - if (Optional<nonloc::LazyCompoundVal> LCS = + if (std::optional<nonloc::LazyCompoundVal> LCS = V.getAs<nonloc::LazyCompoundVal>()) { - const RegionStoreManager::SValListTy &Vals = RM.getInterestingValues(*LCS); - - for (RegionStoreManager::SValListTy::const_iterator I = Vals.begin(), - E = Vals.end(); - I != E; ++I) - VisitBinding(*I); + // `getInterestingValues()` returns SVals contained within LazyCompoundVals, + // so there is no need to visit them. + for (SVal V : RM.getInterestingValues(*LCS)) + if (!isa<nonloc::LazyCompoundVal>(V)) + VisitBinding(V); return; } @@ -1103,7 +1108,7 @@ void InvalidateRegionsWorker::VisitCluster(const MemRegion *baseR, // a pointer value, but the thing pointed by that pointer may // get invalidated. SVal V = RM.getBinding(B, loc::MemRegionVal(VR)); - if (Optional<Loc> L = V.getAs<Loc>()) { + if (std::optional<Loc> L = V.getAs<Loc>()) { if (const MemRegion *LR = L->getAsRegion()) AddToWorkList(LR); } @@ -1163,7 +1168,7 @@ void InvalidateRegionsWorker::VisitCluster(const MemRegion *baseR, if (doNotInvalidateSuperRegion) { // We are not doing blank invalidation of the whole array region so we // have to manually invalidate each elements. - Optional<uint64_t> NumElements; + std::optional<uint64_t> NumElements; // Compute lower and upper offsets for region within array. if (const ConstantArrayType *CAT = dyn_cast<ConstantArrayType>(AT)) @@ -1198,8 +1203,8 @@ void InvalidateRegionsWorker::VisitCluster(const MemRegion *baseR, for (ClusterBindings::iterator I = C->begin(), E = C->end(); I != E; ++I) { const BindingKey &BK = I.getKey(); - Optional<uint64_t> ROffset = - BK.hasSymbolicOffset() ? Optional<uint64_t>() : BK.getOffset(); + std::optional<uint64_t> ROffset = + BK.hasSymbolicOffset() ? std::optional<uint64_t>() : BK.getOffset(); // Check offset is not symbolic and within array's boundaries. // Handles arrays of 0 elements and of 0-sized elements as well. @@ -1287,18 +1292,13 @@ void RegionStoreManager::populateWorkList(InvalidateRegionsWorker &W, for (ArrayRef<SVal>::iterator I = Values.begin(), E = Values.end(); I != E; ++I) { SVal V = *I; - if (Optional<nonloc::LazyCompoundVal> LCS = - V.getAs<nonloc::LazyCompoundVal>()) { - - const SValListTy &Vals = getInterestingValues(*LCS); + if (std::optional<nonloc::LazyCompoundVal> LCS = + V.getAs<nonloc::LazyCompoundVal>()) { - for (SValListTy::const_iterator I = Vals.begin(), - E = Vals.end(); I != E; ++I) { - // Note: the last argument is false here because these are - // non-top-level regions. - if (const MemRegion *R = (*I).getAsRegion()) + for (SVal S : getInterestingValues(*LCS)) + if (const MemRegion *R = S.getAsRegion()) W.AddToWorkList(R); - } + continue; } @@ -1354,11 +1354,11 @@ RegionStoreManager::invalidateRegions(Store store, case GFK_All: B = invalidateGlobalRegion(MemRegion::GlobalInternalSpaceRegionKind, Ex, Count, LCtx, B, Invalidated); - LLVM_FALLTHROUGH; + [[fallthrough]]; case GFK_SystemOnly: B = invalidateGlobalRegion(MemRegion::GlobalSystemSpaceRegionKind, Ex, Count, LCtx, B, Invalidated); - LLVM_FALLTHROUGH; + [[fallthrough]]; case GFK_None: break; } @@ -1416,19 +1416,20 @@ SVal RegionStoreManager::getBinding(RegionBindingsConstRef B, Loc L, QualType T) return UnknownVal(); } - if (!isa<TypedValueRegion>(MR)) { - if (T.isNull()) { - if (const TypedRegion *TR = dyn_cast<TypedRegion>(MR)) - T = TR->getLocationType()->getPointeeType(); - else if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(MR)) - T = SR->getSymbol()->getType()->getPointeeType(); - } - assert(!T.isNull() && "Unable to auto-detect binding type!"); - assert(!T->isVoidType() && "Attempting to dereference a void pointer!"); - MR = GetElementZeroRegion(cast<SubRegion>(MR), T); - } else { - T = cast<TypedValueRegion>(MR)->getValueType(); + // Auto-detect the binding type. + if (T.isNull()) { + if (const auto *TVR = dyn_cast<TypedValueRegion>(MR)) + T = TVR->getValueType(); + else if (const auto *TR = dyn_cast<TypedRegion>(MR)) + T = TR->getLocationType()->getPointeeType(); + else if (const auto *SR = dyn_cast<SymbolicRegion>(MR)) + T = SR->getPointeeStaticType(); } + assert(!T.isNull() && "Unable to auto-detect binding type!"); + assert(!T->isVoidType() && "Attempting to dereference a void pointer!"); + + if (!isa<TypedValueRegion>(MR)) + MR = GetElementZeroRegion(cast<SubRegion>(MR), T); // FIXME: Perhaps this method should just take a 'const MemRegion*' argument // instead of 'Loc', and have the other Loc cases handled at a higher level. @@ -1537,16 +1538,17 @@ static QualType getUnderlyingType(const SubRegion *R) { /// /// Note that unlike RegionStoreManager::findLazyBinding, this will not search /// for lazy bindings for super-regions of \p R. -static Optional<nonloc::LazyCompoundVal> +static std::optional<nonloc::LazyCompoundVal> getExistingLazyBinding(SValBuilder &SVB, RegionBindingsConstRef B, const SubRegion *R, bool AllowSubregionBindings) { - Optional<SVal> V = B.getDefaultBinding(R); + std::optional<SVal> V = B.getDefaultBinding(R); if (!V) - return None; + return std::nullopt; - Optional<nonloc::LazyCompoundVal> LCV = V->getAs<nonloc::LazyCompoundVal>(); + std::optional<nonloc::LazyCompoundVal> LCV = + V->getAs<nonloc::LazyCompoundVal>(); if (!LCV) - return None; + return std::nullopt; // If the LCV is for a subregion, the types might not match, and we shouldn't // reuse the binding. @@ -1555,7 +1557,7 @@ getExistingLazyBinding(SValBuilder &SVB, RegionBindingsConstRef B, !RegionTy->isVoidPointerType()) { QualType SourceRegionTy = LCV->getRegion()->getValueType(); if (!SVB.getContext().hasSameUnqualifiedType(RegionTy, SourceRegionTy)) - return None; + return std::nullopt; } if (!AllowSubregionBindings) { @@ -1565,20 +1567,19 @@ getExistingLazyBinding(SValBuilder &SVB, RegionBindingsConstRef B, collectSubRegionBindings(Bindings, SVB, *B.lookup(R->getBaseRegion()), R, /*IncludeAllDefaultBindings=*/true); if (Bindings.size() > 1) - return None; + return std::nullopt; } return *LCV; } - std::pair<Store, const SubRegion *> RegionStoreManager::findLazyBinding(RegionBindingsConstRef B, const SubRegion *R, const SubRegion *originalRegion) { if (originalRegion != R) { - if (Optional<nonloc::LazyCompoundVal> V = - getExistingLazyBinding(svalBuilder, B, R, true)) + if (std::optional<nonloc::LazyCompoundVal> V = + getExistingLazyBinding(svalBuilder, B, R, true)) return std::make_pair(V->getStore(), V->getRegion()); } @@ -1666,7 +1667,7 @@ getElementRegionOffsetsWithBase(const ElementRegion *ER) { /// \param ArrayExtents [in] The array of extents. /// \param DstOffsets [out] The array of offsets of type `uint64_t`. /// \returns: -/// - `None` for successful convertion. +/// - `std::nullopt` for successful convertion. /// - `UndefinedVal` or `UnknownVal` otherwise. It's expected that this SVal /// will be returned as a suitable value of the access operation. /// which should be returned as a correct @@ -1675,10 +1676,10 @@ getElementRegionOffsetsWithBase(const ElementRegion *ER) { /// const int arr[10][20][30] = {}; // ArrayExtents { 10, 20, 30 } /// int x1 = arr[4][5][6]; // SrcOffsets { NonLoc(6), NonLoc(5), NonLoc(4) } /// // DstOffsets { 4, 5, 6 } -/// // returns None +/// // returns std::nullopt /// int x2 = arr[42][5][-6]; // returns UndefinedVal /// int x3 = arr[4][5][x2]; // returns UnknownVal -static Optional<SVal> +static std::optional<SVal> convertOffsetsFromSvalToUnsigneds(const SmallVector<SVal, 2> &SrcOffsets, const SmallVector<uint64_t, 2> ArrayExtents, SmallVector<uint64_t, 2> &DstOffsets) { @@ -1718,10 +1719,10 @@ convertOffsetsFromSvalToUnsigneds(const SmallVector<SVal, 2> &SrcOffsets, // account. return UnknownVal(); } - return None; + return std::nullopt; } -Optional<SVal> RegionStoreManager::getConstantValFromConstArrayInitializer( +std::optional<SVal> RegionStoreManager::getConstantValFromConstArrayInitializer( RegionBindingsConstRef B, const ElementRegion *R) { assert(R && "ElementRegion should not be null"); @@ -1731,7 +1732,7 @@ Optional<SVal> RegionStoreManager::getConstantValFromConstArrayInitializer( std::tie(SValOffsets, Base) = getElementRegionOffsetsWithBase(R); const VarRegion *VR = dyn_cast<VarRegion>(Base); if (!VR) - return None; + return std::nullopt; assert(!SValOffsets.empty() && "getElementRegionOffsets guarantees the " "offsets vector is not empty."); @@ -1742,7 +1743,7 @@ Optional<SVal> RegionStoreManager::getConstantValFromConstArrayInitializer( if (!VD->getType().isConstQualified() && !R->getElementType().isConstQualified() && (!B.isMainAnalysis() || !VD->hasGlobalStorage())) - return None; + return std::nullopt; // Array's declaration should have `ConstantArrayType` type, because only this // type contains an array extent. It may happen that array type can be of @@ -1757,13 +1758,13 @@ Optional<SVal> RegionStoreManager::getConstantValFromConstArrayInitializer( // NOTE: If `Init` is non-null, then a new `VD` is non-null for sure. So check // `Init` for null only and don't worry about the replaced `VD`. if (!Init) - return None; + return std::nullopt; // Array's declaration should have ConstantArrayType type, because only this // type contains an array extent. const ConstantArrayType *CAT = Ctx.getAsConstantArrayType(VD->getType()); if (!CAT) - return None; + return std::nullopt; // Get array extents. SmallVector<uint64_t, 2> Extents = getConstantArrayExtents(CAT); @@ -1775,11 +1776,11 @@ Optional<SVal> RegionStoreManager::getConstantValFromConstArrayInitializer( // auto x = ptr[4][2]; // UB // FIXME: Should return UndefinedVal. if (SValOffsets.size() != Extents.size()) - return None; + return std::nullopt; SmallVector<uint64_t, 2> ConcreteOffsets; - if (Optional<SVal> V = convertOffsetsFromSvalToUnsigneds(SValOffsets, Extents, - ConcreteOffsets)) + if (std::optional<SVal> V = convertOffsetsFromSvalToUnsigneds( + SValOffsets, Extents, ConcreteOffsets)) return *V; // Handle InitListExpr. @@ -1797,7 +1798,7 @@ Optional<SVal> RegionStoreManager::getConstantValFromConstArrayInitializer( // FIXME: Handle CompoundLiteralExpr. - return None; + return std::nullopt; } /// Returns an SVal, if possible, for the specified position of an @@ -1817,7 +1818,7 @@ Optional<SVal> RegionStoreManager::getConstantValFromConstArrayInitializer( /// NOTE: Inorder to get a valid SVal, a caller shall guarantee valid offsets /// for the given initialization list. Otherwise SVal can be an equivalent to 0 /// or lead to assertion. -Optional<SVal> RegionStoreManager::getSValFromInitListExpr( +std::optional<SVal> RegionStoreManager::getSValFromInitListExpr( const InitListExpr *ILE, const SmallVector<uint64_t, 2> &Offsets, QualType ElemT) { assert(ILE && "InitListExpr should not be null"); @@ -1888,7 +1889,7 @@ SVal RegionStoreManager::getSValFromStringLiteral(const StringLiteral *SL, return svalBuilder.makeIntVal(Code, ElemT); } -static Optional<SVal> getDerivedSymbolForBinding( +static std::optional<SVal> getDerivedSymbolForBinding( RegionBindingsConstRef B, const TypedValueRegion *BaseRegion, const TypedValueRegion *SubReg, const ASTContext &Ctx, SValBuilder &SVB) { assert(BaseRegion); @@ -1896,7 +1897,8 @@ static Optional<SVal> getDerivedSymbolForBinding( QualType Ty = SubReg->getValueType(); if (BaseTy->isScalarType() && Ty->isScalarType()) { if (Ctx.getTypeSizeInChars(BaseTy) >= Ctx.getTypeSizeInChars(Ty)) { - if (const Optional<SVal> &ParentValue = B.getDirectBinding(BaseRegion)) { + if (const std::optional<SVal> &ParentValue = + B.getDirectBinding(BaseRegion)) { if (SymbolRef ParentValueAsSym = ParentValue->getAsSymbol()) return SVB.getDerivedRegionValueSymbolVal(ParentValueAsSym, SubReg); @@ -1909,13 +1911,13 @@ static Optional<SVal> getDerivedSymbolForBinding( } } } - return None; + return std::nullopt; } SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B, const ElementRegion* R) { // Check if the region has a binding. - if (const Optional<SVal> &V = B.getDirectBinding(R)) + if (const std::optional<SVal> &V = B.getDirectBinding(R)) return *V; const MemRegion* superR = R->getSuperRegion(); @@ -1936,7 +1938,7 @@ SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B, return getSValFromStringLiteral(SL, Idx.getZExtValue(), T); } } else if (isa<ElementRegion, VarRegion>(superR)) { - if (Optional<SVal> V = getConstantValFromConstArrayInitializer(B, R)) + if (std::optional<SVal> V = getConstantValFromConstArrayInitializer(B, R)) return *V; } @@ -1967,7 +1969,7 @@ SVal RegionStoreManager::getBindingForField(RegionBindingsConstRef B, const FieldRegion* R) { // Check if the region has a binding. - if (const Optional<SVal> &V = B.getDirectBinding(R)) + if (const std::optional<SVal> &V = B.getDirectBinding(R)) return *V; // If the containing record was initialized, try to get its constant value. @@ -1987,7 +1989,7 @@ SVal RegionStoreManager::getBindingForField(RegionBindingsConstRef B, if (const auto *InitList = dyn_cast<InitListExpr>(Init)) { if (Index < InitList->getNumInits()) { if (const Expr *FieldInit = InitList->getInit(Index)) - if (Optional<SVal> V = svalBuilder.getConstantVal(FieldInit)) + if (std::optional<SVal> V = svalBuilder.getConstantVal(FieldInit)) return *V; } else { return svalBuilder.makeZeroVal(Ty); @@ -2018,13 +2020,11 @@ SVal RegionStoreManager::getBindingForField(RegionBindingsConstRef B, return getBindingForFieldOrElementCommon(B, R, Ty); } -Optional<SVal> -RegionStoreManager::getBindingForDerivedDefaultValue(RegionBindingsConstRef B, - const MemRegion *superR, - const TypedValueRegion *R, - QualType Ty) { +std::optional<SVal> RegionStoreManager::getBindingForDerivedDefaultValue( + RegionBindingsConstRef B, const MemRegion *superR, + const TypedValueRegion *R, QualType Ty) { - if (const Optional<SVal> &D = B.getDefaultBinding(superR)) { + if (const std::optional<SVal> &D = B.getDefaultBinding(superR)) { const SVal &val = *D; if (SymbolRef parentSym = val.getAsSymbol()) return svalBuilder.getDerivedRegionValueSymbolVal(parentSym, R); @@ -2043,7 +2043,7 @@ RegionStoreManager::getBindingForDerivedDefaultValue(RegionBindingsConstRef B, llvm_unreachable("Unknown default value"); } - return None; + return std::nullopt; } SVal RegionStoreManager::getLazyBinding(const SubRegion *LazyBindingRegion, @@ -2114,7 +2114,8 @@ RegionStoreManager::getBindingForFieldOrElementCommon(RegionBindingsConstRef B, const SubRegion *SR = R; while (SR) { const MemRegion *Base = SR->getSuperRegion(); - if (Optional<SVal> D = getBindingForDerivedDefaultValue(B, Base, R, Ty)) { + if (std::optional<SVal> D = + getBindingForDerivedDefaultValue(B, Base, R, Ty)) { if (D->getAs<nonloc::LazyCompoundVal>()) { hasPartialLazyBinding = true; break; @@ -2156,7 +2157,7 @@ RegionStoreManager::getBindingForFieldOrElementCommon(RegionBindingsConstRef B, // Try to get direct binding if all other attempts failed thus far. // Else, return UndefinedVal() if (!hasPartialLazyBinding && !isa<BlockDataRegion>(R->getBaseRegion())) { - if (const Optional<SVal> &V = B.getDefaultBinding(R)) + if (const std::optional<SVal> &V = B.getDefaultBinding(R)) return *V; return UndefinedVal(); } @@ -2169,13 +2170,13 @@ RegionStoreManager::getBindingForFieldOrElementCommon(RegionBindingsConstRef B, SVal RegionStoreManager::getBindingForObjCIvar(RegionBindingsConstRef B, const ObjCIvarRegion* R) { // Check if the region has a binding. - if (const Optional<SVal> &V = B.getDirectBinding(R)) + if (const std::optional<SVal> &V = B.getDirectBinding(R)) return *V; const MemRegion *superR = R->getSuperRegion(); // Check if the super region has a default binding. - if (const Optional<SVal> &V = B.getDefaultBinding(superR)) { + if (const std::optional<SVal> &V = B.getDefaultBinding(superR)) { if (SymbolRef parentSym = V->getAsSymbol()) return svalBuilder.getDerivedRegionValueSymbolVal(parentSym, R); @@ -2190,10 +2191,10 @@ SVal RegionStoreManager::getBindingForVar(RegionBindingsConstRef B, const VarRegion *R) { // Check if the region has a binding. - if (Optional<SVal> V = B.getDirectBinding(R)) + if (std::optional<SVal> V = B.getDirectBinding(R)) return *V; - if (Optional<SVal> V = B.getDefaultBinding(R)) + if (std::optional<SVal> V = B.getDefaultBinding(R)) return *V; // Lazily derive a value for the VarRegion. @@ -2207,7 +2208,7 @@ SVal RegionStoreManager::getBindingForVar(RegionBindingsConstRef B, // Is 'VD' declared constant? If so, retrieve the constant value. if (VD->getType().isConstQualified()) { if (const Expr *Init = VD->getAnyInitializer()) { - if (Optional<SVal> V = svalBuilder.getConstantVal(Init)) + if (std::optional<SVal> V = svalBuilder.getConstantVal(Init)) return *V; // If the variable is const qualified and has an initializer but @@ -2228,7 +2229,7 @@ SVal RegionStoreManager::getBindingForVar(RegionBindingsConstRef B, // If we're in main(), then global initializers have not become stale yet. if (B.isMainAnalysis()) if (const Expr *Init = VD->getAnyInitializer()) - if (Optional<SVal> V = svalBuilder.getConstantVal(Init)) + if (std::optional<SVal> V = svalBuilder.getConstantVal(Init)) return *V; // Function-scoped static variables are default-initialized to 0; if they @@ -2238,7 +2239,7 @@ SVal RegionStoreManager::getBindingForVar(RegionBindingsConstRef B, if (isa<StaticGlobalSpaceRegion>(MS)) return svalBuilder.makeZeroVal(T); - if (Optional<SVal> V = getBindingForDerivedDefaultValue(B, MS, R, T)) { + if (std::optional<SVal> V = getBindingForDerivedDefaultValue(B, MS, R, T)) { assert(!V->getAs<nonloc::LazyCompoundVal>()); return *V; } @@ -2283,11 +2284,9 @@ RegionStoreManager::getInterestingValues(nonloc::LazyCompoundVal LCV) { if (V.isUnknownOrUndef() || V.isConstant()) continue; - if (Optional<nonloc::LazyCompoundVal> InnerLCV = - V.getAs<nonloc::LazyCompoundVal>()) { + if (auto InnerLCV = V.getAs<nonloc::LazyCompoundVal>()) { const SValListTy &InnerList = getInterestingValues(*InnerLCV); List.insert(List.end(), InnerList.begin(), InnerList.end()); - continue; } List.push_back(V); @@ -2298,8 +2297,8 @@ RegionStoreManager::getInterestingValues(nonloc::LazyCompoundVal LCV) { NonLoc RegionStoreManager::createLazyBinding(RegionBindingsConstRef B, const TypedValueRegion *R) { - if (Optional<nonloc::LazyCompoundVal> V = - getExistingLazyBinding(svalBuilder, B, R, false)) + if (std::optional<nonloc::LazyCompoundVal> V = + getExistingLazyBinding(svalBuilder, B, R, false)) return *V; return svalBuilder.makeLazyCompoundVal(StoreRef(B.asStore(), *this), R); @@ -2359,7 +2358,7 @@ bool RegionStoreManager::includedInBindings(Store store, //===----------------------------------------------------------------------===// StoreRef RegionStoreManager::killBinding(Store ST, Loc L) { - if (Optional<loc::MemRegionVal> LV = L.getAs<loc::MemRegionVal>()) + if (std::optional<loc::MemRegionVal> LV = L.getAs<loc::MemRegionVal>()) if (const MemRegion* R = LV->getRegion()) return StoreRef(getRegionBindings(ST).removeBinding(R) .asImmutableMap() @@ -2390,22 +2389,21 @@ RegionStoreManager::bind(RegionBindingsConstRef B, Loc L, SVal V) { return bindAggregate(B, TR, V); } - if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) { - // Binding directly to a symbolic region should be treated as binding - // to element 0. - QualType T = SR->getSymbol()->getType(); - if (T->isAnyPointerType() || T->isReferenceType()) - T = T->getPointeeType(); - - R = GetElementZeroRegion(SR, T); - } + // Binding directly to a symbolic region should be treated as binding + // to element 0. + if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) + R = GetElementZeroRegion(SR, SR->getPointeeStaticType()); assert((!isa<CXXThisRegion>(R) || !B.lookup(R)) && "'this' pointer is not an l-value and is not assignable"); // Clear out bindings that may overlap with this binding. RegionBindingsRef NewB = removeSubRegionBindings(B, cast<SubRegion>(R)); - return NewB.addBinding(BindingKey::Make(R, BindingKey::Direct), V); + + // LazyCompoundVals should be always bound as 'default' bindings. + auto KeyKind = isa<nonloc::LazyCompoundVal>(V) ? BindingKey::Default + : BindingKey::Direct; + return NewB.addBinding(BindingKey::Make(R, KeyKind), V); } RegionBindingsRef @@ -2435,7 +2433,7 @@ RegionStoreManager::setImplicitDefaultValue(RegionBindingsConstRef B, return B.addBinding(R, BindingKey::Default, V); } -Optional<RegionBindingsRef> RegionStoreManager::tryBindSmallArray( +std::optional<RegionBindingsRef> RegionStoreManager::tryBindSmallArray( RegionBindingsConstRef B, const TypedValueRegion *R, const ArrayType *AT, nonloc::LazyCompoundVal LCV) { @@ -2443,16 +2441,16 @@ Optional<RegionBindingsRef> RegionStoreManager::tryBindSmallArray( // If we don't know the size, create a lazyCompoundVal instead. if (!CAT) - return None; + return std::nullopt; QualType Ty = CAT->getElementType(); if (!(Ty->isScalarType() || Ty->isReferenceType())) - return None; + return std::nullopt; // If the array is too big, create a LCV instead. uint64_t ArrSize = CAT->getSize().getLimitedValue(); if (ArrSize > SmallArrayLimit) - return None; + return std::nullopt; RegionBindingsRef NewB = B; @@ -2476,7 +2474,7 @@ RegionStoreManager::bindArray(RegionBindingsConstRef B, const ArrayType *AT =cast<ArrayType>(Ctx.getCanonicalType(R->getValueType())); QualType ElementTy = AT->getElementType(); - Optional<uint64_t> Size; + std::optional<uint64_t> Size; if (const ConstantArrayType* CAT = dyn_cast<ConstantArrayType>(AT)) Size = CAT->getSize().getZExtValue(); @@ -2484,15 +2482,16 @@ RegionStoreManager::bindArray(RegionBindingsConstRef B, // Check if the init expr is a literal. If so, bind the rvalue instead. // FIXME: It's not responsibility of the Store to transform this lvalue // to rvalue. ExprEngine or maybe even CFG should do this before binding. - if (Optional<loc::MemRegionVal> MRV = Init.getAs<loc::MemRegionVal>()) { + if (std::optional<loc::MemRegionVal> MRV = Init.getAs<loc::MemRegionVal>()) { SVal V = getBinding(B.asStore(), *MRV, R->getValueType()); return bindAggregate(B, R, V); } // Handle lazy compound values. - if (Optional<nonloc::LazyCompoundVal> LCV = + if (std::optional<nonloc::LazyCompoundVal> LCV = Init.getAs<nonloc::LazyCompoundVal>()) { - if (Optional<RegionBindingsRef> NewB = tryBindSmallArray(B, R, AT, *LCV)) + if (std::optional<RegionBindingsRef> NewB = + tryBindSmallArray(B, R, AT, *LCV)) return *NewB; return bindAggregate(B, R, Init); @@ -2573,16 +2572,14 @@ RegionBindingsRef RegionStoreManager::bindVector(RegionBindingsConstRef B, return NewB; } -Optional<RegionBindingsRef> -RegionStoreManager::tryBindSmallStruct(RegionBindingsConstRef B, - const TypedValueRegion *R, - const RecordDecl *RD, - nonloc::LazyCompoundVal LCV) { +std::optional<RegionBindingsRef> RegionStoreManager::tryBindSmallStruct( + RegionBindingsConstRef B, const TypedValueRegion *R, const RecordDecl *RD, + nonloc::LazyCompoundVal LCV) { FieldVector Fields; if (const CXXRecordDecl *Class = dyn_cast<CXXRecordDecl>(RD)) if (Class->getNumBases() != 0 || Class->getNumVBases() != 0) - return None; + return std::nullopt; for (const auto *FD : RD->fields()) { if (FD->isUnnamedBitfield()) @@ -2591,11 +2588,17 @@ RegionStoreManager::tryBindSmallStruct(RegionBindingsConstRef B, // If there are too many fields, or if any of the fields are aggregates, // just use the LCV as a default binding. if (Fields.size() == SmallStructLimit) - return None; + return std::nullopt; QualType Ty = FD->getType(); + + // Zero length arrays are basically no-ops, so we also ignore them here. + if (Ty->isConstantArrayType() && + Ctx.getConstantArrayElementCount(Ctx.getAsConstantArrayType(Ty)) == 0) + continue; + if (!(Ty->isScalarType() || Ty->isReferenceType())) - return None; + return std::nullopt; Fields.push_back(FD); } @@ -2626,9 +2629,10 @@ RegionBindingsRef RegionStoreManager::bindStruct(RegionBindingsConstRef B, return B; // Handle lazy compound values and symbolic values. - if (Optional<nonloc::LazyCompoundVal> LCV = - V.getAs<nonloc::LazyCompoundVal>()) { - if (Optional<RegionBindingsRef> NewB = tryBindSmallStruct(B, R, RD, *LCV)) + if (std::optional<nonloc::LazyCompoundVal> LCV = + V.getAs<nonloc::LazyCompoundVal>()) { + if (std::optional<RegionBindingsRef> NewB = + tryBindSmallStruct(B, R, RD, *LCV)) return *NewB; return bindAggregate(B, R, V); } @@ -2830,16 +2834,17 @@ void RemoveDeadBindingsWorker::VisitCluster(const MemRegion *baseR, } void RemoveDeadBindingsWorker::VisitBinding(SVal V) { - // Is it a LazyCompoundVal? All referenced regions are live as well. - if (Optional<nonloc::LazyCompoundVal> LCS = - V.getAs<nonloc::LazyCompoundVal>()) { - - const RegionStoreManager::SValListTy &Vals = RM.getInterestingValues(*LCS); + // Is it a LazyCompoundVal? All referenced regions are live as well. + // The LazyCompoundVal itself is not live but should be readable. + if (auto LCS = V.getAs<nonloc::LazyCompoundVal>()) { + SymReaper.markLazilyCopied(LCS->getRegion()); - for (RegionStoreManager::SValListTy::const_iterator I = Vals.begin(), - E = Vals.end(); - I != E; ++I) - VisitBinding(*I); + for (SVal V : RM.getInterestingValues(*LCS)) { + if (auto DepLCS = V.getAs<nonloc::LazyCompoundVal>()) + SymReaper.markLazilyCopied(DepLCS->getRegion()); + else + VisitBinding(V); + } return; } diff --git a/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp b/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp index d90e869196eb..fed17c77f03d 100644 --- a/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp +++ b/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp @@ -34,11 +34,10 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" #include "llvm/ADT/APSInt.h" -#include "llvm/ADT/None.h" -#include "llvm/ADT/Optional.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Compiler.h" #include <cassert> +#include <optional> #include <tuple> using namespace clang; @@ -124,7 +123,8 @@ SVal SValBuilder::convertToArrayIndex(SVal val) { return val; // Common case: we have an appropriately sized integer. - if (Optional<nonloc::ConcreteInt> CI = val.getAs<nonloc::ConcreteInt>()) { + if (std::optional<nonloc::ConcreteInt> CI = + val.getAs<nonloc::ConcreteInt>()) { const llvm::APSInt& I = CI->getValue(); if (I.getBitWidth() == ArrayIndexWidth && I.isSigned()) return val; @@ -297,11 +297,11 @@ DefinedSVal SValBuilder::getBlockPointer(const BlockDecl *block, return loc::MemRegionVal(BD); } -Optional<loc::MemRegionVal> +std::optional<loc::MemRegionVal> SValBuilder::getCastedMemRegionVal(const MemRegion *R, QualType Ty) { if (auto OptR = StateMgr.getStoreManager().castRegion(R, Ty)) return loc::MemRegionVal(*OptR); - return None; + return std::nullopt; } /// Return a memory region for the 'this' object reference. @@ -319,7 +319,7 @@ loc::MemRegionVal SValBuilder::getCXXThis(const CXXRecordDecl *D, return loc::MemRegionVal(getRegionManager().getCXXThisRegion(PT, SFC)); } -Optional<SVal> SValBuilder::getConstantVal(const Expr *E) { +std::optional<SVal> SValBuilder::getConstantVal(const Expr *E) { E = E->IgnoreParens(); switch (E->getStmtClass()) { @@ -389,21 +389,21 @@ Optional<SVal> SValBuilder::getConstantVal(const Expr *E) { case CK_NoOp: case CK_BitCast: { const Expr *SE = CE->getSubExpr(); - Optional<SVal> Val = getConstantVal(SE); + std::optional<SVal> Val = getConstantVal(SE); if (!Val) - return None; + return std::nullopt; return evalCast(*Val, CE->getType(), SE->getType()); } } // FALLTHROUGH - LLVM_FALLTHROUGH; + [[fallthrough]]; } // If we don't have a special case, fall back to the AST's constant evaluator. default: { // Don't try to come up with a value for materialized temporaries. if (E->isGLValue()) - return None; + return std::nullopt; ASTContext &Ctx = getContext(); Expr::EvalResult Result; @@ -414,7 +414,7 @@ Optional<SVal> SValBuilder::getConstantVal(const Expr *E) { if (E->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNotNull)) return makeNullWithType(E->getType()); - return None; + return std::nullopt; } } } @@ -434,11 +434,13 @@ SVal SValBuilder::makeSymExprValNN(BinaryOperator::Opcode Op, return makeNonLoc(symLHS, Op, symRHS, ResultTy); if (symLHS && symLHS->computeComplexity() < MaxComp) - if (Optional<nonloc::ConcreteInt> rInt = RHS.getAs<nonloc::ConcreteInt>()) + if (std::optional<nonloc::ConcreteInt> rInt = + RHS.getAs<nonloc::ConcreteInt>()) return makeNonLoc(symLHS, Op, rInt->getValue(), ResultTy); if (symRHS && symRHS->computeComplexity() < MaxComp) - if (Optional<nonloc::ConcreteInt> lInt = LHS.getAs<nonloc::ConcreteInt>()) + if (std::optional<nonloc::ConcreteInt> lInt = + LHS.getAs<nonloc::ConcreteInt>()) return makeNonLoc(lInt->getValue(), Op, symRHS, ResultTy); return UnknownVal(); @@ -501,14 +503,14 @@ SVal SValBuilder::evalBinOp(ProgramStateRef state, BinaryOperator::Opcode op, return UnknownVal(); } - if (Optional<Loc> LV = lhs.getAs<Loc>()) { - if (Optional<Loc> RV = rhs.getAs<Loc>()) + if (std::optional<Loc> LV = lhs.getAs<Loc>()) { + if (std::optional<Loc> RV = rhs.getAs<Loc>()) return evalBinOpLL(state, op, *LV, *RV, type); return evalBinOpLN(state, op, *LV, rhs.castAs<NonLoc>(), type); } - if (const Optional<Loc> RV = rhs.getAs<Loc>()) { + if (const std::optional<Loc> RV = rhs.getAs<Loc>()) { const auto IsCommutative = [](BinaryOperatorKind Op) { return Op == BO_Mul || Op == BO_Add || Op == BO_And || Op == BO_Xor || Op == BO_Or; diff --git a/clang/lib/StaticAnalyzer/Core/SVals.cpp b/clang/lib/StaticAnalyzer/Core/SVals.cpp index 31725926cd0d..bc9c1e40d808 100644 --- a/clang/lib/StaticAnalyzer/Core/SVals.cpp +++ b/clang/lib/StaticAnalyzer/Core/SVals.cpp @@ -25,12 +25,12 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" -#include "llvm/ADT/Optional.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" #include <cassert> +#include <optional> using namespace clang; using namespace ento; @@ -44,7 +44,7 @@ using namespace ento; //===----------------------------------------------------------------------===// const FunctionDecl *SVal::getAsFunctionDecl() const { - if (Optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>()) { + if (std::optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>()) { const MemRegion* R = X->getRegion(); if (const FunctionCodeRegion *CTR = R->getAs<FunctionCodeRegion>()) if (const auto *FD = dyn_cast<FunctionDecl>(CTR->getDecl())) @@ -78,7 +78,7 @@ SymbolRef SVal::getAsLocSymbol(bool IncludeBaseRegions) const { /// Get the symbol in the SVal or its base region. SymbolRef SVal::getLocSymbolInBase() const { - Optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>(); + std::optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>(); if (!X) return nullptr; @@ -103,7 +103,7 @@ SymbolRef SVal::getLocSymbolInBase() const { /// should continue to the base regions if the region is not symbolic. SymbolRef SVal::getAsSymbol(bool IncludeBaseRegions) const { // FIXME: should we consider SymbolRef wrapped in CodeTextRegion? - if (Optional<nonloc::SymbolVal> X = getAs<nonloc::SymbolVal>()) + if (std::optional<nonloc::SymbolVal> X = getAs<nonloc::SymbolVal>()) return X->getSymbol(); return getAsLocSymbol(IncludeBaseRegions); @@ -118,10 +118,10 @@ const llvm::APSInt *SVal::getAsInteger() const { } const MemRegion *SVal::getAsRegion() const { - if (Optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>()) + if (std::optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>()) return X->getRegion(); - if (Optional<nonloc::LocAsInteger> X = getAs<nonloc::LocAsInteger>()) + if (std::optional<nonloc::LocAsInteger> X = getAs<nonloc::LocAsInteger>()) return X->getLoc().getAsRegion(); return nullptr; @@ -251,9 +251,9 @@ bool SVal::isConstant() const { } bool SVal::isConstant(int I) const { - if (Optional<loc::ConcreteInt> LV = getAs<loc::ConcreteInt>()) + if (std::optional<loc::ConcreteInt> LV = getAs<loc::ConcreteInt>()) return LV->getValue() == I; - if (Optional<nonloc::ConcreteInt> NV = getAs<nonloc::ConcreteInt>()) + if (std::optional<nonloc::ConcreteInt> NV = getAs<nonloc::ConcreteInt>()) return NV->getValue() == I; return false; } diff --git a/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp b/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp index ad3110792592..fab520098f13 100644 --- a/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp +++ b/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp @@ -13,6 +13,8 @@ #include "clang/Analysis/MacroExpansionContext.h" #include "clang/Analysis/PathDiagnostic.h" #include "clang/Basic/FileManager.h" +#include "clang/Basic/Sarif.h" +#include "clang/Basic/SourceManager.h" #include "clang/Basic/Version.h" #include "clang/Lex/Preprocessor.h" #include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" @@ -30,10 +32,12 @@ namespace { class SarifDiagnostics : public PathDiagnosticConsumer { std::string OutputFile; const LangOptions &LO; + SarifDocumentWriter SarifWriter; public: - SarifDiagnostics(const std::string &Output, const LangOptions &LO) - : OutputFile(Output), LO(LO) {} + SarifDiagnostics(const std::string &Output, const LangOptions &LO, + const SourceManager &SM) + : OutputFile(Output), LO(LO), SarifWriter(SM) {} ~SarifDiagnostics() override = default; void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags, @@ -56,250 +60,12 @@ void ento::createSarifDiagnosticConsumer( if (Output.empty()) return; - C.push_back(new SarifDiagnostics(Output, PP.getLangOpts())); + C.push_back( + new SarifDiagnostics(Output, PP.getLangOpts(), PP.getSourceManager())); createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts), C, Output, PP, CTU, MacroExpansions); } -static StringRef getFileName(const FileEntry &FE) { - StringRef Filename = FE.tryGetRealPathName(); - if (Filename.empty()) - Filename = FE.getName(); - return Filename; -} - -static std::string percentEncodeURICharacter(char C) { - // RFC 3986 claims alpha, numeric, and this handful of - // characters are not reserved for the path component and - // should be written out directly. Otherwise, percent - // encode the character and write that out instead of the - // reserved character. - if (llvm::isAlnum(C) || - StringRef::npos != StringRef("-._~:@!$&'()*+,;=").find(C)) - return std::string(&C, 1); - return "%" + llvm::toHex(StringRef(&C, 1)); -} - -static std::string fileNameToURI(StringRef Filename) { - llvm::SmallString<32> Ret = StringRef("file://"); - - // Get the root name to see if it has a URI authority. - StringRef Root = sys::path::root_name(Filename); - if (Root.startswith("//")) { - // There is an authority, so add it to the URI. - Ret += Root.drop_front(2).str(); - } else if (!Root.empty()) { - // There is no authority, so end the component and add the root to the URI. - Ret += Twine("/" + Root).str(); - } - - auto Iter = sys::path::begin(Filename), End = sys::path::end(Filename); - assert(Iter != End && "Expected there to be a non-root path component."); - // Add the rest of the path components, encoding any reserved characters; - // we skip past the first path component, as it was handled it above. - for (StringRef Component : llvm::make_range(++Iter, End)) { - // For reasons unknown to me, we may get a backslash with Windows native - // paths for the initial backslash following the drive component, which - // we need to ignore as a URI path part. - if (Component == "\\") - continue; - - // Add the separator between the previous path part and the one being - // currently processed. - Ret += "/"; - - // URI encode the part. - for (char C : Component) { - Ret += percentEncodeURICharacter(C); - } - } - - return std::string(Ret); -} - -static json::Object createArtifactLocation(const FileEntry &FE) { - return json::Object{{"uri", fileNameToURI(getFileName(FE))}}; -} - -static json::Object createArtifact(const FileEntry &FE) { - return json::Object{{"location", createArtifactLocation(FE)}, - {"roles", json::Array{"resultFile"}}, - {"length", FE.getSize()}, - {"mimeType", "text/plain"}}; -} - -static json::Object createArtifactLocation(const FileEntry &FE, - json::Array &Artifacts) { - std::string FileURI = fileNameToURI(getFileName(FE)); - - // See if the Artifacts array contains this URI already. If it does not, - // create a new artifact object to add to the array. - auto I = llvm::find_if(Artifacts, [&](const json::Value &File) { - if (const json::Object *Obj = File.getAsObject()) { - if (const json::Object *FileLoc = Obj->getObject("location")) { - Optional<StringRef> URI = FileLoc->getString("uri"); - return URI && URI->equals(FileURI); - } - } - return false; - }); - - // Calculate the index within the artifact array so it can be stored in - // the JSON object. - auto Index = static_cast<unsigned>(std::distance(Artifacts.begin(), I)); - if (I == Artifacts.end()) - Artifacts.push_back(createArtifact(FE)); - - return json::Object{{"uri", FileURI}, {"index", Index}}; -} - -static unsigned int adjustColumnPos(const SourceManager &SM, SourceLocation Loc, - unsigned int TokenLen = 0) { - assert(!Loc.isInvalid() && "invalid Loc when adjusting column position"); - - std::pair<FileID, unsigned> LocInfo = SM.getDecomposedExpansionLoc(Loc); - assert(LocInfo.second > SM.getExpansionColumnNumber(Loc) && - "position in file is before column number?"); - - Optional<MemoryBufferRef> Buf = SM.getBufferOrNone(LocInfo.first); - assert(Buf && "got an invalid buffer for the location's file"); - assert(Buf->getBufferSize() >= (LocInfo.second + TokenLen) && - "token extends past end of buffer?"); - - // Adjust the offset to be the start of the line, since we'll be counting - // Unicode characters from there until our column offset. - unsigned int Off = LocInfo.second - (SM.getExpansionColumnNumber(Loc) - 1); - unsigned int Ret = 1; - while (Off < (LocInfo.second + TokenLen)) { - Off += getNumBytesForUTF8(Buf->getBuffer()[Off]); - Ret++; - } - - return Ret; -} - -static json::Object createTextRegion(const LangOptions &LO, SourceRange R, - const SourceManager &SM) { - json::Object Region{ - {"startLine", SM.getExpansionLineNumber(R.getBegin())}, - {"startColumn", adjustColumnPos(SM, R.getBegin())}, - }; - if (R.getBegin() == R.getEnd()) { - Region["endColumn"] = adjustColumnPos(SM, R.getBegin()); - } else { - Region["endLine"] = SM.getExpansionLineNumber(R.getEnd()); - Region["endColumn"] = adjustColumnPos( - SM, R.getEnd(), - Lexer::MeasureTokenLength(R.getEnd(), SM, LO)); - } - return Region; -} - -static json::Object createPhysicalLocation(const LangOptions &LO, - SourceRange R, const FileEntry &FE, - const SourceManager &SMgr, - json::Array &Artifacts) { - return json::Object{ - {{"artifactLocation", createArtifactLocation(FE, Artifacts)}, - {"region", createTextRegion(LO, R, SMgr)}}}; -} - -enum class Importance { Important, Essential, Unimportant }; - -static StringRef importanceToStr(Importance I) { - switch (I) { - case Importance::Important: - return "important"; - case Importance::Essential: - return "essential"; - case Importance::Unimportant: - return "unimportant"; - } - llvm_unreachable("Fully covered switch is not so fully covered"); -} - -static json::Object createThreadFlowLocation(json::Object &&Location, - Importance I) { - return json::Object{{"location", std::move(Location)}, - {"importance", importanceToStr(I)}}; -} - -static json::Object createMessage(StringRef Text) { - return json::Object{{"text", Text.str()}}; -} - -static json::Object createLocation(json::Object &&PhysicalLocation, - StringRef Message = "") { - json::Object Ret{{"physicalLocation", std::move(PhysicalLocation)}}; - if (!Message.empty()) - Ret.insert({"message", createMessage(Message)}); - return Ret; -} - -static Importance calculateImportance(const PathDiagnosticPiece &Piece) { - switch (Piece.getKind()) { - case PathDiagnosticPiece::Call: - case PathDiagnosticPiece::Macro: - case PathDiagnosticPiece::Note: - case PathDiagnosticPiece::PopUp: - // FIXME: What should be reported here? - break; - case PathDiagnosticPiece::Event: - return Piece.getTagStr() == "ConditionBRVisitor" ? Importance::Important - : Importance::Essential; - case PathDiagnosticPiece::ControlFlow: - return Importance::Unimportant; - } - return Importance::Unimportant; -} - -static json::Object createThreadFlow(const LangOptions &LO, - const PathPieces &Pieces, - json::Array &Artifacts) { - const SourceManager &SMgr = Pieces.front()->getLocation().getManager(); - json::Array Locations; - for (const auto &Piece : Pieces) { - const PathDiagnosticLocation &P = Piece->getLocation(); - Locations.push_back(createThreadFlowLocation( - createLocation(createPhysicalLocation( - LO, P.asRange(), - *P.asLocation().getExpansionLoc().getFileEntry(), - SMgr, Artifacts), - Piece->getString()), - calculateImportance(*Piece))); - } - return json::Object{{"locations", std::move(Locations)}}; -} - -static json::Object createCodeFlow(const LangOptions &LO, - const PathPieces &Pieces, - json::Array &Artifacts) { - return json::Object{ - {"threadFlows", json::Array{createThreadFlow(LO, Pieces, Artifacts)}}}; -} - -static json::Object createResult(const LangOptions &LO, - const PathDiagnostic &Diag, - json::Array &Artifacts, - const StringMap<unsigned> &RuleMapping) { - const PathPieces &Path = Diag.path.flatten(false); - const SourceManager &SMgr = Path.front()->getLocation().getManager(); - - auto Iter = RuleMapping.find(Diag.getCheckerName()); - assert(Iter != RuleMapping.end() && "Rule ID is not in the array index map?"); - - return json::Object{ - {"message", createMessage(Diag.getVerboseDescription())}, - {"codeFlows", json::Array{createCodeFlow(LO, Path, Artifacts)}}, - {"locations", - json::Array{createLocation(createPhysicalLocation( - LO, Diag.getLocation().asRange(), - *Diag.getLocation().asLocation().getExpansionLoc().getFileEntry(), - SMgr, Artifacts))}}, - {"ruleIndex", Iter->getValue()}, - {"ruleId", Diag.getCheckerName()}}; -} - static StringRef getRuleDescription(StringRef CheckName) { return llvm::StringSwitch<StringRef>(CheckName) #define GET_CHECKERS @@ -322,60 +88,99 @@ static StringRef getRuleHelpURIStr(StringRef CheckName) { ; } -static json::Object createRule(const PathDiagnostic &Diag) { - StringRef CheckName = Diag.getCheckerName(); - json::Object Ret{ - {"fullDescription", createMessage(getRuleDescription(CheckName))}, - {"name", CheckName}, - {"id", CheckName}}; +static ThreadFlowImportance +calculateImportance(const PathDiagnosticPiece &Piece) { + switch (Piece.getKind()) { + case PathDiagnosticPiece::Call: + case PathDiagnosticPiece::Macro: + case PathDiagnosticPiece::Note: + case PathDiagnosticPiece::PopUp: + // FIXME: What should be reported here? + break; + case PathDiagnosticPiece::Event: + return Piece.getTagStr() == "ConditionBRVisitor" + ? ThreadFlowImportance::Important + : ThreadFlowImportance::Essential; + case PathDiagnosticPiece::ControlFlow: + return ThreadFlowImportance::Unimportant; + } + return ThreadFlowImportance::Unimportant; +} + +/// Accepts a SourceRange corresponding to a pair of the first and last tokens +/// and converts to a Character granular CharSourceRange. +static CharSourceRange convertTokenRangeToCharRange(const SourceRange &R, + const SourceManager &SM, + const LangOptions &LO) { + // Caret diagnostics have the first and last locations pointed at the same + // location, return these as-is. + if (R.getBegin() == R.getEnd()) + return CharSourceRange::getCharRange(R); - std::string RuleURI = std::string(getRuleHelpURIStr(CheckName)); - if (!RuleURI.empty()) - Ret["helpUri"] = RuleURI; + SourceLocation BeginCharLoc = R.getBegin(); + // For token ranges, the raw end SLoc points at the first character of the + // last token in the range. This must be moved to one past the end of the + // last character using the lexer. + SourceLocation EndCharLoc = + Lexer::getLocForEndOfToken(R.getEnd(), /* Offset = */ 0, SM, LO); + return CharSourceRange::getCharRange(BeginCharLoc, EndCharLoc); +} - return Ret; +static SmallVector<ThreadFlow, 8> createThreadFlows(const PathDiagnostic *Diag, + const LangOptions &LO) { + SmallVector<ThreadFlow, 8> Flows; + const PathPieces &Pieces = Diag->path.flatten(false); + for (const auto &Piece : Pieces) { + auto Range = convertTokenRangeToCharRange( + Piece->getLocation().asRange(), Piece->getLocation().getManager(), LO); + auto Flow = ThreadFlow::create() + .setImportance(calculateImportance(*Piece)) + .setRange(Range) + .setMessage(Piece->getString()); + Flows.push_back(Flow); + } + return Flows; } -static json::Array createRules(std::vector<const PathDiagnostic *> &Diags, - StringMap<unsigned> &RuleMapping) { - json::Array Rules; +static StringMap<uint32_t> +createRuleMapping(const std::vector<const PathDiagnostic *> &Diags, + SarifDocumentWriter &SarifWriter) { + StringMap<uint32_t> RuleMapping; llvm::StringSet<> Seen; for (const PathDiagnostic *D : Diags) { - StringRef RuleID = D->getCheckerName(); - std::pair<llvm::StringSet<>::iterator, bool> P = Seen.insert(RuleID); + StringRef CheckName = D->getCheckerName(); + std::pair<llvm::StringSet<>::iterator, bool> P = Seen.insert(CheckName); if (P.second) { - RuleMapping[RuleID] = Rules.size(); // Maps RuleID to an Array Index. - Rules.push_back(createRule(*D)); + auto Rule = SarifRule::create() + .setName(CheckName) + .setRuleId(CheckName) + .setDescription(getRuleDescription(CheckName)) + .setHelpURI(getRuleHelpURIStr(CheckName)); + size_t RuleIdx = SarifWriter.createRule(Rule); + RuleMapping[CheckName] = RuleIdx; } } - - return Rules; + return RuleMapping; } -static json::Object createTool(std::vector<const PathDiagnostic *> &Diags, - StringMap<unsigned> &RuleMapping) { - return json::Object{ - {"driver", json::Object{{"name", "clang"}, - {"fullName", "clang static analyzer"}, - {"language", "en-US"}, - {"version", getClangFullVersion()}, - {"rules", createRules(Diags, RuleMapping)}}}}; -} - -static json::Object createRun(const LangOptions &LO, - std::vector<const PathDiagnostic *> &Diags) { - json::Array Results, Artifacts; - StringMap<unsigned> RuleMapping; - json::Object Tool = createTool(Diags, RuleMapping); +static SarifResult createResult(const PathDiagnostic *Diag, + const StringMap<uint32_t> &RuleMapping, + const LangOptions &LO) { - for (const PathDiagnostic *D : Diags) - Results.push_back(createResult(LO, *D, Artifacts, RuleMapping)); + StringRef CheckName = Diag->getCheckerName(); + uint32_t RuleIdx = RuleMapping.lookup(CheckName); + auto Range = convertTokenRangeToCharRange( + Diag->getLocation().asRange(), Diag->getLocation().getManager(), LO); - return json::Object{{"tool", std::move(Tool)}, - {"results", std::move(Results)}, - {"artifacts", std::move(Artifacts)}, - {"columnKind", "unicodeCodePoints"}}; + SmallVector<ThreadFlow, 8> Flows = createThreadFlows(Diag, LO); + auto Result = SarifResult::create(RuleIdx) + .setRuleId(CheckName) + .setDiagnosticMessage(Diag->getVerboseDescription()) + .setDiagnosticLevel(SarifResultLevel::Warning) + .setLocations({Range}) + .setThreadFlows(Flows); + return Result; } void SarifDiagnostics::FlushDiagnosticsImpl( @@ -391,10 +196,14 @@ void SarifDiagnostics::FlushDiagnosticsImpl( llvm::errs() << "warning: could not create file: " << EC.message() << '\n'; return; } - json::Object Sarif{ - {"$schema", - "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json"}, - {"version", "2.1.0"}, - {"runs", json::Array{createRun(LO, Diags)}}}; - OS << llvm::formatv("{0:2}\n", json::Value(std::move(Sarif))); + + std::string ToolVersion = getClangFullVersion(); + SarifWriter.createRun("clang", "clang static analyzer", ToolVersion); + StringMap<uint32_t> RuleMapping = createRuleMapping(Diags, SarifWriter); + for (const PathDiagnostic *D : Diags) { + SarifResult Result = createResult(D, RuleMapping, LO); + SarifWriter.appendResult(Result); + } + auto Document = SarifWriter.createDocument(); + OS << llvm::formatv("{0:2}\n", json::Value(std::move(Document))); } diff --git a/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp b/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp index dcb6043e9df3..3286d7f468f0 100644 --- a/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp @@ -15,6 +15,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include <optional> namespace clang { @@ -26,7 +27,7 @@ ProgramStateRef SimpleConstraintManager::assumeInternal(ProgramStateRef State, DefinedSVal Cond, bool Assumption) { // If we have a Loc value, cast it to a bool NonLoc first. - if (Optional<Loc> LV = Cond.getAs<Loc>()) { + if (std::optional<Loc> LV = Cond.getAs<Loc>()) { SValBuilder &SVB = State->getStateManager().getSValBuilder(); QualType T; const MemRegion *MR = LV->getAsRegion(); diff --git a/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp index 762ecc18ecea..58d360a2e2db 100644 --- a/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp +++ b/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp @@ -15,6 +15,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h" +#include <optional> using namespace clang; using namespace ento; @@ -348,9 +349,9 @@ static bool shouldRearrange(ProgramStateRef State, BinaryOperator::Opcode Op, isWithinConstantOverflowBounds(Int))); } -static Optional<NonLoc> tryRearrange(ProgramStateRef State, - BinaryOperator::Opcode Op, NonLoc Lhs, - NonLoc Rhs, QualType ResultTy) { +static std::optional<NonLoc> tryRearrange(ProgramStateRef State, + BinaryOperator::Opcode Op, NonLoc Lhs, + NonLoc Rhs, QualType ResultTy) { ProgramStateManager &StateMgr = State->getStateManager(); SValBuilder &SVB = StateMgr.getSValBuilder(); @@ -361,35 +362,35 @@ static Optional<NonLoc> tryRearrange(ProgramStateRef State, // rearrange additive operations but rearrange comparisons only if // option is set. if (!SVB.getAnalyzerOptions().ShouldAggressivelySimplifyBinaryOperation) - return None; + return std::nullopt; SymbolRef LSym = Lhs.getAsSymbol(); if (!LSym) - return None; + return std::nullopt; if (BinaryOperator::isComparisonOp(Op)) { SingleTy = LSym->getType(); if (ResultTy != SVB.getConditionType()) - return None; + return std::nullopt; // Initialize SingleTy later with a symbol's type. } else if (BinaryOperator::isAdditiveOp(Op)) { SingleTy = ResultTy; if (LSym->getType() != SingleTy) - return None; + return std::nullopt; } else { // Don't rearrange other operations. - return None; + return std::nullopt; } assert(!SingleTy.isNull() && "We should have figured out the type by now!"); // Rearrange signed symbolic expressions only if (!SingleTy->isSignedIntegerOrEnumerationType()) - return None; + return std::nullopt; SymbolRef RSym = Rhs.getAsSymbol(); if (!RSym || RSym->getType() != SingleTy) - return None; + return std::nullopt; BasicValueFactory &BV = State->getBasicVals(); llvm::APSInt LInt, RInt; @@ -397,7 +398,7 @@ static Optional<NonLoc> tryRearrange(ProgramStateRef State, std::tie(RSym, RInt) = decomposeSymbol(RSym, BV); if (!shouldRearrange(State, Op, LSym, LInt, SingleTy) || !shouldRearrange(State, Op, RSym, RInt, SingleTy)) - return None; + return std::nullopt; // We know that no overflows can occur anymore. return doRearrangeUnchecked(State, Op, LSym, LInt, RSym, RInt); @@ -544,7 +545,7 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, case BO_LE: case BO_GE: op = BinaryOperator::reverseComparisonOp(op); - LLVM_FALLTHROUGH; + [[fallthrough]]; case BO_EQ: case BO_NE: case BO_Add: @@ -558,7 +559,7 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, // (~0)>>a if (LHSValue.isAllOnes() && LHSValue.isSigned()) return evalCast(lhs, resultTy, QualType{}); - LLVM_FALLTHROUGH; + [[fallthrough]]; case BO_Shl: // 0<<a and 0>>a if (LHSValue == 0) @@ -570,7 +571,7 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, // 0 % x == 0 if (LHSValue == 0) return makeZeroVal(resultTy); - LLVM_FALLTHROUGH; + [[fallthrough]]; default: return makeSymExprValNN(op, InputLHS, InputRHS, resultTy); } @@ -680,7 +681,7 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, if (const llvm::APSInt *RHSValue = getConstValue(state, rhs)) return MakeSymIntVal(Sym, op, *RHSValue, resultTy); - if (Optional<NonLoc> V = tryRearrange(state, op, lhs, rhs, resultTy)) + if (std::optional<NonLoc> V = tryRearrange(state, op, lhs, rhs, resultTy)) return *V; // Give up -- this is not a symbolic expression we can handle. @@ -843,7 +844,7 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state, } // If both operands are constants, just perform the operation. - if (Optional<loc::ConcreteInt> rInt = rhs.getAs<loc::ConcreteInt>()) { + if (std::optional<loc::ConcreteInt> rInt = rhs.getAs<loc::ConcreteInt>()) { assert(BinaryOperator::isComparisonOp(op) || op == BO_Sub); if (const auto *ResultInt = @@ -877,7 +878,7 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state, return UnknownVal(); } case loc::MemRegionValKind: { - if (Optional<loc::ConcreteInt> rInt = rhs.getAs<loc::ConcreteInt>()) { + if (std::optional<loc::ConcreteInt> rInt = rhs.getAs<loc::ConcreteInt>()) { // If one of the operands is a symbol and the other is a constant, // build an expression for use by the constraint manager. if (SymbolRef lSym = lhs.getAsLocSymbol(true)) { @@ -974,7 +975,7 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state, // Get the left index and cast it to the correct type. // If the index is unknown or undefined, bail out here. SVal LeftIndexVal = LeftER->getIndex(); - Optional<NonLoc> LeftIndex = LeftIndexVal.getAs<NonLoc>(); + std::optional<NonLoc> LeftIndex = LeftIndexVal.getAs<NonLoc>(); if (!LeftIndex) return UnknownVal(); LeftIndexVal = evalCast(*LeftIndex, ArrayIndexTy, QualType{}); @@ -984,7 +985,7 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state, // Do the same for the right index. SVal RightIndexVal = RightER->getIndex(); - Optional<NonLoc> RightIndex = RightIndexVal.getAs<NonLoc>(); + std::optional<NonLoc> RightIndex = RightIndexVal.getAs<NonLoc>(); if (!RightIndex) return UnknownVal(); RightIndexVal = evalCast(*RightIndex, ArrayIndexTy, QualType{}); @@ -1092,8 +1093,10 @@ SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state, // We are dealing with pointer arithmetic. // Handle pointer arithmetic on constant values. - if (Optional<nonloc::ConcreteInt> rhsInt = rhs.getAs<nonloc::ConcreteInt>()) { - if (Optional<loc::ConcreteInt> lhsInt = lhs.getAs<loc::ConcreteInt>()) { + if (std::optional<nonloc::ConcreteInt> rhsInt = + rhs.getAs<nonloc::ConcreteInt>()) { + if (std::optional<loc::ConcreteInt> lhsInt = + lhs.getAs<loc::ConcreteInt>()) { const llvm::APSInt &leftI = lhsInt->getValue(); assert(leftI.isUnsigned()); llvm::APSInt rightI(rhsInt->getValue(), /* isUnsigned */ true); @@ -1157,7 +1160,7 @@ SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state, if (elementType->isVoidType()) elementType = getContext().CharTy; - if (Optional<NonLoc> indexV = index.getAs<NonLoc>()) { + if (std::optional<NonLoc> indexV = index.getAs<NonLoc>()) { return loc::MemRegionVal(MemMgr.getElementRegion(elementType, *indexV, superR, getContext())); } @@ -1170,10 +1173,10 @@ const llvm::APSInt *SimpleSValBuilder::getConstValue(ProgramStateRef state, if (V.isUnknownOrUndef()) return nullptr; - if (Optional<loc::ConcreteInt> X = V.getAs<loc::ConcreteInt>()) + if (std::optional<loc::ConcreteInt> X = V.getAs<loc::ConcreteInt>()) return &X->getValue(); - if (Optional<nonloc::ConcreteInt> X = V.getAs<nonloc::ConcreteInt>()) + if (std::optional<nonloc::ConcreteInt> X = V.getAs<nonloc::ConcreteInt>()) return &X->getValue(); if (SymbolRef Sym = V.getAsSymbol()) diff --git a/clang/lib/StaticAnalyzer/Core/Store.cpp b/clang/lib/StaticAnalyzer/Core/Store.cpp index 96e8878da616..fe1fa22af7ab 100644 --- a/clang/lib/StaticAnalyzer/Core/Store.cpp +++ b/clang/lib/StaticAnalyzer/Core/Store.cpp @@ -29,12 +29,12 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/StoreRef.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" #include "llvm/ADT/APSInt.h" -#include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/Casting.h" #include "llvm/Support/ErrorHandling.h" #include <cassert> #include <cstdint> +#include <optional> using namespace clang; using namespace ento; @@ -71,8 +71,8 @@ const ElementRegion *StoreManager::GetElementZeroRegion(const SubRegion *R, return MRMgr.getElementRegion(T, idx, R, Ctx); } -Optional<const MemRegion *> StoreManager::castRegion(const MemRegion *R, - QualType CastToTy) { +std::optional<const MemRegion *> StoreManager::castRegion(const MemRegion *R, + QualType CastToTy) { ASTContext &Ctx = StateMgr.getContext(); // Handle casts to Objective-C objects. @@ -89,7 +89,7 @@ Optional<const MemRegion *> StoreManager::castRegion(const MemRegion *R, // We don't know what to make of it. Return a NULL region, which // will be interpreted as UnknownVal. - return None; + return std::nullopt; } // Now assume we are casting from pointer to pointer. Other cases should @@ -175,7 +175,7 @@ Optional<const MemRegion *> StoreManager::castRegion(const MemRegion *R, // If we cannot compute a raw offset, throw up our hands and return // a NULL MemRegion*. if (!baseR) - return None; + return std::nullopt; CharUnits off = rawOff.getOffset(); @@ -314,7 +314,8 @@ static const CXXRecordDecl *getCXXRecordType(const MemRegion *MR) { return nullptr; } -Optional<SVal> StoreManager::evalBaseToDerived(SVal Base, QualType TargetType) { +std::optional<SVal> StoreManager::evalBaseToDerived(SVal Base, + QualType TargetType) { const MemRegion *MR = Base.getAsRegion(); if (!MR) return UnknownVal(); @@ -390,7 +391,7 @@ Optional<SVal> StoreManager::evalBaseToDerived(SVal Base, QualType TargetType) { // We failed if the region we ended up with has perfect type info. if (isa<TypedValueRegion>(MR)) - return None; + return std::nullopt; return UnknownVal(); } diff --git a/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp b/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp index 2227bd324adc..3e97f0c95fc3 100644 --- a/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp @@ -411,10 +411,14 @@ void SymbolReaper::markLive(SymbolRef sym) { } void SymbolReaper::markLive(const MemRegion *region) { - RegionRoots.insert(region->getBaseRegion()); + LiveRegionRoots.insert(region->getBaseRegion()); markElementIndicesLive(region); } +void SymbolReaper::markLazilyCopied(const clang::ento::MemRegion *region) { + LazilyCopiedRegionRoots.insert(region->getBaseRegion()); +} + void SymbolReaper::markElementIndicesLive(const MemRegion *region) { for (auto SR = dyn_cast<SubRegion>(region); SR; SR = dyn_cast<SubRegion>(SR->getSuperRegion())) { @@ -437,8 +441,7 @@ bool SymbolReaper::isLiveRegion(const MemRegion *MR) { // is not used later in the path, we can diagnose a leak of a value within // that field earlier than, say, the variable that contains the field dies. MR = MR->getBaseRegion(); - - if (RegionRoots.count(MR)) + if (LiveRegionRoots.count(MR)) return true; if (const auto *SR = dyn_cast<SymbolicRegion>(MR)) @@ -454,6 +457,15 @@ bool SymbolReaper::isLiveRegion(const MemRegion *MR) { return isa<AllocaRegion, CXXThisRegion, MemSpaceRegion, CodeTextRegion>(MR); } +bool SymbolReaper::isLazilyCopiedRegion(const MemRegion *MR) const { + // TODO: See comment in isLiveRegion. + return LazilyCopiedRegionRoots.count(MR->getBaseRegion()); +} + +bool SymbolReaper::isReadableRegion(const MemRegion *MR) { + return isLiveRegion(MR) || isLazilyCopiedRegion(MR); +} + bool SymbolReaper::isLive(SymbolRef sym) { if (TheLiving.count(sym)) { markDependentsLive(sym); @@ -464,7 +476,7 @@ bool SymbolReaper::isLive(SymbolRef sym) { switch (sym->getKind()) { case SymExpr::SymbolRegionValueKind: - KnownLive = isLiveRegion(cast<SymbolRegionValue>(sym)->getRegion()); + KnownLive = isReadableRegion(cast<SymbolRegionValue>(sym)->getRegion()); break; case SymExpr::SymbolConjuredKind: KnownLive = false; diff --git a/clang/lib/StaticAnalyzer/Core/TextDiagnostics.cpp b/clang/lib/StaticAnalyzer/Core/TextDiagnostics.cpp index 48c82cfb82b2..05f4d19ebda0 100644 --- a/clang/lib/StaticAnalyzer/Core/TextDiagnostics.cpp +++ b/clang/lib/StaticAnalyzer/Core/TextDiagnostics.cpp @@ -31,8 +31,8 @@ using namespace ento; using namespace tooling; namespace { -/// Emitsd minimal diagnostics (report message + notes) for the 'none' output -/// type to the standard error, or to to compliment many others. Emits detailed +/// Emits minimal diagnostics (report message + notes) for the 'none' output +/// type to the standard error, or to complement many others. Emits detailed /// diagnostics in textual format for the 'text' output type. class TextDiagnostics : public PathDiagnosticConsumer { PathDiagnosticConsumerOptions DiagOpts; |
