diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2023-02-11 12:38:04 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2023-02-11 12:38:11 +0000 |
commit | e3b557809604d036af6e00c60f012c2025b59a5e (patch) | |
tree | 8a11ba2269a3b669601e2fd41145b174008f4da8 /clang/lib/StaticAnalyzer | |
parent | 08e8dd7b9db7bb4a9de26d44c1cbfd24e869c014 (diff) | |
download | src-e3b557809604d036af6e00c60f012c2025b59a5e.tar.gz src-e3b557809604d036af6e00c60f012c2025b59a5e.zip |
Diffstat (limited to 'clang/lib/StaticAnalyzer')
104 files changed, 3456 insertions, 2136 deletions
diff --git a/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp index 6154eeb3419c..45783729e142 100644 --- a/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp @@ -19,6 +19,7 @@ #include "llvm/ADT/SmallString.h" #include "llvm/ADT/Statistic.h" #include "llvm/Support/raw_ostream.h" +#include <optional> using namespace clang; using namespace ento; @@ -59,7 +60,7 @@ void AnalyzerStatsChecker::checkEndAnalysis(ExplodedGraph &G, if (D != P.getLocationContext()->getDecl()) continue; - if (Optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) { + if (std::optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) { const CFGBlock *CB = BE->getBlock(); reachable.insert(CB); } @@ -123,7 +124,7 @@ void AnalyzerStatsChecker::checkEndAnalysis(ExplodedGraph &G, if (Exit->empty()) continue; const CFGElement &CE = Exit->front(); - if (Optional<CFGStmt> CS = CE.getAs<CFGStmt>()) { + if (std::optional<CFGStmt> CS = CE.getAs<CFGStmt>()) { SmallString<128> bufI; llvm::raw_svector_ostream outputI(bufI); outputI << "(" << NameOfRootFunction << ")" << diff --git a/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp b/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp index 5be5bcde4d6e..986b0add93df 100644 --- a/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp @@ -23,6 +23,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" +#include <optional> using namespace clang; using namespace ento; @@ -84,7 +85,7 @@ static SVal computeExtentBegin(SValBuilder &svalBuilder, static std::pair<NonLoc, nonloc::ConcreteInt> getSimplifiedOffsets(NonLoc offset, nonloc::ConcreteInt extent, SValBuilder &svalBuilder) { - Optional<nonloc::SymbolVal> SymVal = offset.getAs<nonloc::SymbolVal>(); + std::optional<nonloc::SymbolVal> SymVal = offset.getAs<nonloc::SymbolVal>(); if (SymVal && SymVal->isExpression()) { if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(SymVal->getSymbol())) { llvm::APSInt constant = @@ -143,7 +144,7 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad, SVal extentBegin = computeExtentBegin(svalBuilder, rawOffset.getRegion()); - if (Optional<NonLoc> NV = extentBegin.getAs<NonLoc>()) { + if (std::optional<NonLoc> NV = extentBegin.getAs<NonLoc>()) { if (auto ConcreteNV = NV->getAs<nonloc::ConcreteInt>()) { std::pair<NonLoc, nonloc::ConcreteInt> simplifiedOffsets = getSimplifiedOffsets(rawOffset.getByteOffset(), *ConcreteNV, @@ -155,7 +156,7 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad, SVal lowerBound = svalBuilder.evalBinOpNN(state, BO_LT, rawOffsetVal, *NV, svalBuilder.getConditionType()); - Optional<NonLoc> lowerBoundToCheck = lowerBound.getAs<NonLoc>(); + std::optional<NonLoc> lowerBoundToCheck = lowerBound.getAs<NonLoc>(); if (!lowerBoundToCheck) return; @@ -194,7 +195,7 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad, Size.castAs<NonLoc>(), svalBuilder.getConditionType()); - Optional<NonLoc> upperboundToCheck = upperbound.getAs<NonLoc>(); + std::optional<NonLoc> upperboundToCheck = upperbound.getAs<NonLoc>(); if (!upperboundToCheck) break; diff --git a/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp index ca76e2d83381..44166aaf5b85 100644 --- a/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp @@ -33,6 +33,7 @@ #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringMap.h" #include "llvm/Support/raw_ostream.h" +#include <optional> using namespace clang; using namespace ento; @@ -369,7 +370,7 @@ enum CFNumberType { kCFNumberCGFloatType = 16 }; -static Optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) { +static std::optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) { static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 }; if (i < kCFNumberCharType) @@ -390,7 +391,7 @@ static Optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) { case kCFNumberCGFloatType: // FIXME: We need a way to map from names to Type*. default: - return None; + return std::nullopt; } return Ctx.getTypeSize(T); @@ -442,12 +443,13 @@ void CFNumberChecker::checkPreStmt(const CallExpr *CE, // FIXME: We really should allow ranges of valid theType values, and // bifurcate the state appropriately. - Optional<nonloc::ConcreteInt> V = dyn_cast<nonloc::ConcreteInt>(TheTypeVal); + std::optional<nonloc::ConcreteInt> V = + dyn_cast<nonloc::ConcreteInt>(TheTypeVal); if (!V) return; uint64_t NumberKind = V->getValue().getLimitedValue(); - Optional<uint64_t> OptCFNumberSize = GetCFNumberSize(Ctx, NumberKind); + std::optional<uint64_t> OptCFNumberSize = GetCFNumberSize(Ctx, NumberKind); // FIXME: In some cases we can emit an error. if (!OptCFNumberSize) @@ -462,7 +464,7 @@ void CFNumberChecker::checkPreStmt(const CallExpr *CE, // FIXME: Eventually we should handle arbitrary locations. We can do this // by having an enhanced memory model that does low-level typing. - Optional<loc::MemRegionVal> LV = TheValueExpr.getAs<loc::MemRegionVal>(); + std::optional<loc::MemRegionVal> LV = TheValueExpr.getAs<loc::MemRegionVal>(); if (!LV) return; @@ -531,10 +533,10 @@ namespace { class CFRetainReleaseChecker : public Checker<check::PreCall> { mutable APIMisuse BT{this, "null passed to CF memory management function"}; const CallDescriptionSet ModelledCalls = { - {"CFRetain", 1}, - {"CFRelease", 1}, - {"CFMakeCollectable", 1}, - {"CFAutorelease", 1}, + {{"CFRetain"}, 1}, + {{"CFRelease"}, 1}, + {{"CFMakeCollectable"}, 1}, + {{"CFAutorelease"}, 1}, }; public: @@ -554,7 +556,7 @@ void CFRetainReleaseChecker::checkPreCall(const CallEvent &Call, // Get the argument's value. SVal ArgVal = Call.getArgSVal(0); - Optional<DefinedSVal> DefArgVal = ArgVal.getAs<DefinedSVal>(); + std::optional<DefinedSVal> DefArgVal = ArgVal.getAs<DefinedSVal>(); if (!DefArgVal) return; @@ -742,7 +744,7 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg, return; // Verify that all arguments have Objective-C types. - Optional<ExplodedNode*> errorNode; + std::optional<ExplodedNode *> errorNode; for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) { QualType ArgTy = msg.getArgExpr(I)->getType(); @@ -769,7 +771,7 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg, if (!errorNode) errorNode = C.generateNonFatalErrorNode(); - if (!errorNode.value()) + if (!*errorNode) continue; SmallString<128> sbuf; @@ -786,8 +788,8 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg, ArgTy.print(os, C.getLangOpts()); os << "'"; - auto R = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), - errorNode.value()); + auto R = + std::make_unique<PathSensitiveBugReport>(*BT, os.str(), *errorNode); R->addRange(msg.getArgSourceRange(I)); C.emitReport(std::move(R)); } @@ -857,7 +859,8 @@ static ProgramStateRef checkCollectionNonNil(CheckerContext &C, return nullptr; SVal CollectionVal = C.getSVal(FCS->getCollection()); - Optional<DefinedSVal> KnownCollection = CollectionVal.getAs<DefinedSVal>(); + std::optional<DefinedSVal> KnownCollection = + CollectionVal.getAs<DefinedSVal>(); if (!KnownCollection) return State; @@ -889,7 +892,7 @@ static ProgramStateRef checkElementNonNil(CheckerContext &C, const Stmt *Element = FCS->getElement(); // FIXME: Copied from ExprEngineObjC. - Optional<Loc> ElementLoc; + std::optional<Loc> ElementLoc; if (const DeclStmt *DS = dyn_cast<DeclStmt>(Element)) { const VarDecl *ElemDecl = cast<VarDecl>(DS->getSingleDecl()); assert(ElemDecl->getInit() == nullptr); @@ -928,8 +931,8 @@ assumeCollectionNonEmpty(CheckerContext &C, ProgramStateRef State, nonloc::SymbolVal(*CountS), SvalBuilder.makeIntVal(0, (*CountS)->getType()), SvalBuilder.getConditionType()); - Optional<DefinedSVal> CountGreaterThanZero = - CountGreaterThanZeroVal.getAs<DefinedSVal>(); + std::optional<DefinedSVal> CountGreaterThanZero = + CountGreaterThanZeroVal.getAs<DefinedSVal>(); if (!CountGreaterThanZero) { // The SValBuilder cannot construct a valid SVal for this condition. // This means we cannot properly reason about it. @@ -957,7 +960,7 @@ static bool alreadyExecutedAtLeastOneLoopIteration(const ExplodedNode *N, return false; ProgramPoint P = N->getLocation(); - if (Optional<BlockEdge> BE = P.getAs<BlockEdge>()) { + if (std::optional<BlockEdge> BE = P.getAs<BlockEdge>()) { return BE->getSrc()->getLoopTarget() == FCS; } @@ -1092,7 +1095,7 @@ ObjCLoopChecker::checkPointerEscape(ProgramStateRef State, PointerEscapeKind Kind) const { SymbolRef ImmutableReceiver = getMethodReceiverIfKnownImmutable(Call); - // Remove the invalidated symbols form the collection count map. + // Remove the invalidated symbols from the collection count map. for (InvalidatedSymbols::const_iterator I = Escaped.begin(), E = Escaped.end(); I != E; ++I) { @@ -1174,7 +1177,8 @@ ObjCNonNilReturnValueChecker::assumeExprIsNonNull(const Expr *NonNullExpr, ProgramStateRef State, CheckerContext &C) const { SVal Val = C.getSVal(NonNullExpr); - if (Optional<DefinedOrUnknownSVal> DV = Val.getAs<DefinedOrUnknownSVal>()) + if (std::optional<DefinedOrUnknownSVal> DV = + Val.getAs<DefinedOrUnknownSVal>()) return State->assume(*DV, true); return State; } diff --git a/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp index 8416ab39e194..76f091562cd5 100644 --- a/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp @@ -64,19 +64,15 @@ public: REGISTER_TRAIT_WITH_PROGRAMSTATE(MutexCounter, unsigned) BlockInCriticalSectionChecker::BlockInCriticalSectionChecker() - : IILockGuard(nullptr), IIUniqueLock(nullptr), - LockFn("lock"), UnlockFn("unlock"), SleepFn("sleep"), GetcFn("getc"), - FgetsFn("fgets"), ReadFn("read"), RecvFn("recv"), - PthreadLockFn("pthread_mutex_lock"), - PthreadTryLockFn("pthread_mutex_trylock"), - PthreadUnlockFn("pthread_mutex_unlock"), - MtxLock("mtx_lock"), - MtxTimedLock("mtx_timedlock"), - MtxTryLock("mtx_trylock"), - MtxUnlock("mtx_unlock"), - ClassLockGuard("lock_guard"), - ClassUniqueLock("unique_lock"), - IdentifierInfoInitialized(false) { + : IILockGuard(nullptr), IIUniqueLock(nullptr), LockFn({"lock"}), + UnlockFn({"unlock"}), SleepFn({"sleep"}), GetcFn({"getc"}), + FgetsFn({"fgets"}), ReadFn({"read"}), RecvFn({"recv"}), + PthreadLockFn({"pthread_mutex_lock"}), + PthreadTryLockFn({"pthread_mutex_trylock"}), + PthreadUnlockFn({"pthread_mutex_unlock"}), MtxLock({"mtx_lock"}), + MtxTimedLock({"mtx_timedlock"}), MtxTryLock({"mtx_trylock"}), + MtxUnlock({"mtx_unlock"}), ClassLockGuard("lock_guard"), + ClassUniqueLock("unique_lock"), IdentifierInfoInitialized(false) { // Initialize the bug type. BlockInCritSectionBugType.reset( new BugType(this, "Call to blocking function in critical section", diff --git a/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp index dad25d6f853b..2d20e394681d 100644 --- a/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp @@ -17,6 +17,7 @@ #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include <optional> using namespace clang; using namespace ento; @@ -74,7 +75,7 @@ void BoolAssignmentChecker::checkBind(SVal loc, SVal val, const Stmt *S, // Get the value of the right-hand side. We only care about values // that are defined (UnknownVals and UndefinedVals are handled by other // checkers). - Optional<NonLoc> NV = val.getAs<NonLoc>(); + std::optional<NonLoc> NV = val.getAs<NonLoc>(); if (!NV) return; diff --git a/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp index 9a6c013bcf66..12b948a65261 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -27,6 +27,7 @@ #include "llvm/ADT/StringExtras.h" #include "llvm/Support/raw_ostream.h" #include <functional> +#include <optional> using namespace clang; using namespace ento; @@ -75,6 +76,16 @@ static ErrorMessage createOutOfBoundErrorMsg(StringRef FunctionDescription, } enum class ConcatFnKind { none = 0, strcat = 1, strlcat = 2 }; + +enum class CharKind { Regular = 0, Wide }; +constexpr CharKind CK_Regular = CharKind::Regular; +constexpr CharKind CK_Wide = CharKind::Wide; + +static QualType getCharPtrType(ASTContext &Ctx, CharKind CK) { + return Ctx.getPointerType(CK == CharKind::Regular ? Ctx.CharTy + : Ctx.WideCharTy); +} + class CStringChecker : public Checker< eval::Call, check::PreStmt<DeclStmt>, check::LiveSymbols, @@ -124,35 +135,46 @@ public: const CallExpr *)>; CallDescriptionMap<FnCheck> Callbacks = { - {{CDF_MaybeBuiltin, "memcpy", 3}, - std::bind(&CStringChecker::evalMemcpy, _1, _2, _3, false)}, - {{CDF_MaybeBuiltin, "wmemcpy", 3}, - std::bind(&CStringChecker::evalMemcpy, _1, _2, _3, true)}, - {{CDF_MaybeBuiltin, "mempcpy", 3}, &CStringChecker::evalMempcpy}, - {{CDF_MaybeBuiltin, "memcmp", 3}, &CStringChecker::evalMemcmp}, - {{CDF_MaybeBuiltin, "memmove", 3}, &CStringChecker::evalMemmove}, - {{CDF_MaybeBuiltin, "memset", 3}, &CStringChecker::evalMemset}, - {{CDF_MaybeBuiltin, "explicit_memset", 3}, &CStringChecker::evalMemset}, - {{CDF_MaybeBuiltin, "strcpy", 2}, &CStringChecker::evalStrcpy}, - {{CDF_MaybeBuiltin, "strncpy", 3}, &CStringChecker::evalStrncpy}, - {{CDF_MaybeBuiltin, "stpcpy", 2}, &CStringChecker::evalStpcpy}, - {{CDF_MaybeBuiltin, "strlcpy", 3}, &CStringChecker::evalStrlcpy}, - {{CDF_MaybeBuiltin, "strcat", 2}, &CStringChecker::evalStrcat}, - {{CDF_MaybeBuiltin, "strncat", 3}, &CStringChecker::evalStrncat}, - {{CDF_MaybeBuiltin, "strlcat", 3}, &CStringChecker::evalStrlcat}, - {{CDF_MaybeBuiltin, "strlen", 1}, &CStringChecker::evalstrLength}, - {{CDF_MaybeBuiltin, "wcslen", 1}, &CStringChecker::evalstrLength}, - {{CDF_MaybeBuiltin, "strnlen", 2}, &CStringChecker::evalstrnLength}, - {{CDF_MaybeBuiltin, "wcsnlen", 2}, &CStringChecker::evalstrnLength}, - {{CDF_MaybeBuiltin, "strcmp", 2}, &CStringChecker::evalStrcmp}, - {{CDF_MaybeBuiltin, "strncmp", 3}, &CStringChecker::evalStrncmp}, - {{CDF_MaybeBuiltin, "strcasecmp", 2}, &CStringChecker::evalStrcasecmp}, - {{CDF_MaybeBuiltin, "strncasecmp", 3}, &CStringChecker::evalStrncasecmp}, - {{CDF_MaybeBuiltin, "strsep", 2}, &CStringChecker::evalStrsep}, - {{CDF_MaybeBuiltin, "bcopy", 3}, &CStringChecker::evalBcopy}, - {{CDF_MaybeBuiltin, "bcmp", 3}, &CStringChecker::evalMemcmp}, - {{CDF_MaybeBuiltin, "bzero", 2}, &CStringChecker::evalBzero}, - {{CDF_MaybeBuiltin, "explicit_bzero", 2}, &CStringChecker::evalBzero}, + {{CDF_MaybeBuiltin, {"memcpy"}, 3}, + std::bind(&CStringChecker::evalMemcpy, _1, _2, _3, CK_Regular)}, + {{CDF_MaybeBuiltin, {"wmemcpy"}, 3}, + std::bind(&CStringChecker::evalMemcpy, _1, _2, _3, CK_Wide)}, + {{CDF_MaybeBuiltin, {"mempcpy"}, 3}, + std::bind(&CStringChecker::evalMempcpy, _1, _2, _3, CK_Regular)}, + {{CDF_None, {"wmempcpy"}, 3}, + std::bind(&CStringChecker::evalMempcpy, _1, _2, _3, CK_Wide)}, + {{CDF_MaybeBuiltin, {"memcmp"}, 3}, + std::bind(&CStringChecker::evalMemcmp, _1, _2, _3, CK_Regular)}, + {{CDF_MaybeBuiltin, {"wmemcmp"}, 3}, + std::bind(&CStringChecker::evalMemcmp, _1, _2, _3, CK_Wide)}, + {{CDF_MaybeBuiltin, {"memmove"}, 3}, + std::bind(&CStringChecker::evalMemmove, _1, _2, _3, CK_Regular)}, + {{CDF_MaybeBuiltin, {"wmemmove"}, 3}, + std::bind(&CStringChecker::evalMemmove, _1, _2, _3, CK_Wide)}, + {{CDF_MaybeBuiltin, {"memset"}, 3}, &CStringChecker::evalMemset}, + {{CDF_MaybeBuiltin, {"explicit_memset"}, 3}, &CStringChecker::evalMemset}, + {{CDF_MaybeBuiltin, {"strcpy"}, 2}, &CStringChecker::evalStrcpy}, + {{CDF_MaybeBuiltin, {"strncpy"}, 3}, &CStringChecker::evalStrncpy}, + {{CDF_MaybeBuiltin, {"stpcpy"}, 2}, &CStringChecker::evalStpcpy}, + {{CDF_MaybeBuiltin, {"strlcpy"}, 3}, &CStringChecker::evalStrlcpy}, + {{CDF_MaybeBuiltin, {"strcat"}, 2}, &CStringChecker::evalStrcat}, + {{CDF_MaybeBuiltin, {"strncat"}, 3}, &CStringChecker::evalStrncat}, + {{CDF_MaybeBuiltin, {"strlcat"}, 3}, &CStringChecker::evalStrlcat}, + {{CDF_MaybeBuiltin, {"strlen"}, 1}, &CStringChecker::evalstrLength}, + {{CDF_MaybeBuiltin, {"wcslen"}, 1}, &CStringChecker::evalstrLength}, + {{CDF_MaybeBuiltin, {"strnlen"}, 2}, &CStringChecker::evalstrnLength}, + {{CDF_MaybeBuiltin, {"wcsnlen"}, 2}, &CStringChecker::evalstrnLength}, + {{CDF_MaybeBuiltin, {"strcmp"}, 2}, &CStringChecker::evalStrcmp}, + {{CDF_MaybeBuiltin, {"strncmp"}, 3}, &CStringChecker::evalStrncmp}, + {{CDF_MaybeBuiltin, {"strcasecmp"}, 2}, &CStringChecker::evalStrcasecmp}, + {{CDF_MaybeBuiltin, {"strncasecmp"}, 3}, + &CStringChecker::evalStrncasecmp}, + {{CDF_MaybeBuiltin, {"strsep"}, 2}, &CStringChecker::evalStrsep}, + {{CDF_MaybeBuiltin, {"bcopy"}, 3}, &CStringChecker::evalBcopy}, + {{CDF_MaybeBuiltin, {"bcmp"}, 3}, + std::bind(&CStringChecker::evalMemcmp, _1, _2, _3, CK_Regular)}, + {{CDF_MaybeBuiltin, {"bzero"}, 2}, &CStringChecker::evalBzero}, + {{CDF_MaybeBuiltin, {"explicit_bzero"}, 2}, &CStringChecker::evalBzero}, }; // These require a bit of special handling. @@ -160,16 +182,16 @@ public: StdCopyBackward{{"std", "copy_backward"}, 3}; FnCheck identifyCall(const CallEvent &Call, CheckerContext &C) const; - void evalMemcpy(CheckerContext &C, const CallExpr *CE, bool IsWide) const; - void evalMempcpy(CheckerContext &C, const CallExpr *CE) const; - void evalMemmove(CheckerContext &C, const CallExpr *CE) const; + void evalMemcpy(CheckerContext &C, const CallExpr *CE, CharKind CK) const; + void evalMempcpy(CheckerContext &C, const CallExpr *CE, CharKind CK) const; + void evalMemmove(CheckerContext &C, const CallExpr *CE, CharKind CK) const; void evalBcopy(CheckerContext &C, const CallExpr *CE) const; void evalCopyCommon(CheckerContext &C, const CallExpr *CE, ProgramStateRef state, SizeArgExpr Size, DestinationArgExpr Dest, SourceArgExpr Source, - bool Restricted, bool IsMempcpy, bool IsWide) const; + bool Restricted, bool IsMempcpy, CharKind CK) const; - void evalMemcmp(CheckerContext &C, const CallExpr *CE) const; + void evalMemcmp(CheckerContext &C, const CallExpr *CE, CharKind CK) const; void evalstrLength(CheckerContext &C, const CallExpr *CE) const; void evalstrnLength(CheckerContext &C, const CallExpr *CE) const; @@ -248,14 +270,16 @@ public: AnyArgExpr Arg, SVal l) const; ProgramStateRef CheckLocation(CheckerContext &C, ProgramStateRef state, AnyArgExpr Buffer, SVal Element, - AccessKind Access, bool IsWide = false) const; + AccessKind Access, + CharKind CK = CharKind::Regular) const; ProgramStateRef CheckBufferAccess(CheckerContext &C, ProgramStateRef State, AnyArgExpr Buffer, SizeArgExpr Size, AccessKind Access, - bool IsWide = false) const; + CharKind CK = CharKind::Regular) const; ProgramStateRef CheckOverlap(CheckerContext &C, ProgramStateRef state, SizeArgExpr Size, AnyArgExpr First, - AnyArgExpr Second, bool IsWide = false) const; + AnyArgExpr Second, + CharKind CK = CharKind::Regular) const; void emitOverlapBug(CheckerContext &C, ProgramStateRef state, const Stmt *First, @@ -295,7 +319,7 @@ REGISTER_MAP_WITH_PROGRAMSTATE(CStringLength, const MemRegion *, SVal) std::pair<ProgramStateRef , ProgramStateRef > CStringChecker::assumeZero(CheckerContext &C, ProgramStateRef state, SVal V, QualType Ty) { - Optional<DefinedSVal> val = V.getAs<DefinedSVal>(); + std::optional<DefinedSVal> val = V.getAs<DefinedSVal>(); if (!val) return std::pair<ProgramStateRef , ProgramStateRef >(state, state); @@ -339,7 +363,7 @@ ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C, ProgramStateRef state, AnyArgExpr Buffer, SVal Element, AccessKind Access, - bool IsWide) const { + CharKind CK) const { // If a previous check has failed, propagate the failure. if (!state) @@ -360,7 +384,7 @@ ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C, // Get the index of the accessed element. NonLoc Idx = ER->getIndex(); - if (!IsWide) { + if (CK == CharKind::Regular) { if (ER->getValueType() != Ctx.CharTy) return state; } else { @@ -417,7 +441,7 @@ ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C, ProgramStateRef CStringChecker::CheckBufferAccess(CheckerContext &C, ProgramStateRef State, AnyArgExpr Buffer, SizeArgExpr Size, - AccessKind Access, bool IsWide) const { + AccessKind Access, CharKind CK) const { // If a previous check has failed, propagate the failure. if (!State) return nullptr; @@ -426,7 +450,7 @@ CStringChecker::CheckBufferAccess(CheckerContext &C, ProgramStateRef State, ASTContext &Ctx = svalBuilder.getContext(); QualType SizeTy = Size.Expression->getType(); - QualType PtrTy = Ctx.getPointerType(IsWide ? Ctx.WideCharTy : Ctx.CharTy); + QualType PtrTy = getCharPtrType(Ctx, CK); // Check that the first buffer is non-null. SVal BufVal = C.getSVal(Buffer.Expression); @@ -442,7 +466,7 @@ CStringChecker::CheckBufferAccess(CheckerContext &C, ProgramStateRef State, // FIXME: This assumes the caller has already checked that the access length // is positive. And that it's unsigned. SVal LengthVal = C.getSVal(Size.Expression); - Optional<NonLoc> Length = LengthVal.getAs<NonLoc>(); + std::optional<NonLoc> Length = LengthVal.getAs<NonLoc>(); if (!Length) return State; @@ -456,11 +480,11 @@ CStringChecker::CheckBufferAccess(CheckerContext &C, ProgramStateRef State, // Check that the first buffer is sufficiently long. SVal BufStart = svalBuilder.evalCast(BufVal, PtrTy, Buffer.Expression->getType()); - if (Optional<Loc> BufLoc = BufStart.getAs<Loc>()) { + if (std::optional<Loc> BufLoc = BufStart.getAs<Loc>()) { SVal BufEnd = svalBuilder.evalBinOpLN(State, BO_Add, *BufLoc, LastOffset, PtrTy); - State = CheckLocation(C, State, Buffer, BufEnd, Access, IsWide); + State = CheckLocation(C, State, Buffer, BufEnd, Access, CK); // If the buffer isn't large enough, abort. if (!State) @@ -475,7 +499,7 @@ ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C, ProgramStateRef state, SizeArgExpr Size, AnyArgExpr First, AnyArgExpr Second, - bool IsWide) const { + CharKind CK) const { if (!Filter.CheckCStringBufferOverlap) return state; @@ -499,11 +523,11 @@ ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C, SVal firstVal = state->getSVal(First.Expression, LCtx); SVal secondVal = state->getSVal(Second.Expression, LCtx); - Optional<Loc> firstLoc = firstVal.getAs<Loc>(); + std::optional<Loc> firstLoc = firstVal.getAs<Loc>(); if (!firstLoc) return state; - Optional<Loc> secondLoc = secondVal.getAs<Loc>(); + std::optional<Loc> secondLoc = secondVal.getAs<Loc>(); if (!secondLoc) return state; @@ -526,7 +550,7 @@ ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C, QualType cmpTy = svalBuilder.getConditionType(); SVal reverse = svalBuilder.evalBinOpLL(state, BO_GT, *firstLoc, *secondLoc, cmpTy); - Optional<DefinedOrUnknownSVal> reverseTest = + std::optional<DefinedOrUnknownSVal> reverseTest = reverse.getAs<DefinedOrUnknownSVal>(); if (!reverseTest) return state; @@ -547,31 +571,31 @@ ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C, // Get the length, and make sure it too is known. SVal LengthVal = state->getSVal(Size.Expression, LCtx); - Optional<NonLoc> Length = LengthVal.getAs<NonLoc>(); + std::optional<NonLoc> Length = LengthVal.getAs<NonLoc>(); if (!Length) return state; // Convert the first buffer's start address to char*. // Bail out if the cast fails. ASTContext &Ctx = svalBuilder.getContext(); - QualType CharPtrTy = Ctx.getPointerType(IsWide ? Ctx.WideCharTy : Ctx.CharTy); + QualType CharPtrTy = getCharPtrType(Ctx, CK); SVal FirstStart = svalBuilder.evalCast(*firstLoc, CharPtrTy, First.Expression->getType()); - Optional<Loc> FirstStartLoc = FirstStart.getAs<Loc>(); + std::optional<Loc> FirstStartLoc = FirstStart.getAs<Loc>(); if (!FirstStartLoc) return state; // Compute the end of the first buffer. Bail out if THAT fails. SVal FirstEnd = svalBuilder.evalBinOpLN(state, BO_Add, *FirstStartLoc, *Length, CharPtrTy); - Optional<Loc> FirstEndLoc = FirstEnd.getAs<Loc>(); + std::optional<Loc> FirstEndLoc = FirstEnd.getAs<Loc>(); if (!FirstEndLoc) return state; // Is the end of the first buffer past the start of the second buffer? SVal Overlap = svalBuilder.evalBinOpLL(state, BO_GT, *FirstEndLoc, *secondLoc, cmpTy); - Optional<DefinedOrUnknownSVal> OverlapTest = + std::optional<DefinedOrUnknownSVal> OverlapTest = Overlap.getAs<DefinedOrUnknownSVal>(); if (!OverlapTest) return state; @@ -736,7 +760,7 @@ ProgramStateRef CStringChecker::checkAdditionOverflow(CheckerContext &C, left = right; } - if (Optional<NonLoc> maxMinusRightNL = maxMinusRight.getAs<NonLoc>()) { + if (std::optional<NonLoc> maxMinusRightNL = maxMinusRight.getAs<NonLoc>()) { QualType cmpTy = svalBuilder.getConditionType(); // If left > max - right, we have an overflow. SVal willOverflow = svalBuilder.evalBinOpNN(state, BO_GT, left, @@ -822,7 +846,7 @@ SVal CStringChecker::getCStringLengthForRegion(CheckerContext &C, C.blockCount()); if (!hypothetical) { - if (Optional<NonLoc> strLn = strLength.getAs<NonLoc>()) { + if (std::optional<NonLoc> strLn = strLength.getAs<NonLoc>()) { // In case of unbounded calls strlen etc bound the range to SIZE_MAX/4 BasicValueFactory &BVF = svalBuilder.getBasicValueFactory(); const llvm::APSInt &maxValInt = BVF.getMaxValue(sizeTy); @@ -848,7 +872,7 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state, // If we can't get a region, see if it's something we /know/ isn't a // C string. In the context of locations, the only time we can issue such // a warning is for labels. - if (Optional<loc::GotoLabel> Label = Buf.getAs<loc::GotoLabel>()) { + if (std::optional<loc::GotoLabel> Label = Buf.getAs<loc::GotoLabel>()) { if (Filter.CheckCStringNotNullTerm) { SmallString<120> buf; llvm::raw_svector_ostream os(buf); @@ -953,7 +977,7 @@ bool CStringChecker::IsFirstBufInBound(CheckerContext &C, SVal BufVal = state->getSVal(FirstBuf, LCtx); SVal LengthVal = state->getSVal(Size, LCtx); - Optional<NonLoc> Length = LengthVal.getAs<NonLoc>(); + std::optional<NonLoc> Length = LengthVal.getAs<NonLoc>(); if (!Length) return true; // cf top comment. @@ -966,7 +990,7 @@ bool CStringChecker::IsFirstBufInBound(CheckerContext &C, // Check that the first buffer is sufficiently long. SVal BufStart = svalBuilder.evalCast(BufVal, PtrTy, FirstBuf->getType()); - Optional<Loc> BufLoc = BufStart.getAs<Loc>(); + std::optional<Loc> BufLoc = BufStart.getAs<Loc>(); if (!BufLoc) return true; // cf top comment. @@ -1004,14 +1028,14 @@ ProgramStateRef CStringChecker::InvalidateBuffer(CheckerContext &C, const Expr *E, SVal V, bool IsSourceBuffer, const Expr *Size) { - Optional<Loc> L = V.getAs<Loc>(); + std::optional<Loc> L = V.getAs<Loc>(); if (!L) return state; // FIXME: This is a simplified version of what's in CFRefCount.cpp -- it makes // some assumptions about the value that CFRefCount can't. Even so, it should // probably be refactored. - if (Optional<loc::MemRegionVal> MR = L->getAs<loc::MemRegionVal>()) { + if (std::optional<loc::MemRegionVal> MR = L->getAs<loc::MemRegionVal>()) { const MemRegion *R = MR->getRegion()->StripCasts(); // Are we dealing with an ElementRegion? If so, we should be invalidating @@ -1111,7 +1135,7 @@ bool CStringChecker::memsetAux(const Expr *DstBuffer, SVal CharVal, RegionOffset Offset = MR->getAsOffset(); const MemRegion *BR = Offset.getRegion(); - Optional<NonLoc> SizeNL = SizeVal.getAs<NonLoc>(); + std::optional<NonLoc> SizeNL = SizeVal.getAs<NonLoc>(); if (!SizeNL) return false; @@ -1190,7 +1214,7 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, const CallExpr *CE, ProgramStateRef state, SizeArgExpr Size, DestinationArgExpr Dest, SourceArgExpr Source, bool Restricted, - bool IsMempcpy, bool IsWide) const { + bool IsMempcpy, CharKind CK) const { CurrentFunctionDescription = "memory copy function"; // See if the size argument is zero. @@ -1233,11 +1257,11 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, const CallExpr *CE, return; // Ensure the accesses are valid and that the buffers do not overlap. - state = CheckBufferAccess(C, state, Dest, Size, AccessKind::write, IsWide); - state = CheckBufferAccess(C, state, Source, Size, AccessKind::read, IsWide); + state = CheckBufferAccess(C, state, Dest, Size, AccessKind::write, CK); + state = CheckBufferAccess(C, state, Source, Size, AccessKind::read, CK); if (Restricted) - state = CheckOverlap(C, state, Size, Dest, Source, IsWide); + state = CheckOverlap(C, state, Size, Dest, Source, CK); if (!state) return; @@ -1248,7 +1272,7 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, const CallExpr *CE, // Get the byte after the last byte copied. SValBuilder &SvalBuilder = C.getSValBuilder(); ASTContext &Ctx = SvalBuilder.getContext(); - QualType CharPtrTy = Ctx.getPointerType(Ctx.CharTy); + QualType CharPtrTy = getCharPtrType(Ctx, CK); SVal DestRegCharVal = SvalBuilder.evalCast(destVal, CharPtrTy, Dest.Expression->getType()); SVal lastElement = C.getSValBuilder().evalBinOp( @@ -1288,7 +1312,7 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, const CallExpr *CE, } void CStringChecker::evalMemcpy(CheckerContext &C, const CallExpr *CE, - bool IsWide) const { + CharKind CK) const { // void *memcpy(void *restrict dst, const void *restrict src, size_t n); // The return value is the address of the destination buffer. DestinationArgExpr Dest = {CE->getArg(0), 0}; @@ -1299,11 +1323,11 @@ void CStringChecker::evalMemcpy(CheckerContext &C, const CallExpr *CE, constexpr bool IsRestricted = true; constexpr bool IsMempcpy = false; - evalCopyCommon(C, CE, State, Size, Dest, Src, IsRestricted, IsMempcpy, - IsWide); + evalCopyCommon(C, CE, State, Size, Dest, Src, IsRestricted, IsMempcpy, CK); } -void CStringChecker::evalMempcpy(CheckerContext &C, const CallExpr *CE) const { +void CStringChecker::evalMempcpy(CheckerContext &C, const CallExpr *CE, + CharKind CK) const { // void *mempcpy(void *restrict dst, const void *restrict src, size_t n); // The return value is a pointer to the byte following the last written byte. DestinationArgExpr Dest = {CE->getArg(0), 0}; @@ -1313,10 +1337,11 @@ void CStringChecker::evalMempcpy(CheckerContext &C, const CallExpr *CE) const { constexpr bool IsRestricted = true; constexpr bool IsMempcpy = true; evalCopyCommon(C, CE, C.getState(), Size, Dest, Src, IsRestricted, IsMempcpy, - false); + CK); } -void CStringChecker::evalMemmove(CheckerContext &C, const CallExpr *CE) const { +void CStringChecker::evalMemmove(CheckerContext &C, const CallExpr *CE, + CharKind CK) const { // void *memmove(void *dst, const void *src, size_t n); // The return value is the address of the destination buffer. DestinationArgExpr Dest = {CE->getArg(0), 0}; @@ -1326,7 +1351,7 @@ void CStringChecker::evalMemmove(CheckerContext &C, const CallExpr *CE) const { constexpr bool IsRestricted = false; constexpr bool IsMempcpy = false; evalCopyCommon(C, CE, C.getState(), Size, Dest, Src, IsRestricted, IsMempcpy, - false); + CK); } void CStringChecker::evalBcopy(CheckerContext &C, const CallExpr *CE) const { @@ -1338,10 +1363,11 @@ void CStringChecker::evalBcopy(CheckerContext &C, const CallExpr *CE) const { constexpr bool IsRestricted = false; constexpr bool IsMempcpy = false; evalCopyCommon(C, CE, C.getState(), Size, Dest, Src, IsRestricted, IsMempcpy, - false); + CharKind::Regular); } -void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const { +void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE, + CharKind CK) const { // int memcmp(const void *s1, const void *s2, size_t n); CurrentFunctionDescription = "memory comparison function"; @@ -1401,8 +1427,8 @@ void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const { // If the two arguments might be different buffers, we have to check // the size of both of them. assert(NotSameBuffer); - State = CheckBufferAccess(C, State, Right, Size, AccessKind::read); - State = CheckBufferAccess(C, State, Left, Size, AccessKind::read); + State = CheckBufferAccess(C, State, Right, Size, AccessKind::read, CK); + State = CheckBufferAccess(C, State, Left, Size, AccessKind::read, CK); if (State) { // The return value is the comparison result, which we don't know. SVal CmpV = Builder.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount()); @@ -1481,8 +1507,8 @@ void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE, const Expr *maxlenExpr = CE->getArg(1); SVal maxlenVal = state->getSVal(maxlenExpr, LCtx); - Optional<NonLoc> strLengthNL = strLength.getAs<NonLoc>(); - Optional<NonLoc> maxlenValNL = maxlenVal.getAs<NonLoc>(); + std::optional<NonLoc> strLengthNL = strLength.getAs<NonLoc>(); + std::optional<NonLoc> maxlenValNL = maxlenVal.getAs<NonLoc>(); if (strLengthNL && maxlenValNL) { ProgramStateRef stateStringTooLong, stateStringNotTooLong; @@ -1630,11 +1656,11 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, // Get the string length of the source. SVal strLength = getCStringLength(C, state, srcExpr.Expression, srcVal); - Optional<NonLoc> strLengthNL = strLength.getAs<NonLoc>(); + std::optional<NonLoc> strLengthNL = strLength.getAs<NonLoc>(); // Get the string length of the destination buffer. SVal dstStrLength = getCStringLength(C, state, Dst.Expression, DstVal); - Optional<NonLoc> dstStrLengthNL = dstStrLength.getAs<NonLoc>(); + std::optional<NonLoc> dstStrLengthNL = dstStrLength.getAs<NonLoc>(); // If the source isn't a valid C string, give up. if (strLength.isUndef()) @@ -1672,7 +1698,7 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, lenVal = svalBuilder.evalCast(lenVal, sizeTy, lenExpr.Expression->getType()); - Optional<NonLoc> lenValNL = lenVal.getAs<NonLoc>(); + std::optional<NonLoc> lenValNL = lenVal.getAs<NonLoc>(); // If we know both values, we might be able to figure out how much // we're copying. @@ -1714,7 +1740,7 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, freeSpace = svalBuilder.evalBinOp(state, BO_Sub, freeSpace, svalBuilder.makeIntVal(1, sizeTy), sizeTy); - Optional<NonLoc> freeSpaceNL = freeSpace.getAs<NonLoc>(); + std::optional<NonLoc> freeSpaceNL = freeSpace.getAs<NonLoc>(); // While unlikely, it is possible that the subtraction is // too complex to compute, let's check whether it succeeded. @@ -1839,7 +1865,7 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, *dstStrLengthNL, sizeTy); } - Optional<NonLoc> amountCopiedNL = amountCopied.getAs<NonLoc>(); + std::optional<NonLoc> amountCopiedNL = amountCopied.getAs<NonLoc>(); // If we know both string lengths, we might know the final string length. if (amountCopiedNL && dstStrLengthNL) { @@ -1860,7 +1886,8 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, finalStrLength = getCStringLength(C, state, CE, DstVal, true); assert(!finalStrLength.isUndef()); - if (Optional<NonLoc> finalStrLengthNL = finalStrLength.getAs<NonLoc>()) { + if (std::optional<NonLoc> finalStrLengthNL = + finalStrLength.getAs<NonLoc>()) { if (amountCopiedNL && appendK == ConcatFnKind::none) { // we overwrite dst string with the src // finalStrLength >= srcStrLength @@ -1911,13 +1938,13 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, // If the destination is a MemRegion, try to check for a buffer overflow and // record the new string length. - if (Optional<loc::MemRegionVal> dstRegVal = - DstVal.getAs<loc::MemRegionVal>()) { + if (std::optional<loc::MemRegionVal> dstRegVal = + DstVal.getAs<loc::MemRegionVal>()) { QualType ptrTy = Dst.Expression->getType(); // If we have an exact value on a bounded copy, use that to check for // overflows, rather than our estimate about how much is actually copied. - if (Optional<NonLoc> maxLastNL = maxLastElementIndex.getAs<NonLoc>()) { + if (std::optional<NonLoc> maxLastNL = maxLastElementIndex.getAs<NonLoc>()) { SVal maxLastElement = svalBuilder.evalBinOpLN(state, BO_Add, *dstRegVal, *maxLastNL, ptrTy); @@ -1927,7 +1954,7 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, } // Then, if the final length is known... - if (Optional<NonLoc> knownStrLength = finalStrLength.getAs<NonLoc>()) { + if (std::optional<NonLoc> knownStrLength = finalStrLength.getAs<NonLoc>()) { SVal lastElement = svalBuilder.evalBinOpLN(state, BO_Add, *dstRegVal, *knownStrLength, ptrTy); @@ -2119,7 +2146,7 @@ void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE, DefinedSVal zeroVal = svalBuilder.makeIntVal(0, CE->getType()); // Constrain strcmp's result range based on the result of StringRef's // comparison methods. - BinaryOperatorKind op = (compareRes == 1) ? BO_GT : BO_LT; + BinaryOperatorKind op = (compareRes > 0) ? BO_GT : BO_LT; SVal compareWithZero = svalBuilder.evalBinOp(state, op, resultVal, zeroVal, svalBuilder.getConditionType()); @@ -2165,7 +2192,7 @@ void CStringChecker::evalStrsep(CheckerContext &C, const CallExpr *CE) const { SValBuilder &SVB = C.getSValBuilder(); SVal Result; - if (Optional<Loc> SearchStrLoc = SearchStrVal.getAs<Loc>()) { + if (std::optional<Loc> SearchStrLoc = SearchStrVal.getAs<Loc>()) { // Get the current value of the search string pointer, as a char*. Result = State->getSVal(*SearchStrLoc, CharPtrTy); diff --git a/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp index 45f9a82a9d0a..f02d20d45678 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp @@ -24,7 +24,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h" -#include "llvm/ADT/Optional.h" +#include <optional> #include <utility> using namespace clang; @@ -472,7 +472,7 @@ bool CastValueChecker::evalCall(const CallEvent &Call, const CastCheck &Check = Lookup->first; CallKind Kind = Lookup->second; - Optional<DefinedOrUnknownSVal> DV; + std::optional<DefinedOrUnknownSVal> DV; switch (Kind) { case CallKind::Function: { diff --git a/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp index 2d2ddcdf3890..3fcf6f435a43 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp @@ -45,6 +45,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" #include "llvm/Support/raw_ostream.h" +#include <optional> using namespace clang; using namespace ento; @@ -282,7 +283,7 @@ void ObjCDeallocChecker::checkBeginFunction( continue; SVal LVal = State->getLValue(PropImpl->getPropertyIvarDecl(), SelfVal); - Optional<Loc> LValLoc = LVal.getAs<Loc>(); + std::optional<Loc> LValLoc = LVal.getAs<Loc>(); if (!LValLoc) continue; @@ -953,7 +954,7 @@ ObjCDeallocChecker::getValueReleasedByNillingOut(const ObjCMethodCall &M, ProgramStateRef State = C.getState(); SVal LVal = State->getLValue(PropIvarDecl, ReceiverVal); - Optional<Loc> LValLoc = LVal.getAs<Loc>(); + std::optional<Loc> LValLoc = LVal.getAs<Loc>(); if (!LValLoc) return nullptr; @@ -1004,7 +1005,7 @@ bool ObjCDeallocChecker::instanceDeallocIsOnStack(const CheckerContext &C, return false; } -/// Returns true if the ID is a class in which which is known to have +/// Returns true if the ID is a class in which is known to have /// a separate teardown lifecycle. In this case, -dealloc warnings /// about missing releases should be suppressed. bool ObjCDeallocChecker::classHasSeparateTeardown( diff --git a/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp index ce8d6c879870..9ace1583eb53 100644 --- a/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp @@ -43,7 +43,7 @@ class ChrootChecker : public Checker<eval::Call, check::PreCall> { // This bug refers to possibly break out of a chroot() jail. mutable std::unique_ptr<BuiltinBug> BT_BreakJail; - const CallDescription Chroot{"chroot", 1}, Chdir{"chdir", 1}; + const CallDescription Chroot{{"chroot"}, 1}, Chdir{{"chdir"}, 1}; public: ChrootChecker() {} diff --git a/clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp index 77a3218f55fb..67962f75f9bf 100644 --- a/clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp @@ -72,26 +72,26 @@ public: SVal) const; CallDescriptionMap<NoItParamFn> NoIterParamFunctions = { - {{"clear", 0}, &ContainerModeling::handleClear}, - {{"assign", 2}, &ContainerModeling::handleAssign}, - {{"push_back", 1}, &ContainerModeling::handlePushBack}, - {{"emplace_back", 1}, &ContainerModeling::handlePushBack}, - {{"pop_back", 0}, &ContainerModeling::handlePopBack}, - {{"push_front", 1}, &ContainerModeling::handlePushFront}, - {{"emplace_front", 1}, &ContainerModeling::handlePushFront}, - {{"pop_front", 0}, &ContainerModeling::handlePopFront}, + {{{"clear"}, 0}, &ContainerModeling::handleClear}, + {{{"assign"}, 2}, &ContainerModeling::handleAssign}, + {{{"push_back"}, 1}, &ContainerModeling::handlePushBack}, + {{{"emplace_back"}, 1}, &ContainerModeling::handlePushBack}, + {{{"pop_back"}, 0}, &ContainerModeling::handlePopBack}, + {{{"push_front"}, 1}, &ContainerModeling::handlePushFront}, + {{{"emplace_front"}, 1}, &ContainerModeling::handlePushFront}, + {{{"pop_front"}, 0}, &ContainerModeling::handlePopFront}, }; CallDescriptionMap<OneItParamFn> OneIterParamFunctions = { - {{"insert", 2}, &ContainerModeling::handleInsert}, - {{"emplace", 2}, &ContainerModeling::handleInsert}, - {{"erase", 1}, &ContainerModeling::handleErase}, - {{"erase_after", 1}, &ContainerModeling::handleEraseAfter}, + {{{"insert"}, 2}, &ContainerModeling::handleInsert}, + {{{"emplace"}, 2}, &ContainerModeling::handleInsert}, + {{{"erase"}, 1}, &ContainerModeling::handleErase}, + {{{"erase_after"}, 1}, &ContainerModeling::handleEraseAfter}, }; CallDescriptionMap<TwoItParamFn> TwoIterParamFunctions = { - {{"erase", 2}, &ContainerModeling::handleErase}, - {{"erase_after", 2}, &ContainerModeling::handleEraseAfter}, + {{{"erase"}, 2}, &ContainerModeling::handleErase}, + {{{"erase_after"}, 2}, &ContainerModeling::handleEraseAfter}, }; }; diff --git a/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp index 2102f9233bc1..01b662064d7b 100644 --- a/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp @@ -38,17 +38,17 @@ public: llvm::DenseSet<const VarDecl *> &S; bool TraverseObjCAtFinallyStmt(ObjCAtFinallyStmt *S) { - SaveAndRestore<bool> inFinally(inEH, true); + SaveAndRestore inFinally(inEH, true); return ::RecursiveASTVisitor<EHCodeVisitor>::TraverseObjCAtFinallyStmt(S); } bool TraverseObjCAtCatchStmt(ObjCAtCatchStmt *S) { - SaveAndRestore<bool> inCatch(inEH, true); + SaveAndRestore inCatch(inEH, true); return ::RecursiveASTVisitor<EHCodeVisitor>::TraverseObjCAtCatchStmt(S); } bool TraverseCXXCatchStmt(CXXCatchStmt *S) { - SaveAndRestore<bool> inCatch(inEH, true); + SaveAndRestore inCatch(inEH, true); return TraverseStmt(S->getHandlerBlock()); } @@ -103,8 +103,8 @@ void ReachableCode::computeReachableBlocks() { static const Expr * LookThroughTransitiveAssignmentsAndCommaOperators(const Expr *Ex) { while (Ex) { - const BinaryOperator *BO = - dyn_cast<BinaryOperator>(Ex->IgnoreParenCasts()); + Ex = Ex->IgnoreParenCasts(); + const BinaryOperator *BO = dyn_cast<BinaryOperator>(Ex); if (!BO) break; BinaryOperatorKind Op = BO->getOpcode(); @@ -240,7 +240,7 @@ public: case DeadIncrement: BugType = "Dead increment"; - LLVM_FALLTHROUGH; + [[fallthrough]]; case Standard: if (!BugType) BugType = "Dead assignment"; os << "Value stored to '" << *V << "' is never read"; @@ -331,8 +331,7 @@ public: // Special case: check for assigning null to a pointer. // This is a common form of defensive programming. const Expr *RHS = - LookThroughTransitiveAssignmentsAndCommaOperators(B->getRHS()); - RHS = RHS->IgnoreParenCasts(); + LookThroughTransitiveAssignmentsAndCommaOperators(B->getRHS()); QualType T = VD->getType(); if (T.isVolatileQualified()) @@ -415,8 +414,7 @@ public: if (isConstant(E)) return; - if (const DeclRefExpr *DRE = - dyn_cast<DeclRefExpr>(E->IgnoreParenCasts())) + if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) if (const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl())) { // Special case: check for initialization from constant // variables. @@ -438,7 +436,7 @@ public: PathDiagnosticLocation Loc = PathDiagnosticLocation::create(V, BR.getSourceManager()); - Report(V, DeadInit, Loc, E->getSourceRange()); + Report(V, DeadInit, Loc, V->getInit()->getSourceRange()); } } } @@ -450,8 +448,9 @@ private: bool isConstant(const InitListExpr *Candidate) const { // We consider init list to be constant if each member of the list can be // interpreted as constant. - return llvm::all_of(Candidate->inits(), - [this](const Expr *Init) { return isConstant(Init); }); + return llvm::all_of(Candidate->inits(), [this](const Expr *Init) { + return isConstant(Init->IgnoreParenCasts()); + }); } /// Return true if the given expression can be interpreted as constant @@ -461,7 +460,7 @@ private: return true; // We should also allow defensive initialization of structs, i.e. { 0 } - if (const auto *ILE = dyn_cast<InitListExpr>(E->IgnoreParenCasts())) { + if (const auto *ILE = dyn_cast<InitListExpr>(E)) { return isConstant(ILE); } @@ -504,7 +503,7 @@ public: // Treat local variables captured by reference in C++ lambdas as escaped. void findLambdaReferenceCaptures(const LambdaExpr *LE) { const CXXRecordDecl *LambdaClass = LE->getLambdaClass(); - llvm::DenseMap<const VarDecl *, FieldDecl *> CaptureFields; + llvm::DenseMap<const ValueDecl *, FieldDecl *> CaptureFields; FieldDecl *ThisCaptureField; LambdaClass->getCaptureFields(CaptureFields, ThisCaptureField); @@ -512,14 +511,14 @@ public: if (!C.capturesVariable()) continue; - VarDecl *VD = C.getCapturedVar(); + ValueDecl *VD = C.getCapturedVar(); const FieldDecl *FD = CaptureFields[VD]; - if (!FD) + if (!FD || !isa<VarDecl>(VD)) continue; // If the capture field is a reference type, it is capture-by-reference. if (FD->getType()->isReferenceType()) - Escaped.insert(VD); + Escaped.insert(cast<VarDecl>(VD)); } } }; diff --git a/clang/lib/StaticAnalyzer/Checkers/DebugContainerModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/DebugContainerModeling.cpp index 47fd57c7db9b..832bb78c4803 100644 --- a/clang/lib/StaticAnalyzer/Checkers/DebugContainerModeling.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/DebugContainerModeling.cpp @@ -41,9 +41,9 @@ class DebugContainerModeling CheckerContext &) const; CallDescriptionMap<FnCheck> Callbacks = { - {{"clang_analyzer_container_begin", 1}, + {{{"clang_analyzer_container_begin"}, 1}, &DebugContainerModeling::analyzerContainerBegin}, - {{"clang_analyzer_container_end", 1}, + {{{"clang_analyzer_container_end"}, 1}, &DebugContainerModeling::analyzerContainerEnd}, }; diff --git a/clang/lib/StaticAnalyzer/Checkers/DebugIteratorModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/DebugIteratorModeling.cpp index 6add9a007a87..d05298b42c55 100644 --- a/clang/lib/StaticAnalyzer/Checkers/DebugIteratorModeling.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/DebugIteratorModeling.cpp @@ -42,11 +42,11 @@ class DebugIteratorModeling CheckerContext &) const; CallDescriptionMap<FnCheck> Callbacks = { - {{"clang_analyzer_iterator_position", 1}, + {{{"clang_analyzer_iterator_position"}, 1}, &DebugIteratorModeling::analyzerIteratorPosition}, - {{"clang_analyzer_iterator_container", 1}, + {{{"clang_analyzer_iterator_container"}, 1}, &DebugIteratorModeling::analyzerIteratorContainer}, - {{"clang_analyzer_iterator_validity", 1}, + {{{"clang_analyzer_iterator_validity"}, 1}, &DebugIteratorModeling::analyzerIteratorValidity}, }; diff --git a/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp index bfd1d4c162ec..cc01e97d3fa2 100644 --- a/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp @@ -17,6 +17,7 @@ #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include <optional> using namespace clang; using namespace ento; @@ -67,7 +68,7 @@ void DivZeroChecker::checkPreStmt(const BinaryOperator *B, return; SVal Denom = C.getSVal(B->getRHS()); - Optional<DefinedSVal> DV = Denom.getAs<DefinedSVal>(); + std::optional<DefinedSVal> DV = Denom.getAs<DefinedSVal>(); // Divide-by-undefined handled in the generic checking for uses of // undefined values. diff --git a/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp b/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp index aaf8cca32b60..6f26842e62c7 100644 --- a/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp @@ -31,6 +31,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include <optional> using namespace clang; using namespace ento; @@ -846,7 +847,7 @@ void DynamicTypePropagation::checkPreObjCMessage(const ObjCMethodCall &M, return; } - Optional<ArrayRef<QualType>> TypeArgs = + std::optional<ArrayRef<QualType>> TypeArgs = (*TrackedType)->getObjCSubstitutions(Method->getDeclContext()); // This case might happen when there is an unspecialized override of a // specialized method. @@ -979,7 +980,7 @@ void DynamicTypePropagation::checkPostObjCMessage(const ObjCMethodCall &M, if (!Method) return; - Optional<ArrayRef<QualType>> TypeArgs = + std::optional<ArrayRef<QualType>> TypeArgs = (*TrackedType)->getObjCSubstitutions(Method->getDeclContext()); if (!TypeArgs) return; diff --git a/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp index e5088fb266bc..1077ceb6288e 100644 --- a/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp @@ -22,6 +22,7 @@ #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include <optional> using namespace clang; using namespace ento; @@ -108,7 +109,7 @@ void EnumCastOutOfRangeChecker::checkPreStmt(const CastExpr *CE, } // Get the value of the expression to cast. - const llvm::Optional<DefinedOrUnknownSVal> ValueToCast = + const std::optional<DefinedOrUnknownSVal> ValueToCast = C.getSVal(CE->getSubExpr()).getAs<DefinedOrUnknownSVal>(); // If the value cannot be reasoned about (not even a DefinedOrUnknownSVal), diff --git a/clang/lib/StaticAnalyzer/Checkers/ErrnoChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ErrnoChecker.cpp index d8c3d7a4a6a6..265185e64107 100644 --- a/clang/lib/StaticAnalyzer/Checkers/ErrnoChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/ErrnoChecker.cpp @@ -22,6 +22,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" #include "llvm/ADT/STLExtras.h" +#include <optional> using namespace clang; using namespace ento; @@ -132,7 +133,7 @@ void ErrnoChecker::generateErrnoNotCheckedBug( void ErrnoChecker::checkLocation(SVal Loc, bool IsLoad, const Stmt *S, CheckerContext &C) const { - Optional<ento::Loc> ErrnoLoc = getErrnoLoc(C.getState()); + std::optional<ento::Loc> ErrnoLoc = getErrnoLoc(C.getState()); if (!ErrnoLoc) return; @@ -206,7 +207,7 @@ void ErrnoChecker::checkPreCall(const CallEvent &Call, C.getSourceManager().isInSystemHeader(CallF->getLocation()) && !isErrno(CallF)) { if (getErrnoState(C.getState()) == MustBeChecked) { - Optional<ento::Loc> ErrnoLoc = getErrnoLoc(C.getState()); + std::optional<ento::Loc> ErrnoLoc = getErrnoLoc(C.getState()); assert(ErrnoLoc && "ErrnoLoc should exist if an errno state is set."); generateErrnoNotCheckedBug(C, setErrnoStateIrrelevant(C.getState()), ErrnoLoc->getAsRegion(), &Call); @@ -219,7 +220,7 @@ ProgramStateRef ErrnoChecker::checkRegionChanges( ArrayRef<const MemRegion *> ExplicitRegions, ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx, const CallEvent *Call) const { - Optional<ento::Loc> ErrnoLoc = getErrnoLoc(State); + std::optional<ento::Loc> ErrnoLoc = getErrnoLoc(State); if (!ErrnoLoc) return State; const MemRegion *ErrnoRegion = ErrnoLoc->getAsRegion(); @@ -227,12 +228,12 @@ ProgramStateRef ErrnoChecker::checkRegionChanges( // If 'errno' is invalidated we can not know if it is checked or written into, // allow read and write without bug reports. if (llvm::is_contained(Regions, ErrnoRegion)) - return setErrnoStateIrrelevant(State); + return clearErrnoState(State); // Always reset errno state when the system memory space is invalidated. // The ErrnoRegion is not always found in the list in this case. if (llvm::is_contained(Regions, ErrnoRegion->getMemorySpace())) - return setErrnoStateIrrelevant(State); + return clearErrnoState(State); return State; } diff --git a/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp index 618f7e97f6e8..51f39c606d5c 100644 --- a/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp @@ -28,6 +28,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" #include "llvm/ADT/STLExtras.h" +#include <optional> using namespace clang; using namespace ento; @@ -57,11 +58,11 @@ public: private: // FIXME: Names from `ErrnoLocationFuncNames` are used to build this set. - CallDescriptionSet ErrnoLocationCalls{{"__errno_location", 0, 0}, - {"___errno", 0, 0}, - {"__errno", 0, 0}, - {"_errno", 0, 0}, - {"__error", 0, 0}}; + CallDescriptionSet ErrnoLocationCalls{{{"__errno_location"}, 0, 0}, + {{"___errno"}, 0, 0}, + {{"__errno"}, 0, 0}, + {{"_errno"}, 0, 0}, + {{"__error"}, 0, 0}}; }; } // namespace @@ -207,7 +208,7 @@ namespace clang { namespace ento { namespace errno_modeling { -Optional<SVal> getErrnoValue(ProgramStateRef State) { +std::optional<SVal> getErrnoValue(ProgramStateRef State) { const MemRegion *ErrnoR = State->get<ErrnoRegion>(); if (!ErrnoR) return {}; @@ -239,19 +240,23 @@ ProgramStateRef setErrnoValue(ProgramStateRef State, CheckerContext &C, return State->set<ErrnoState>(EState); } -Optional<Loc> getErrnoLoc(ProgramStateRef State) { +std::optional<Loc> getErrnoLoc(ProgramStateRef State) { const MemRegion *ErrnoR = State->get<ErrnoRegion>(); if (!ErrnoR) return {}; return loc::MemRegionVal{ErrnoR}; } +ErrnoCheckState getErrnoState(ProgramStateRef State) { + return State->get<ErrnoState>(); +} + ProgramStateRef setErrnoState(ProgramStateRef State, ErrnoCheckState EState) { return State->set<ErrnoState>(EState); } -ErrnoCheckState getErrnoState(ProgramStateRef State) { - return State->get<ErrnoState>(); +ProgramStateRef clearErrnoState(ProgramStateRef State) { + return setErrnoState(State, Irrelevant); } bool isErrno(const Decl *D) { @@ -264,6 +269,12 @@ bool isErrno(const Decl *D) { return false; } +const char *describeErrnoCheckState(ErrnoCheckState CS) { + assert(CS == errno_modeling::MustNotBeChecked && + "Errno description not applicable."); + return "may be undefined after the call and should not be used"; +} + const NoteTag *getErrnoNoteTag(CheckerContext &C, const std::string &Message) { return C.getNoteTag([Message](PathSensitiveBugReport &BR) -> std::string { const MemRegion *ErrnoR = BR.getErrorNode()->getState()->get<ErrnoRegion>(); @@ -275,6 +286,53 @@ const NoteTag *getErrnoNoteTag(CheckerContext &C, const std::string &Message) { }); } +ProgramStateRef setErrnoForStdSuccess(ProgramStateRef State, + CheckerContext &C) { + return setErrnoState(State, MustNotBeChecked); +} + +ProgramStateRef setErrnoForStdFailure(ProgramStateRef State, CheckerContext &C, + NonLoc ErrnoSym) { + SValBuilder &SVB = C.getSValBuilder(); + NonLoc ZeroVal = SVB.makeZeroVal(C.getASTContext().IntTy).castAs<NonLoc>(); + DefinedOrUnknownSVal Cond = + SVB.evalBinOp(State, BO_NE, ErrnoSym, ZeroVal, SVB.getConditionType()) + .castAs<DefinedOrUnknownSVal>(); + State = State->assume(Cond, true); + if (!State) + return nullptr; + return setErrnoValue(State, C.getLocationContext(), ErrnoSym, Irrelevant); +} + +ProgramStateRef setErrnoStdMustBeChecked(ProgramStateRef State, + CheckerContext &C, + const Expr *InvalE) { + const MemRegion *ErrnoR = State->get<ErrnoRegion>(); + if (!ErrnoR) + return State; + State = State->invalidateRegions(ErrnoR, InvalE, C.blockCount(), + C.getLocationContext(), false); + if (!State) + return nullptr; + return setErrnoState(State, MustBeChecked); +} + +const NoteTag *getNoteTagForStdSuccess(CheckerContext &C, llvm::StringRef Fn) { + return getErrnoNoteTag( + C, (Twine("Assuming that function '") + Twine(Fn) + + Twine("' is successful, in this case the value 'errno' ") + + Twine(describeErrnoCheckState(MustNotBeChecked))) + .str()); +} + +const NoteTag *getNoteTagForStdMustBeChecked(CheckerContext &C, + llvm::StringRef Fn) { + return getErrnoNoteTag( + C, (Twine("Function '") + Twine(Fn) + + Twine("' indicates failure only by setting of 'errno'")) + .str()); +} + } // namespace errno_modeling } // namespace ento } // namespace clang diff --git a/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h b/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h index 3757e25e1afe..2ca3979944e3 100644 --- a/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h +++ b/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h @@ -16,35 +16,43 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include <optional> namespace clang { namespace ento { namespace errno_modeling { +/// Describe how reads and writes of \c errno are handled by the checker. enum ErrnoCheckState : unsigned { /// We do not know anything about 'errno'. + /// Read and write is always allowed. Irrelevant = 0, /// Value of 'errno' should be checked to find out if a previous function call /// has failed. + /// When this state is set \c errno must be read by the program before a next + /// standard function call or other overwrite of \c errno follows, otherwise + /// a bug report is emitted. MustBeChecked = 1, /// Value of 'errno' is not allowed to be read, it can contain an unspecified /// value. + /// When this state is set \c errno is not allowed to be read by the program + /// until it is overwritten or invalidated. MustNotBeChecked = 2 }; /// Returns the value of 'errno', if 'errno' was found in the AST. -llvm::Optional<SVal> getErrnoValue(ProgramStateRef State); +std::optional<SVal> getErrnoValue(ProgramStateRef State); /// Returns the errno check state, \c Errno_Irrelevant if 'errno' was not found /// (this is not the only case for that value). ErrnoCheckState getErrnoState(ProgramStateRef State); /// Returns the location that points to the \c MemoryRegion where the 'errno' -/// value is stored. Returns \c None if 'errno' was not found. Otherwise it -/// always returns a valid memory region in the system global memory space. -llvm::Optional<Loc> getErrnoLoc(ProgramStateRef State); +/// value is stored. Returns \c std::nullopt if 'errno' was not found. Otherwise +/// it always returns a valid memory region in the system global memory space. +std::optional<Loc> getErrnoLoc(ProgramStateRef State); /// Set value of 'errno' to any SVal, if possible. /// The errno check state is set always when the 'errno' value is set. @@ -60,6 +68,9 @@ ProgramStateRef setErrnoValue(ProgramStateRef State, CheckerContext &C, /// Set the errno check state, do not modify the errno value. ProgramStateRef setErrnoState(ProgramStateRef State, ErrnoCheckState EState); +/// Clear state of errno (make it irrelevant). +ProgramStateRef clearErrnoState(ProgramStateRef State); + /// Determine if a `Decl` node related to 'errno'. /// This is true if the declaration is the errno variable or a function /// that returns a pointer to the 'errno' value (usually the 'errno' macro is @@ -67,10 +78,53 @@ ProgramStateRef setErrnoState(ProgramStateRef State, ErrnoCheckState EState); /// declaration. bool isErrno(const Decl *D); +/// Produce a textual description about how \c errno is allowed to be used +/// (in a \c ErrnoCheckState). +/// The returned string is insertable into a longer warning message in the form +/// "the value 'errno' <...>". +/// Currently only the \c errno_modeling::MustNotBeChecked state is supported, +/// others are not used by the clients. +const char *describeErrnoCheckState(ErrnoCheckState CS); + /// Create a NoteTag that displays the message if the 'errno' memory region is /// marked as interesting, and resets the interestingness. const NoteTag *getErrnoNoteTag(CheckerContext &C, const std::string &Message); +/// Set errno state for the common case when a standard function is successful. +/// Set \c ErrnoCheckState to \c MustNotBeChecked (the \c errno value is not +/// affected). At the state transition a note tag created by +/// \c getNoteTagForStdSuccess can be used. +ProgramStateRef setErrnoForStdSuccess(ProgramStateRef State, CheckerContext &C); + +/// Set errno state for the common case when a standard function fails. +/// Set \c errno value to be not equal to zero and \c ErrnoCheckState to +/// \c Irrelevant . The irrelevant errno state ensures that no related bug +/// report is emitted later and no note tag is needed. +/// \arg \c ErrnoSym Value to be used for \c errno and constrained to be +/// non-zero. +ProgramStateRef setErrnoForStdFailure(ProgramStateRef State, CheckerContext &C, + NonLoc ErrnoSym); + +/// Set errno state for the common case when a standard function indicates +/// failure only by \c errno. Sets \c ErrnoCheckState to \c MustBeChecked, and +/// invalidates the errno region (clear of previous value). +/// At the state transition a note tag created by +/// \c getNoteTagForStdMustBeChecked can be used. +/// \arg \c InvalE Expression that causes invalidation of \c errno. +ProgramStateRef setErrnoStdMustBeChecked(ProgramStateRef State, + CheckerContext &C, const Expr *InvalE); + +/// Generate the note tag that can be applied at the state generated by +/// \c setErrnoForStdSuccess . +/// \arg \c Fn Name of the (standard) function that is modeled. +const NoteTag *getNoteTagForStdSuccess(CheckerContext &C, llvm::StringRef Fn); + +/// Generate the note tag that can be applied at the state generated by +/// \c setErrnoStdMustBeChecked . +/// \arg \c Fn Name of the (standard) function that is modeled. +const NoteTag *getNoteTagForStdMustBeChecked(CheckerContext &C, + llvm::StringRef Fn); + } // namespace errno_modeling } // namespace ento } // namespace clang diff --git a/clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp index 924407ebe2d1..c46ebee0c94f 100644 --- a/clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp @@ -17,6 +17,7 @@ #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include <optional> using namespace clang; using namespace ento; @@ -69,13 +70,13 @@ private: using EvalFn = std::function<void(CheckerContext &, const CallEvent &)>; const CallDescriptionMap<EvalFn> TestCalls{ - {{"ErrnoTesterChecker_setErrno", 1}, &ErrnoTesterChecker::evalSetErrno}, - {{"ErrnoTesterChecker_getErrno", 0}, &ErrnoTesterChecker::evalGetErrno}, - {{"ErrnoTesterChecker_setErrnoIfError", 0}, + {{{"ErrnoTesterChecker_setErrno"}, 1}, &ErrnoTesterChecker::evalSetErrno}, + {{{"ErrnoTesterChecker_getErrno"}, 0}, &ErrnoTesterChecker::evalGetErrno}, + {{{"ErrnoTesterChecker_setErrnoIfError"}, 0}, &ErrnoTesterChecker::evalSetErrnoIfError}, - {{"ErrnoTesterChecker_setErrnoIfErrorRange", 0}, + {{{"ErrnoTesterChecker_setErrnoIfErrorRange"}, 0}, &ErrnoTesterChecker::evalSetErrnoIfErrorRange}, - {{"ErrnoTesterChecker_setErrnoCheckState", 0}, + {{{"ErrnoTesterChecker_setErrnoCheckState"}, 0}, &ErrnoTesterChecker::evalSetErrnoCheckState}}; }; @@ -91,7 +92,7 @@ void ErrnoTesterChecker::evalGetErrno(CheckerContext &C, const CallEvent &Call) { ProgramStateRef State = C.getState(); - Optional<SVal> ErrnoVal = getErrnoValue(State); + std::optional<SVal> ErrnoVal = getErrnoValue(State); assert(ErrnoVal && "Errno value should be available."); State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), *ErrnoVal); diff --git a/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp index ec1b0a70d7d3..355e9c2238a4 100644 --- a/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp @@ -17,6 +17,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/ScopedPrinter.h" +#include <optional> using namespace clang; using namespace ento; @@ -58,9 +59,9 @@ class ExprInspectionChecker // Optional parameter `ExprVal` for expression value to be marked interesting. ExplodedNode *reportBug(llvm::StringRef Msg, CheckerContext &C, - Optional<SVal> ExprVal = None) const; + std::optional<SVal> ExprVal = std::nullopt) const; ExplodedNode *reportBug(llvm::StringRef Msg, BugReporter &BR, ExplodedNode *N, - Optional<SVal> ExprVal = None) const; + std::optional<SVal> ExprVal = std::nullopt) const; template <typename T> void printAndReport(CheckerContext &C, T What) const; const Expr *getArgExpr(const CallExpr *CE, CheckerContext &C) const; @@ -115,7 +116,8 @@ bool ExprInspectionChecker::evalCall(const CallEvent &Call, .Case("clang_analyzer_hashDump", &ExprInspectionChecker::analyzerHashDump) .Case("clang_analyzer_denote", &ExprInspectionChecker::analyzerDenote) - .Case("clang_analyzer_express", + .Case("clang_analyzer_express", // This also marks the argument as + // interesting. &ExprInspectionChecker::analyzerExpress) .StartsWith("clang_analyzer_isTainted", &ExprInspectionChecker::analyzerIsTainted) @@ -160,17 +162,18 @@ static const char *getArgumentValueString(const CallExpr *CE, } } -ExplodedNode *ExprInspectionChecker::reportBug(llvm::StringRef Msg, - CheckerContext &C, - Optional<SVal> ExprVal) const { +ExplodedNode * +ExprInspectionChecker::reportBug(llvm::StringRef Msg, CheckerContext &C, + std::optional<SVal> ExprVal) const { ExplodedNode *N = C.generateNonFatalErrorNode(); reportBug(Msg, C.getBugReporter(), N, ExprVal); return N; } -ExplodedNode *ExprInspectionChecker::reportBug(llvm::StringRef Msg, - BugReporter &BR, ExplodedNode *N, - Optional<SVal> ExprVal) const { +ExplodedNode * +ExprInspectionChecker::reportBug(llvm::StringRef Msg, BugReporter &BR, + ExplodedNode *N, + std::optional<SVal> ExprVal) const { if (!N) return nullptr; @@ -354,8 +357,7 @@ void ExprInspectionChecker::analyzerDumpElementCount(const CallExpr *CE, if (const auto *TVR = MR->getAs<TypedValueRegion>()) { ElementTy = TVR->getValueType(); } else { - ElementTy = - MR->castAs<SymbolicRegion>()->getSymbol()->getType()->getPointeeType(); + ElementTy = MR->castAs<SymbolicRegion>()->getPointeeStaticType(); } assert(!ElementTy->isPointerType()); @@ -466,58 +468,60 @@ void ExprInspectionChecker::analyzerDenote(const CallExpr *CE, namespace { class SymbolExpressor - : public SymExprVisitor<SymbolExpressor, Optional<std::string>> { + : public SymExprVisitor<SymbolExpressor, std::optional<std::string>> { ProgramStateRef State; public: SymbolExpressor(ProgramStateRef State) : State(State) {} - Optional<std::string> lookup(const SymExpr *S) { + std::optional<std::string> lookup(const SymExpr *S) { if (const StringLiteral *const *SLPtr = State->get<DenotedSymbols>(S)) { const StringLiteral *SL = *SLPtr; return std::string(SL->getBytes()); } - return None; + return std::nullopt; } - Optional<std::string> VisitSymExpr(const SymExpr *S) { return lookup(S); } + std::optional<std::string> VisitSymExpr(const SymExpr *S) { + return lookup(S); + } - Optional<std::string> VisitSymIntExpr(const SymIntExpr *S) { - if (Optional<std::string> Str = lookup(S)) + std::optional<std::string> VisitSymIntExpr(const SymIntExpr *S) { + if (std::optional<std::string> Str = lookup(S)) return Str; - if (Optional<std::string> Str = Visit(S->getLHS())) + if (std::optional<std::string> Str = Visit(S->getLHS())) return (*Str + " " + BinaryOperator::getOpcodeStr(S->getOpcode()) + " " + std::to_string(S->getRHS().getLimitedValue()) + (S->getRHS().isUnsigned() ? "U" : "")) .str(); - return None; + return std::nullopt; } - Optional<std::string> VisitSymSymExpr(const SymSymExpr *S) { - if (Optional<std::string> Str = lookup(S)) + std::optional<std::string> VisitSymSymExpr(const SymSymExpr *S) { + if (std::optional<std::string> Str = lookup(S)) return Str; - if (Optional<std::string> Str1 = Visit(S->getLHS())) - if (Optional<std::string> Str2 = Visit(S->getRHS())) + if (std::optional<std::string> Str1 = Visit(S->getLHS())) + if (std::optional<std::string> Str2 = Visit(S->getRHS())) return (*Str1 + " " + BinaryOperator::getOpcodeStr(S->getOpcode()) + " " + *Str2) .str(); - return None; + return std::nullopt; } - Optional<std::string> VisitUnarySymExpr(const UnarySymExpr *S) { - if (Optional<std::string> Str = lookup(S)) + std::optional<std::string> VisitUnarySymExpr(const UnarySymExpr *S) { + if (std::optional<std::string> Str = lookup(S)) return Str; - if (Optional<std::string> Str = Visit(S->getOperand())) + if (std::optional<std::string> Str = Visit(S->getOperand())) return (UnaryOperator::getOpcodeStr(S->getOpcode()) + *Str).str(); - return None; + return std::nullopt; } - Optional<std::string> VisitSymbolCast(const SymbolCast *S) { - if (Optional<std::string> Str = lookup(S)) + std::optional<std::string> VisitSymbolCast(const SymbolCast *S) { + if (std::optional<std::string> Str = lookup(S)) return Str; - if (Optional<std::string> Str = Visit(S->getOperand())) + if (std::optional<std::string> Str = Visit(S->getOperand())) return (Twine("(") + S->getType().getAsString() + ")" + *Str).str(); - return None; + return std::nullopt; } }; } // namespace @@ -531,14 +535,14 @@ void ExprInspectionChecker::analyzerExpress(const CallExpr *CE, SVal ArgVal = C.getSVal(CE->getArg(0)); SymbolRef Sym = ArgVal.getAsSymbol(); if (!Sym) { - reportBug("Not a symbol", C); + reportBug("Not a symbol", C, ArgVal); return; } SymbolExpressor V(C.getState()); auto Str = V.Visit(Sym); if (!Str) { - reportBug("Unable to express", C); + reportBug("Unable to express", C, ArgVal); return; } diff --git a/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp index eb3b89ee6c5c..65ff1be8ec05 100644 --- a/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp @@ -101,6 +101,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" #include "llvm/ADT/StringExtras.h" +#include <optional> using namespace clang; using namespace ento; @@ -300,7 +301,7 @@ getFuchsiaHandleSymbols(QualType QT, SVal Arg, ProgramStateRef State) { } } else { assert(PtrToHandleLevel == 1); - if (Optional<Loc> ArgLoc = Arg.getAs<Loc>()) { + if (std::optional<Loc> ArgLoc = Arg.getAs<Loc>()) { SymbolRef Sym = State->getSVal(*ArgLoc).getAsSymbol(); if (Sym) { return {Sym}; diff --git a/clang/lib/StaticAnalyzer/Checkers/GTestChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/GTestChecker.cpp index dbfdff4d2a3b..43d2eeeb4b27 100644 --- a/clang/lib/StaticAnalyzer/Checkers/GTestChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/GTestChecker.cpp @@ -20,6 +20,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "llvm/Support/raw_ostream.h" +#include <optional> using namespace clang; using namespace ento; @@ -258,7 +259,7 @@ SVal GTestChecker::getAssertionResultSuccessFieldValue( if (!SuccessField) return UnknownVal(); - Optional<Loc> FieldLoc = + std::optional<Loc> FieldLoc = State->getLValue(SuccessField, Instance).getAs<Loc>(); if (!FieldLoc) return UnknownVal(); diff --git a/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp index f0a114801dda..f6e2f59d5697 100644 --- a/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp @@ -30,6 +30,7 @@ #include <limits> #include <memory> +#include <optional> #include <utility> #define DEBUG_TYPE "taint-checker" @@ -124,16 +125,17 @@ SVal getPointeeOf(const CheckerContext &C, Loc LValue) { } /// Given a pointer/reference argument, return the value it refers to. -Optional<SVal> getPointeeOf(const CheckerContext &C, SVal Arg) { +std::optional<SVal> getPointeeOf(const CheckerContext &C, SVal Arg) { if (auto LValue = Arg.getAs<Loc>()) return getPointeeOf(C, *LValue); - return None; + return std::nullopt; } /// Given a pointer, return the SVal of its pointee or if it is tainted, /// otherwise return the pointer's SVal if tainted. /// Also considers stdin as a taint source. -Optional<SVal> getTaintedPointeeOrPointer(const CheckerContext &C, SVal Arg) { +std::optional<SVal> getTaintedPointeeOrPointer(const CheckerContext &C, + SVal Arg) { const ProgramStateRef State = C.getState(); if (auto Pointee = getPointeeOf(C, Arg)) @@ -147,7 +149,7 @@ Optional<SVal> getTaintedPointeeOrPointer(const CheckerContext &C, SVal Arg) { if (isStdin(Arg, C.getASTContext())) return Arg; - return None; + return std::nullopt; } bool isTaintedOrPointsToTainted(const Expr *E, const ProgramStateRef &State, @@ -161,7 +163,8 @@ bool isTaintedOrPointsToTainted(const Expr *E, const ProgramStateRef &State, class ArgSet { public: ArgSet() = default; - ArgSet(ArgVecTy &&DiscreteArgs, Optional<ArgIdxTy> VariadicIndex = None) + ArgSet(ArgVecTy &&DiscreteArgs, + std::optional<ArgIdxTy> VariadicIndex = std::nullopt) : DiscreteArgs(std::move(DiscreteArgs)), VariadicIndex(std::move(VariadicIndex)) {} @@ -176,7 +179,7 @@ public: private: ArgVecTy DiscreteArgs; - Optional<ArgIdxTy> VariadicIndex; + std::optional<ArgIdxTy> VariadicIndex; }; /// A struct used to specify taint propagation rules for a function. @@ -197,12 +200,12 @@ class GenericTaintRule { ArgSet PropDstArgs; /// A message that explains why the call is sensitive to taint. - Optional<StringRef> SinkMsg; + std::optional<StringRef> SinkMsg; GenericTaintRule() = default; GenericTaintRule(ArgSet &&Sink, ArgSet &&Filter, ArgSet &&Src, ArgSet &&Dst, - Optional<StringRef> SinkMsg = None) + std::optional<StringRef> SinkMsg = std::nullopt) : SinkArgs(std::move(Sink)), FilterArgs(std::move(Filter)), PropSrcArgs(std::move(Src)), PropDstArgs(std::move(Dst)), SinkMsg(SinkMsg) {} @@ -211,7 +214,7 @@ public: /// Make a rule that reports a warning if taint reaches any of \p FilterArgs /// arguments. static GenericTaintRule Sink(ArgSet &&SinkArgs, - Optional<StringRef> Msg = None) { + std::optional<StringRef> Msg = std::nullopt) { return {std::move(SinkArgs), {}, {}, {}, Msg}; } @@ -232,9 +235,9 @@ public: } /// Make a rule that taints all PropDstArgs if any of PropSrcArgs is tainted. - static GenericTaintRule SinkProp(ArgSet &&SinkArgs, ArgSet &&SrcArgs, - ArgSet &&DstArgs, - Optional<StringRef> Msg = None) { + static GenericTaintRule + SinkProp(ArgSet &&SinkArgs, ArgSet &&SrcArgs, ArgSet &&DstArgs, + std::optional<StringRef> Msg = std::nullopt) { return { std::move(SinkArgs), {}, std::move(SrcArgs), std::move(DstArgs), Msg}; } @@ -302,7 +305,7 @@ struct GenericTaintRuleParser { TaintConfiguration &&Config) const; private: - using NamePartsTy = llvm::SmallVector<SmallString<32>, 2>; + using NamePartsTy = llvm::SmallVector<StringRef, 2>; /// Validate part of the configuration, which contains a list of argument /// indexes. @@ -359,8 +362,8 @@ private: // TODO: Remove separation to simplify matching logic once CallDescriptions // are more expressive. - mutable Optional<RuleLookupTy> StaticTaintRules; - mutable Optional<RuleLookupTy> DynamicTaintRules; + mutable std::optional<RuleLookupTy> StaticTaintRules; + mutable std::optional<RuleLookupTy> DynamicTaintRules; }; } // end of anonymous namespace @@ -442,10 +445,8 @@ GenericTaintRuleParser::parseNameParts(const Config &C) { if (!C.Scope.empty()) { // If the Scope argument contains multiple "::" parts, those are considered // namespace identifiers. - llvm::SmallVector<StringRef, 2> NSParts; - StringRef{C.Scope}.split(NSParts, "::", /*MaxSplit*/ -1, + StringRef{C.Scope}.split(NameParts, "::", /*MaxSplit*/ -1, /*KeepEmpty*/ false); - NameParts.append(NSParts.begin(), NSParts.end()); } NameParts.emplace_back(C.Name); return NameParts; @@ -456,10 +457,7 @@ void GenericTaintRuleParser::consumeRulesFromConfig(const Config &C, GenericTaintRule &&Rule, RulesContTy &Rules) { NamePartsTy NameParts = parseNameParts(C); - llvm::SmallVector<const char *, 2> CallDescParts{NameParts.size()}; - llvm::transform(NameParts, CallDescParts.begin(), - [](SmallString<32> &S) { return S.c_str(); }); - Rules.emplace_back(CallDescription(CallDescParts), std::move(Rule)); + Rules.emplace_back(CallDescription(NameParts), std::move(Rule)); } void GenericTaintRuleParser::parseConfig(const std::string &Option, @@ -485,10 +483,12 @@ void GenericTaintRuleParser::parseConfig(const std::string &Option, validateArgVector(Option, P.DstArgs); bool IsSrcVariadic = P.VarType == TaintConfiguration::VariadicType::Src; bool IsDstVariadic = P.VarType == TaintConfiguration::VariadicType::Dst; - Optional<ArgIdxTy> JustVarIndex = P.VarIndex; + std::optional<ArgIdxTy> JustVarIndex = P.VarIndex; - ArgSet SrcDesc(std::move(P.SrcArgs), IsSrcVariadic ? JustVarIndex : None); - ArgSet DstDesc(std::move(P.DstArgs), IsDstVariadic ? JustVarIndex : None); + ArgSet SrcDesc(std::move(P.SrcArgs), + IsSrcVariadic ? JustVarIndex : std::nullopt); + ArgSet DstDesc(std::move(P.DstArgs), + IsDstVariadic ? JustVarIndex : std::nullopt); consumeRulesFromConfig( P, GenericTaintRule::Prop(std::move(SrcDesc), std::move(DstDesc)), Rules); @@ -527,128 +527,128 @@ void GenericTaintChecker::initTaintRules(CheckerContext &C) const { RulesConstructionTy GlobalCRules{ // Sources - {{"fdopen"}, TR::Source({{ReturnValueIndex}})}, - {{"fopen"}, TR::Source({{ReturnValueIndex}})}, - {{"freopen"}, TR::Source({{ReturnValueIndex}})}, - {{"getch"}, TR::Source({{ReturnValueIndex}})}, - {{"getchar"}, TR::Source({{ReturnValueIndex}})}, - {{"getchar_unlocked"}, TR::Source({{ReturnValueIndex}})}, - {{"gets"}, TR::Source({{0}, ReturnValueIndex})}, - {{"gets_s"}, TR::Source({{0}, ReturnValueIndex})}, - {{"scanf"}, TR::Source({{}, 1})}, - {{"scanf_s"}, TR::Source({{}, {1}})}, - {{"wgetch"}, TR::Source({{}, ReturnValueIndex})}, + {{{"fdopen"}}, TR::Source({{ReturnValueIndex}})}, + {{{"fopen"}}, TR::Source({{ReturnValueIndex}})}, + {{{"freopen"}}, TR::Source({{ReturnValueIndex}})}, + {{{"getch"}}, TR::Source({{ReturnValueIndex}})}, + {{{"getchar"}}, TR::Source({{ReturnValueIndex}})}, + {{{"getchar_unlocked"}}, TR::Source({{ReturnValueIndex}})}, + {{{"gets"}}, TR::Source({{0}, ReturnValueIndex})}, + {{{"gets_s"}}, TR::Source({{0}, ReturnValueIndex})}, + {{{"scanf"}}, TR::Source({{}, 1})}, + {{{"scanf_s"}}, TR::Source({{}, {1}})}, + {{{"wgetch"}}, TR::Source({{}, ReturnValueIndex})}, // Sometimes the line between taint sources and propagators is blurry. // _IO_getc is choosen to be a source, but could also be a propagator. // This way it is simpler, as modeling it as a propagator would require // to model the possible sources of _IO_FILE * values, which the _IO_getc // function takes as parameters. - {{"_IO_getc"}, TR::Source({{ReturnValueIndex}})}, - {{"getcwd"}, TR::Source({{0, ReturnValueIndex}})}, - {{"getwd"}, TR::Source({{0, ReturnValueIndex}})}, - {{"readlink"}, TR::Source({{1, ReturnValueIndex}})}, - {{"readlinkat"}, TR::Source({{2, ReturnValueIndex}})}, - {{"get_current_dir_name"}, TR::Source({{ReturnValueIndex}})}, - {{"gethostname"}, TR::Source({{0}})}, - {{"getnameinfo"}, TR::Source({{2, 4}})}, - {{"getseuserbyname"}, TR::Source({{1, 2}})}, - {{"getgroups"}, TR::Source({{1, ReturnValueIndex}})}, - {{"getlogin"}, TR::Source({{ReturnValueIndex}})}, - {{"getlogin_r"}, TR::Source({{0}})}, + {{{"_IO_getc"}}, TR::Source({{ReturnValueIndex}})}, + {{{"getcwd"}}, TR::Source({{0, ReturnValueIndex}})}, + {{{"getwd"}}, TR::Source({{0, ReturnValueIndex}})}, + {{{"readlink"}}, TR::Source({{1, ReturnValueIndex}})}, + {{{"readlinkat"}}, TR::Source({{2, ReturnValueIndex}})}, + {{{"get_current_dir_name"}}, TR::Source({{ReturnValueIndex}})}, + {{{"gethostname"}}, TR::Source({{0}})}, + {{{"getnameinfo"}}, TR::Source({{2, 4}})}, + {{{"getseuserbyname"}}, TR::Source({{1, 2}})}, + {{{"getgroups"}}, TR::Source({{1, ReturnValueIndex}})}, + {{{"getlogin"}}, TR::Source({{ReturnValueIndex}})}, + {{{"getlogin_r"}}, TR::Source({{0}})}, // Props - {{"atoi"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"atol"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"atoll"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"fgetc"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"fgetln"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"fgets"}, TR::Prop({{2}}, {{0, ReturnValueIndex}})}, - {{"fscanf"}, TR::Prop({{0}}, {{}, 2})}, - {{"fscanf_s"}, TR::Prop({{0}}, {{}, {2}})}, - {{"sscanf"}, TR::Prop({{0}}, {{}, 2})}, - - {{"getc"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"getc_unlocked"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"getdelim"}, TR::Prop({{3}}, {{0}})}, - {{"getline"}, TR::Prop({{2}}, {{0}})}, - {{"getw"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"pread"}, TR::Prop({{0, 1, 2, 3}}, {{1, ReturnValueIndex}})}, - {{"read"}, TR::Prop({{0, 2}}, {{1, ReturnValueIndex}})}, - {{"strchr"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"strrchr"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"tolower"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"toupper"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"fread"}, TR::Prop({{3}}, {{0, ReturnValueIndex}})}, - {{"recv"}, TR::Prop({{0}}, {{1, ReturnValueIndex}})}, - {{"recvfrom"}, TR::Prop({{0}}, {{1, ReturnValueIndex}})}, - - {{"ttyname"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"ttyname_r"}, TR::Prop({{0}}, {{1, ReturnValueIndex}})}, - - {{"basename"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"dirname"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"fnmatch"}, TR::Prop({{1}}, {{ReturnValueIndex}})}, - {{"memchr"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"memrchr"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"rawmemchr"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - - {{"mbtowc"}, TR::Prop({{1}}, {{0, ReturnValueIndex}})}, - {{"wctomb"}, TR::Prop({{1}}, {{0, ReturnValueIndex}})}, - {{"wcwidth"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - - {{"memcmp"}, TR::Prop({{0, 1}}, {{ReturnValueIndex}})}, - {{"memcpy"}, TR::Prop({{1}}, {{0, ReturnValueIndex}})}, - {{"memmove"}, TR::Prop({{1}}, {{0, ReturnValueIndex}})}, + {{{"atoi"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"atol"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"atoll"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"fgetc"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"fgetln"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"fgets"}}, TR::Prop({{2}}, {{0, ReturnValueIndex}})}, + {{{"fscanf"}}, TR::Prop({{0}}, {{}, 2})}, + {{{"fscanf_s"}}, TR::Prop({{0}}, {{}, {2}})}, + {{{"sscanf"}}, TR::Prop({{0}}, {{}, 2})}, + + {{{"getc"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"getc_unlocked"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"getdelim"}}, TR::Prop({{3}}, {{0}})}, + {{{"getline"}}, TR::Prop({{2}}, {{0}})}, + {{{"getw"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"pread"}}, TR::Prop({{0, 1, 2, 3}}, {{1, ReturnValueIndex}})}, + {{{"read"}}, TR::Prop({{0, 2}}, {{1, ReturnValueIndex}})}, + {{{"strchr"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"strrchr"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"tolower"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"toupper"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"fread"}}, TR::Prop({{3}}, {{0, ReturnValueIndex}})}, + {{{"recv"}}, TR::Prop({{0}}, {{1, ReturnValueIndex}})}, + {{{"recvfrom"}}, TR::Prop({{0}}, {{1, ReturnValueIndex}})}, + + {{{"ttyname"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"ttyname_r"}}, TR::Prop({{0}}, {{1, ReturnValueIndex}})}, + + {{{"basename"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"dirname"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"fnmatch"}}, TR::Prop({{1}}, {{ReturnValueIndex}})}, + {{{"memchr"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"memrchr"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"rawmemchr"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + + {{{"mbtowc"}}, TR::Prop({{1}}, {{0, ReturnValueIndex}})}, + {{{"wctomb"}}, TR::Prop({{1}}, {{0, ReturnValueIndex}})}, + {{{"wcwidth"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + + {{{"memcmp"}}, TR::Prop({{0, 1}}, {{ReturnValueIndex}})}, + {{{"memcpy"}}, TR::Prop({{1}}, {{0, ReturnValueIndex}})}, + {{{"memmove"}}, TR::Prop({{1}}, {{0, ReturnValueIndex}})}, // If memmem was called with a tainted needle and the search was // successful, that would mean that the value pointed by the return value // has the same content as the needle. If we choose to go by the policy of // content equivalence implies taintedness equivalence, that would mean // haystack should be considered a propagation source argument. - {{"memmem"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"memmem"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, // The comment for memmem above also applies to strstr. - {{"strstr"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"strcasestr"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"strstr"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"strcasestr"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"strchrnul"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"strchrnul"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"index"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"rindex"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"index"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"rindex"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, // FIXME: In case of arrays, only the first element of the array gets // tainted. - {{"qsort"}, TR::Prop({{0}}, {{0}})}, - {{"qsort_r"}, TR::Prop({{0}}, {{0}})}, - - {{"strcmp"}, TR::Prop({{0, 1}}, {{ReturnValueIndex}})}, - {{"strcasecmp"}, TR::Prop({{0, 1}}, {{ReturnValueIndex}})}, - {{"strncmp"}, TR::Prop({{0, 1, 2}}, {{ReturnValueIndex}})}, - {{"strncasecmp"}, TR::Prop({{0, 1, 2}}, {{ReturnValueIndex}})}, - {{"strspn"}, TR::Prop({{0, 1}}, {{ReturnValueIndex}})}, - {{"strcspn"}, TR::Prop({{0, 1}}, {{ReturnValueIndex}})}, - {{"strpbrk"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"strndup"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"strndupa"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"strlen"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"strnlen"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"strtol"}, TR::Prop({{0}}, {{1, ReturnValueIndex}})}, - {{"strtoll"}, TR::Prop({{0}}, {{1, ReturnValueIndex}})}, - {{"strtoul"}, TR::Prop({{0}}, {{1, ReturnValueIndex}})}, - {{"strtoull"}, TR::Prop({{0}}, {{1, ReturnValueIndex}})}, - - {{"isalnum"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"isalpha"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"isascii"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"isblank"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"iscntrl"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"isdigit"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"isgraph"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"islower"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"isprint"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"ispunct"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"isspace"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"isupper"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"isxdigit"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"qsort"}}, TR::Prop({{0}}, {{0}})}, + {{{"qsort_r"}}, TR::Prop({{0}}, {{0}})}, + + {{{"strcmp"}}, TR::Prop({{0, 1}}, {{ReturnValueIndex}})}, + {{{"strcasecmp"}}, TR::Prop({{0, 1}}, {{ReturnValueIndex}})}, + {{{"strncmp"}}, TR::Prop({{0, 1, 2}}, {{ReturnValueIndex}})}, + {{{"strncasecmp"}}, TR::Prop({{0, 1, 2}}, {{ReturnValueIndex}})}, + {{{"strspn"}}, TR::Prop({{0, 1}}, {{ReturnValueIndex}})}, + {{{"strcspn"}}, TR::Prop({{0, 1}}, {{ReturnValueIndex}})}, + {{{"strpbrk"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"strndup"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"strndupa"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"strlen"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"strnlen"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"strtol"}}, TR::Prop({{0}}, {{1, ReturnValueIndex}})}, + {{{"strtoll"}}, TR::Prop({{0}}, {{1, ReturnValueIndex}})}, + {{{"strtoul"}}, TR::Prop({{0}}, {{1, ReturnValueIndex}})}, + {{{"strtoull"}}, TR::Prop({{0}}, {{1, ReturnValueIndex}})}, + + {{{"isalnum"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"isalpha"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"isascii"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"isblank"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"iscntrl"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"isdigit"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"isgraph"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"islower"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"isprint"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"ispunct"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"isspace"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"isupper"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"isxdigit"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, {{CDF_MaybeBuiltin, {BI.getName(Builtin::BIstrncat)}}, TR::Prop({{1, 2}}, {{0, ReturnValueIndex}})}, @@ -656,37 +656,40 @@ void GenericTaintChecker::initTaintRules(CheckerContext &C) const { TR::Prop({{1, 2}}, {{0}})}, {{CDF_MaybeBuiltin, {BI.getName(Builtin::BIstrlcat)}}, TR::Prop({{1, 2}}, {{0}})}, - {{CDF_MaybeBuiltin, {"snprintf"}}, + {{CDF_MaybeBuiltin, {{"snprintf"}}}, TR::Prop({{1}, 3}, {{0, ReturnValueIndex}})}, - {{CDF_MaybeBuiltin, {"sprintf"}}, + {{CDF_MaybeBuiltin, {{"sprintf"}}}, TR::Prop({{1}, 2}, {{0, ReturnValueIndex}})}, - {{CDF_MaybeBuiltin, {"strcpy"}}, + {{CDF_MaybeBuiltin, {{"strcpy"}}}, TR::Prop({{1}}, {{0, ReturnValueIndex}})}, - {{CDF_MaybeBuiltin, {"stpcpy"}}, + {{CDF_MaybeBuiltin, {{"stpcpy"}}}, TR::Prop({{1}}, {{0, ReturnValueIndex}})}, - {{CDF_MaybeBuiltin, {"strcat"}}, + {{CDF_MaybeBuiltin, {{"strcat"}}}, TR::Prop({{1}}, {{0, ReturnValueIndex}})}, - {{CDF_MaybeBuiltin, {"strdup"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{CDF_MaybeBuiltin, {"strdupa"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{CDF_MaybeBuiltin, {"wcsdup"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{CDF_MaybeBuiltin, {{"strdup"}}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{CDF_MaybeBuiltin, {{"strdupa"}}}, + TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{CDF_MaybeBuiltin, {{"wcsdup"}}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, // Sinks - {{"system"}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, - {{"popen"}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, - {{"execl"}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, - {{"execle"}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, - {{"execlp"}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, - {{"execvp"}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, - {{"execvP"}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, - {{"execve"}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, - {{"dlopen"}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, - {{CDF_MaybeBuiltin, {"malloc"}}, TR::Sink({{0}}, MsgTaintedBufferSize)}, - {{CDF_MaybeBuiltin, {"calloc"}}, TR::Sink({{0}}, MsgTaintedBufferSize)}, - {{CDF_MaybeBuiltin, {"alloca"}}, TR::Sink({{0}}, MsgTaintedBufferSize)}, - {{CDF_MaybeBuiltin, {"memccpy"}}, TR::Sink({{3}}, MsgTaintedBufferSize)}, - {{CDF_MaybeBuiltin, {"realloc"}}, TR::Sink({{1}}, MsgTaintedBufferSize)}, - {{{"setproctitle"}}, TR::Sink({{0}, 1}, MsgUncontrolledFormatString)}, - {{{"setproctitle_fast"}}, + {{{"system"}}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, + {{{"popen"}}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, + {{{"execl"}}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, + {{{"execle"}}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, + {{{"execlp"}}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, + {{{"execvp"}}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, + {{{"execvP"}}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, + {{{"execve"}}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, + {{{"dlopen"}}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, + {{CDF_MaybeBuiltin, {{"malloc"}}}, TR::Sink({{0}}, MsgTaintedBufferSize)}, + {{CDF_MaybeBuiltin, {{"calloc"}}}, TR::Sink({{0}}, MsgTaintedBufferSize)}, + {{CDF_MaybeBuiltin, {{"alloca"}}}, TR::Sink({{0}}, MsgTaintedBufferSize)}, + {{CDF_MaybeBuiltin, {{"memccpy"}}}, + TR::Sink({{3}}, MsgTaintedBufferSize)}, + {{CDF_MaybeBuiltin, {{"realloc"}}}, + TR::Sink({{1}}, MsgTaintedBufferSize)}, + {{{{"setproctitle"}}}, TR::Sink({{0}, 1}, MsgUncontrolledFormatString)}, + {{{{"setproctitle_fast"}}}, TR::Sink({{0}, 1}, MsgUncontrolledFormatString)}, // SinkProps @@ -702,7 +705,7 @@ void GenericTaintChecker::initTaintRules(CheckerContext &C) const { {{CDF_MaybeBuiltin, {BI.getName(Builtin::BIstrndup)}}, TR::SinkProp({{1}}, {{0, 1}}, {{ReturnValueIndex}}, MsgTaintedBufferSize)}, - {{CDF_MaybeBuiltin, {"bcopy"}}, + {{CDF_MaybeBuiltin, {{"bcopy"}}}, TR::SinkProp({{2}}, {{0, 2}}, {{1}}, MsgTaintedBufferSize)}}; // `getenv` returns taint only in untrusted environments. @@ -710,7 +713,7 @@ void GenericTaintChecker::initTaintRules(CheckerContext &C) const { // void setproctitle_init(int argc, char *argv[], char *envp[]) GlobalCRules.push_back( {{{"setproctitle_init"}}, TR::Sink({{1, 2}}, MsgCustomSink)}); - GlobalCRules.push_back({{"getenv"}, TR::Source({{ReturnValueIndex}})}); + GlobalCRules.push_back({{{"getenv"}}, TR::Source({{ReturnValueIndex}})}); } StaticTaintRules.emplace(std::make_move_iterator(GlobalCRules.begin()), @@ -723,7 +726,7 @@ void GenericTaintChecker::initTaintRules(CheckerContext &C) const { std::string Option{"Config"}; StringRef ConfigFile = Mgr->getAnalyzerOptions().getCheckerStringOption(this, Option); - llvm::Optional<TaintConfiguration> Config = + std::optional<TaintConfiguration> Config = getConfiguration<TaintConfiguration>(*Mgr, this, Option, ConfigFile); if (!Config) { // We don't have external taint config, no parsing required. @@ -899,7 +902,7 @@ bool GenericTaintRule::UntrustedEnv(CheckerContext &C) { bool GenericTaintChecker::generateReportIfTainted(const Expr *E, StringRef Msg, CheckerContext &C) const { assert(E); - Optional<SVal> TaintedSVal{getTaintedPointeeOrPointer(C, C.getSVal(E))}; + std::optional<SVal> TaintedSVal{getTaintedPointeeOrPointer(C, C.getSVal(E))}; if (!TaintedSVal) return false; diff --git a/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp index ce334b18386e..b3f2d7f4d268 100644 --- a/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp @@ -379,8 +379,7 @@ visit(const ObjCImplementationDecl *ImplD) const { IvarToPropMapTy IvarToPopertyMap; ObjCInterfaceDecl::PropertyMap PropMap; - ObjCInterfaceDecl::PropertyDeclOrder PropOrder; - InterfaceD->collectPropertiesToImplement(PropMap, PropOrder); + InterfaceD->collectPropertiesToImplement(PropMap); for (ObjCInterfaceDecl::PropertyMap::iterator I = PropMap.begin(), E = PropMap.end(); I != E; ++I) { diff --git a/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp index 9870746cbe0c..bca10ec96cea 100644 --- a/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp @@ -29,6 +29,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "llvm/Support/Unicode.h" +#include <optional> using namespace clang; using namespace ento; @@ -1004,7 +1005,7 @@ NonLocalizedStringBRVisitor::VisitNode(const ExplodedNode *Succ, if (Satisfied) return nullptr; - Optional<StmtPoint> Point = Succ->getLocation().getAs<StmtPoint>(); + std::optional<StmtPoint> Point = Succ->getLocation().getAs<StmtPoint>(); if (!Point) return nullptr; @@ -1141,7 +1142,7 @@ void EmptyLocalizationContextChecker::MethodCrawler::VisitObjCMessageExpr( SE = Mgr.getSourceManager().getSLocEntry(SLInfo.first); } - llvm::Optional<llvm::MemoryBufferRef> BF = + std::optional<llvm::MemoryBufferRef> BF = Mgr.getSourceManager().getBufferOrNone(SLInfo.first, SL); if (!BF) return; diff --git a/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp index 139bc0e99d78..153a0a51e980 100644 --- a/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp @@ -30,6 +30,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include <optional> using namespace clang; using namespace ento; @@ -86,7 +87,7 @@ class MIGChecker : public Checker<check::PostCall, check::PreStmt<ReturnStmt>, #undef CALL }; - CallDescription OsRefRetain{"os_ref_retain", 1}; + CallDescription OsRefRetain{{"os_ref_retain"}, 1}; void checkReturnAux(const ReturnStmt *RS, CheckerContext &C) const; @@ -157,7 +158,7 @@ static bool isInMIGCall(CheckerContext &C) { const Decl *D = SFC->getDecl(); - if (Optional<AnyCall> AC = AnyCall::forDecl(D)) { + if (std::optional<AnyCall> AC = AnyCall::forDecl(D)) { // Even though there's a Sema warning when the return type of an annotated // function is not a kern_return_t, this warning isn't an error, so we need // an extra check here. diff --git a/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp index 635eb00e4ca9..c1b85ace3e2d 100644 --- a/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp @@ -21,6 +21,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" +#include <optional> using namespace clang; using namespace ento; @@ -209,7 +210,7 @@ static SymbolRef getAsPointeeSymbol(const Expr *Expr, ProgramStateRef State = C.getState(); SVal ArgV = C.getSVal(Expr); - if (Optional<loc::MemRegionVal> X = ArgV.getAs<loc::MemRegionVal>()) { + if (std::optional<loc::MemRegionVal> X = ArgV.getAs<loc::MemRegionVal>()) { StoreManager& SM = C.getStoreManager(); SymbolRef sym = SM.getBinding(State->getStore(), *X).getAsLocSymbol(); if (sym) diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index adedc9c30fad..f05cd9227b65 100644 --- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -86,6 +86,7 @@ #include "llvm/Support/raw_ostream.h" #include <climits> #include <functional> +#include <optional> #include <utility> using namespace clang; @@ -220,10 +221,10 @@ static bool isReleased(SymbolRef Sym, CheckerContext &C); /// Update the RefState to reflect the new memory allocation. /// The optional \p RetVal parameter specifies the newly allocated pointer /// value; if unspecified, the value of expression \p E is used. -static ProgramStateRef MallocUpdateRefState(CheckerContext &C, const Expr *E, - ProgramStateRef State, - AllocationFamily Family, - Optional<SVal> RetVal = None); +static ProgramStateRef +MallocUpdateRefState(CheckerContext &C, const Expr *E, ProgramStateRef State, + AllocationFamily Family, + std::optional<SVal> RetVal = std::nullopt); //===----------------------------------------------------------------------===// // The modeling of memory reallocation. @@ -391,10 +392,10 @@ private: const CallEvent &Call, CheckerContext &C)>; const CallDescriptionMap<CheckFn> FreeingMemFnMap{ - {{"free", 1}, &MallocChecker::checkFree}, - {{"if_freenameindex", 1}, &MallocChecker::checkIfFreeNameIndex}, - {{"kfree", 1}, &MallocChecker::checkFree}, - {{"g_free", 1}, &MallocChecker::checkFree}, + {{{"free"}, 1}, &MallocChecker::checkFree}, + {{{"if_freenameindex"}, 1}, &MallocChecker::checkIfFreeNameIndex}, + {{{"kfree"}, 1}, &MallocChecker::checkFree}, + {{{"g_free"}, 1}, &MallocChecker::checkFree}, }; bool isFreeingCall(const CallEvent &Call) const; @@ -403,61 +404,60 @@ private: friend class NoOwnershipChangeVisitor; CallDescriptionMap<CheckFn> AllocatingMemFnMap{ - {{"alloca", 1}, &MallocChecker::checkAlloca}, - {{"_alloca", 1}, &MallocChecker::checkAlloca}, - {{"malloc", 1}, &MallocChecker::checkBasicAlloc}, - {{"malloc", 3}, &MallocChecker::checkKernelMalloc}, - {{"calloc", 2}, &MallocChecker::checkCalloc}, - {{"valloc", 1}, &MallocChecker::checkBasicAlloc}, - {{CDF_MaybeBuiltin, "strndup", 2}, &MallocChecker::checkStrdup}, - {{CDF_MaybeBuiltin, "strdup", 1}, &MallocChecker::checkStrdup}, - {{"_strdup", 1}, &MallocChecker::checkStrdup}, - {{"kmalloc", 2}, &MallocChecker::checkKernelMalloc}, - {{"if_nameindex", 1}, &MallocChecker::checkIfNameIndex}, - {{CDF_MaybeBuiltin, "wcsdup", 1}, &MallocChecker::checkStrdup}, - {{CDF_MaybeBuiltin, "_wcsdup", 1}, &MallocChecker::checkStrdup}, - {{"g_malloc", 1}, &MallocChecker::checkBasicAlloc}, - {{"g_malloc0", 1}, &MallocChecker::checkGMalloc0}, - {{"g_try_malloc", 1}, &MallocChecker::checkBasicAlloc}, - {{"g_try_malloc0", 1}, &MallocChecker::checkGMalloc0}, - {{"g_memdup", 2}, &MallocChecker::checkGMemdup}, - {{"g_malloc_n", 2}, &MallocChecker::checkGMallocN}, - {{"g_malloc0_n", 2}, &MallocChecker::checkGMallocN0}, - {{"g_try_malloc_n", 2}, &MallocChecker::checkGMallocN}, - {{"g_try_malloc0_n", 2}, &MallocChecker::checkGMallocN0}, + {{{"alloca"}, 1}, &MallocChecker::checkAlloca}, + {{{"_alloca"}, 1}, &MallocChecker::checkAlloca}, + {{{"malloc"}, 1}, &MallocChecker::checkBasicAlloc}, + {{{"malloc"}, 3}, &MallocChecker::checkKernelMalloc}, + {{{"calloc"}, 2}, &MallocChecker::checkCalloc}, + {{{"valloc"}, 1}, &MallocChecker::checkBasicAlloc}, + {{CDF_MaybeBuiltin, {"strndup"}, 2}, &MallocChecker::checkStrdup}, + {{CDF_MaybeBuiltin, {"strdup"}, 1}, &MallocChecker::checkStrdup}, + {{{"_strdup"}, 1}, &MallocChecker::checkStrdup}, + {{{"kmalloc"}, 2}, &MallocChecker::checkKernelMalloc}, + {{{"if_nameindex"}, 1}, &MallocChecker::checkIfNameIndex}, + {{CDF_MaybeBuiltin, {"wcsdup"}, 1}, &MallocChecker::checkStrdup}, + {{CDF_MaybeBuiltin, {"_wcsdup"}, 1}, &MallocChecker::checkStrdup}, + {{{"g_malloc"}, 1}, &MallocChecker::checkBasicAlloc}, + {{{"g_malloc0"}, 1}, &MallocChecker::checkGMalloc0}, + {{{"g_try_malloc"}, 1}, &MallocChecker::checkBasicAlloc}, + {{{"g_try_malloc0"}, 1}, &MallocChecker::checkGMalloc0}, + {{{"g_memdup"}, 2}, &MallocChecker::checkGMemdup}, + {{{"g_malloc_n"}, 2}, &MallocChecker::checkGMallocN}, + {{{"g_malloc0_n"}, 2}, &MallocChecker::checkGMallocN0}, + {{{"g_try_malloc_n"}, 2}, &MallocChecker::checkGMallocN}, + {{{"g_try_malloc0_n"}, 2}, &MallocChecker::checkGMallocN0}, }; CallDescriptionMap<CheckFn> ReallocatingMemFnMap{ - {{"realloc", 2}, + {{{"realloc"}, 2}, std::bind(&MallocChecker::checkRealloc, _1, _2, _3, false)}, - {{"reallocf", 2}, + {{{"reallocf"}, 2}, std::bind(&MallocChecker::checkRealloc, _1, _2, _3, true)}, - {{"g_realloc", 2}, + {{{"g_realloc"}, 2}, std::bind(&MallocChecker::checkRealloc, _1, _2, _3, false)}, - {{"g_try_realloc", 2}, + {{{"g_try_realloc"}, 2}, std::bind(&MallocChecker::checkRealloc, _1, _2, _3, false)}, - {{"g_realloc_n", 3}, &MallocChecker::checkReallocN}, - {{"g_try_realloc_n", 3}, &MallocChecker::checkReallocN}, + {{{"g_realloc_n"}, 3}, &MallocChecker::checkReallocN}, + {{{"g_try_realloc_n"}, 3}, &MallocChecker::checkReallocN}, }; bool isMemCall(const CallEvent &Call) const; // TODO: Remove mutable by moving the initializtaion to the registry function. - mutable Optional<uint64_t> KernelZeroFlagVal; + mutable std::optional<uint64_t> KernelZeroFlagVal; - using KernelZeroSizePtrValueTy = Optional<int>; + using KernelZeroSizePtrValueTy = std::optional<int>; /// Store the value of macro called `ZERO_SIZE_PTR`. /// The value is initialized at first use, before first use the outer /// Optional is empty, afterwards it contains another Optional that indicates /// if the macro value could be determined, and if yes the value itself. - mutable Optional<KernelZeroSizePtrValueTy> KernelZeroSizePtrValue; + mutable std::optional<KernelZeroSizePtrValueTy> KernelZeroSizePtrValue; /// Process C++ operator new()'s allocation, which is the part of C++ /// new-expression that goes before the constructor. - LLVM_NODISCARD - ProgramStateRef processNewAllocation(const CXXAllocatorCall &Call, - CheckerContext &C, - AllocationFamily Family) const; + [[nodiscard]] ProgramStateRef + processNewAllocation(const CXXAllocatorCall &Call, CheckerContext &C, + AllocationFamily Family) const; /// Perform a zero-allocation check. /// @@ -467,11 +467,10 @@ private: /// 0. /// \param [in] RetVal Specifies the newly allocated pointer value; /// if unspecified, the value of expression \p E is used. - LLVM_NODISCARD - static ProgramStateRef ProcessZeroAllocCheck(const CallEvent &Call, - const unsigned IndexOfSizeArg, - ProgramStateRef State, - Optional<SVal> RetVal = None); + [[nodiscard]] static ProgramStateRef + ProcessZeroAllocCheck(const CallEvent &Call, const unsigned IndexOfSizeArg, + ProgramStateRef State, + std::optional<SVal> RetVal = std::nullopt); /// Model functions with the ownership_returns attribute. /// @@ -489,10 +488,9 @@ private: /// \param [in] Att The ownership_returns attribute. /// \param [in] State The \c ProgramState right before allocation. /// \returns The ProgramState right after allocation. - LLVM_NODISCARD - ProgramStateRef MallocMemReturnsAttr(CheckerContext &C, const CallEvent &Call, - const OwnershipAttr *Att, - ProgramStateRef State) const; + [[nodiscard]] ProgramStateRef + MallocMemReturnsAttr(CheckerContext &C, const CallEvent &Call, + const OwnershipAttr *Att, ProgramStateRef State) const; /// Models memory allocation. /// @@ -503,11 +501,9 @@ private: /// malloc leaves it undefined. /// \param [in] State The \c ProgramState right before allocation. /// \returns The ProgramState right after allocation. - LLVM_NODISCARD - static ProgramStateRef MallocMemAux(CheckerContext &C, const CallEvent &Call, - const Expr *SizeEx, SVal Init, - ProgramStateRef State, - AllocationFamily Family); + [[nodiscard]] static ProgramStateRef + MallocMemAux(CheckerContext &C, const CallEvent &Call, const Expr *SizeEx, + SVal Init, ProgramStateRef State, AllocationFamily Family); /// Models memory allocation. /// @@ -518,16 +514,13 @@ private: /// malloc leaves it undefined. /// \param [in] State The \c ProgramState right before allocation. /// \returns The ProgramState right after allocation. - LLVM_NODISCARD - static ProgramStateRef MallocMemAux(CheckerContext &C, const CallEvent &Call, - SVal Size, SVal Init, - ProgramStateRef State, - AllocationFamily Family); + [[nodiscard]] static ProgramStateRef + MallocMemAux(CheckerContext &C, const CallEvent &Call, SVal Size, SVal Init, + ProgramStateRef State, AllocationFamily Family); // Check if this malloc() for special flags. At present that means M_ZERO or // __GFP_ZERO (in which case, treat it like calloc). - LLVM_NODISCARD - llvm::Optional<ProgramStateRef> + [[nodiscard]] std::optional<ProgramStateRef> performKernelMalloc(const CallEvent &Call, CheckerContext &C, const ProgramStateRef &State) const; @@ -548,10 +541,10 @@ private: /// \param [in] Att The ownership_takes or ownership_holds attribute. /// \param [in] State The \c ProgramState right before allocation. /// \returns The ProgramState right after deallocation. - LLVM_NODISCARD - ProgramStateRef FreeMemAttr(CheckerContext &C, const CallEvent &Call, - const OwnershipAttr *Att, - ProgramStateRef State) const; + [[nodiscard]] ProgramStateRef FreeMemAttr(CheckerContext &C, + const CallEvent &Call, + const OwnershipAttr *Att, + ProgramStateRef State) const; /// Models memory deallocation. /// @@ -572,12 +565,10 @@ private: /// \param [in] ReturnsNullOnFailure Whether the memory deallocation function /// we're modeling returns with Null on failure. /// \returns The ProgramState right after deallocation. - LLVM_NODISCARD - ProgramStateRef FreeMemAux(CheckerContext &C, const CallEvent &Call, - ProgramStateRef State, unsigned Num, bool Hold, - bool &IsKnownToBeAllocated, - AllocationFamily Family, - bool ReturnsNullOnFailure = false) const; + [[nodiscard]] ProgramStateRef + FreeMemAux(CheckerContext &C, const CallEvent &Call, ProgramStateRef State, + unsigned Num, bool Hold, bool &IsKnownToBeAllocated, + AllocationFamily Family, bool ReturnsNullOnFailure = false) const; /// Models memory deallocation. /// @@ -598,12 +589,10 @@ private: /// \param [in] ReturnsNullOnFailure Whether the memory deallocation function /// we're modeling returns with Null on failure. /// \returns The ProgramState right after deallocation. - LLVM_NODISCARD - ProgramStateRef FreeMemAux(CheckerContext &C, const Expr *ArgExpr, - const CallEvent &Call, ProgramStateRef State, - bool Hold, bool &IsKnownToBeAllocated, - AllocationFamily Family, - bool ReturnsNullOnFailure = false) const; + [[nodiscard]] ProgramStateRef + FreeMemAux(CheckerContext &C, const Expr *ArgExpr, const CallEvent &Call, + ProgramStateRef State, bool Hold, bool &IsKnownToBeAllocated, + AllocationFamily Family, bool ReturnsNullOnFailure = false) const; // TODO: Needs some refactoring, as all other deallocation modeling // functions are suffering from out parameters and messy code due to how @@ -618,29 +607,27 @@ private: /// \param [in] SuffixWithN Whether the reallocation function we're modeling /// has an '_n' suffix, such as g_realloc_n. /// \returns The ProgramState right after reallocation. - LLVM_NODISCARD - ProgramStateRef ReallocMemAux(CheckerContext &C, const CallEvent &Call, - bool ShouldFreeOnFail, ProgramStateRef State, - AllocationFamily Family, - bool SuffixWithN = false) const; + [[nodiscard]] ProgramStateRef + ReallocMemAux(CheckerContext &C, const CallEvent &Call, bool ShouldFreeOnFail, + ProgramStateRef State, AllocationFamily Family, + bool SuffixWithN = false) const; /// Evaluates the buffer size that needs to be allocated. /// /// \param [in] Blocks The amount of blocks that needs to be allocated. /// \param [in] BlockBytes The size of a block. /// \returns The symbolic value of \p Blocks * \p BlockBytes. - LLVM_NODISCARD - static SVal evalMulForBufferSize(CheckerContext &C, const Expr *Blocks, - const Expr *BlockBytes); + [[nodiscard]] static SVal evalMulForBufferSize(CheckerContext &C, + const Expr *Blocks, + const Expr *BlockBytes); /// Models zero initialized array allocation. /// /// \param [in] Call The expression that reallocated memory /// \param [in] State The \c ProgramState right before reallocation. /// \returns The ProgramState right after allocation. - LLVM_NODISCARD - static ProgramStateRef CallocMem(CheckerContext &C, const CallEvent &Call, - ProgramStateRef State); + [[nodiscard]] static ProgramStateRef + CallocMem(CheckerContext &C, const CallEvent &Call, ProgramStateRef State); /// See if deallocation happens in a suspicious context. If so, escape the /// pointers that otherwise would have been deallocated and return true. @@ -673,12 +660,11 @@ private: SymbolRef &EscapingSymbol) const; /// Implementation of the checkPointerEscape callbacks. - LLVM_NODISCARD - ProgramStateRef checkPointerEscapeAux(ProgramStateRef State, - const InvalidatedSymbols &Escaped, - const CallEvent *Call, - PointerEscapeKind Kind, - bool IsConstPointerEscape) const; + [[nodiscard]] ProgramStateRef + checkPointerEscapeAux(ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call, PointerEscapeKind Kind, + bool IsConstPointerEscape) const; // Implementation of the checkPreStmt and checkEndFunction callbacks. void checkEscapeOnReturn(const ReturnStmt *S, CheckerContext &C) const; @@ -687,11 +673,11 @@ private: /// Tells if a given family/call/symbol is tracked by the current checker. /// Sets CheckKind to the kind of the checker responsible for this /// family/call/symbol. - Optional<CheckKind> getCheckIfTracked(AllocationFamily Family, - bool IsALeakCheck = false) const; + std::optional<CheckKind> getCheckIfTracked(AllocationFamily Family, + bool IsALeakCheck = false) const; - Optional<CheckKind> getCheckIfTracked(CheckerContext &C, SymbolRef Sym, - bool IsALeakCheck = false) const; + std::optional<CheckKind> getCheckIfTracked(CheckerContext &C, SymbolRef Sym, + bool IsALeakCheck = false) const; ///@} static bool SummarizeValue(raw_ostream &os, SVal V); static bool SummarizeRegion(raw_ostream &os, const MemRegion *MR); @@ -870,7 +856,7 @@ protected: // If a variable is dead (is not referenced directly or indirectly after // some point), it will be removed from the Store before the end of its // actual lifetime. - // This means that that if the ownership status didn't change, CurrOwners + // This means that if the ownership status didn't change, CurrOwners // must be a superset of, but not necessarily equal to ExitOwners. return !llvm::set_is_subset(ExitOwners, CurrOwners); } @@ -1132,7 +1118,7 @@ bool MallocChecker::isMemCall(const CallEvent &Call) const { return Func && Func->hasAttr<OwnershipAttr>(); } -llvm::Optional<ProgramStateRef> +std::optional<ProgramStateRef> MallocChecker::performKernelMalloc(const CallEvent &Call, CheckerContext &C, const ProgramStateRef &State) const { // 3-argument malloc(), as commonly used in {Free,Net,Open}BSD Kernels: @@ -1170,33 +1156,32 @@ MallocChecker::performKernelMalloc(const CallEvent &Call, CheckerContext &C, // Fall back to normal malloc behavior on platforms where we don't // know M_ZERO. - return None; + return std::nullopt; } // We treat the last argument as the flags argument, and callers fall-back to // normal malloc on a None return. This works for the FreeBSD kernel malloc // as well as Linux kmalloc. if (Call.getNumArgs() < 2) - return None; + return std::nullopt; const Expr *FlagsEx = Call.getArgExpr(Call.getNumArgs() - 1); const SVal V = C.getSVal(FlagsEx); if (!isa<NonLoc>(V)) { // The case where 'V' can be a location can only be due to a bad header, // so in this case bail out. - return None; + return std::nullopt; } NonLoc Flags = V.castAs<NonLoc>(); - NonLoc ZeroFlag = - C.getSValBuilder() - .makeIntVal(KernelZeroFlagVal.value(), FlagsEx->getType()) - .castAs<NonLoc>(); + NonLoc ZeroFlag = C.getSValBuilder() + .makeIntVal(*KernelZeroFlagVal, FlagsEx->getType()) + .castAs<NonLoc>(); SVal MaskedFlagsUC = C.getSValBuilder().evalBinOpNN(State, BO_And, Flags, ZeroFlag, FlagsEx->getType()); if (MaskedFlagsUC.isUnknownOrUndef()) - return None; + return std::nullopt; DefinedSVal MaskedFlags = MaskedFlagsUC.castAs<DefinedSVal>(); // Check if maskedFlags is non-zero. @@ -1210,7 +1195,7 @@ MallocChecker::performKernelMalloc(const CallEvent &Call, CheckerContext &C, AF_Malloc); } - return None; + return std::nullopt; } SVal MallocChecker::evalMulForBufferSize(CheckerContext &C, const Expr *Blocks, @@ -1236,10 +1221,10 @@ void MallocChecker::checkBasicAlloc(const CallEvent &Call, void MallocChecker::checkKernelMalloc(const CallEvent &Call, CheckerContext &C) const { ProgramStateRef State = C.getState(); - llvm::Optional<ProgramStateRef> MaybeState = + std::optional<ProgramStateRef> MaybeState = performKernelMalloc(Call, C, State); if (MaybeState) - State = MaybeState.value(); + State = *MaybeState; else State = MallocMemAux(C, Call, Call.getArgExpr(0), UndefinedVal(), State, AF_Malloc); @@ -1506,7 +1491,7 @@ void MallocChecker::checkPostCall(const CallEvent &Call, // Performs a 0-sized allocations check. ProgramStateRef MallocChecker::ProcessZeroAllocCheck( const CallEvent &Call, const unsigned IndexOfSizeArg, ProgramStateRef State, - Optional<SVal> RetVal) { + std::optional<SVal> RetVal) { if (!State) return nullptr; @@ -1658,7 +1643,7 @@ static bool isKnownDeallocObjCMethodName(const ObjCMethodCall &Call) { FirstSlot == "initWithCharactersNoCopy"; } -static Optional<bool> getFreeWhenDoneArg(const ObjCMethodCall &Call) { +static std::optional<bool> getFreeWhenDoneArg(const ObjCMethodCall &Call) { Selector S = Call.getSelector(); // FIXME: We should not rely on fully-constrained symbols being folded. @@ -1666,7 +1651,7 @@ static Optional<bool> getFreeWhenDoneArg(const ObjCMethodCall &Call) { if (S.getNameForSlot(i).equals("freeWhenDone")) return !Call.getArgSVal(i).isZeroConstant(); - return None; + return std::nullopt; } void MallocChecker::checkPostObjCMessage(const ObjCMethodCall &Call, @@ -1677,7 +1662,7 @@ void MallocChecker::checkPostObjCMessage(const ObjCMethodCall &Call, if (!isKnownDeallocObjCMethodName(Call)) return; - if (Optional<bool> FreeWhenDone = getFreeWhenDoneArg(Call)) + if (std::optional<bool> FreeWhenDone = getFreeWhenDoneArg(Call)) if (!*FreeWhenDone) return; @@ -1749,6 +1734,10 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C, // Fill the region with the initialization value. State = State->bindDefaultInitial(RetVal, Init, LCtx); + // If Size is somehow undefined at this point, this line prevents a crash. + if (Size.isUndef()) + Size = UnknownVal(); + // Set the region's extent. State = setDynamicExtent(State, RetVal.getAsRegion(), Size.castAs<DefinedOrUnknownSVal>(), svalBuilder); @@ -1759,7 +1748,7 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C, static ProgramStateRef MallocUpdateRefState(CheckerContext &C, const Expr *E, ProgramStateRef State, AllocationFamily Family, - Optional<SVal> RetVal) { + std::optional<SVal> RetVal) { if (!State) return nullptr; @@ -2074,7 +2063,7 @@ ProgramStateRef MallocChecker::FreeMemAux( RefState::getReleased(Family, ParentExpr)); } -Optional<MallocChecker::CheckKind> +std::optional<MallocChecker::CheckKind> MallocChecker::getCheckIfTracked(AllocationFamily Family, bool IsALeakCheck) const { switch (Family) { @@ -2083,7 +2072,7 @@ MallocChecker::getCheckIfTracked(AllocationFamily Family, case AF_IfNameIndex: { if (ChecksEnabled[CK_MallocChecker]) return CK_MallocChecker; - return None; + return std::nullopt; } case AF_CXXNew: case AF_CXXNewArray: { @@ -2095,12 +2084,12 @@ MallocChecker::getCheckIfTracked(AllocationFamily Family, if (ChecksEnabled[CK_NewDeleteChecker]) return CK_NewDeleteChecker; } - return None; + return std::nullopt; } case AF_InnerBuffer: { if (ChecksEnabled[CK_InnerPointerChecker]) return CK_InnerPointerChecker; - return None; + return std::nullopt; } case AF_None: { llvm_unreachable("no family"); @@ -2109,7 +2098,7 @@ MallocChecker::getCheckIfTracked(AllocationFamily Family, llvm_unreachable("unhandled family"); } -Optional<MallocChecker::CheckKind> +std::optional<MallocChecker::CheckKind> MallocChecker::getCheckIfTracked(CheckerContext &C, SymbolRef Sym, bool IsALeakCheck) const { if (C.getState()->contains<ReallocSizeZeroSymbols>(Sym)) @@ -2121,11 +2110,13 @@ MallocChecker::getCheckIfTracked(CheckerContext &C, SymbolRef Sym, } bool MallocChecker::SummarizeValue(raw_ostream &os, SVal V) { - if (Optional<nonloc::ConcreteInt> IntVal = V.getAs<nonloc::ConcreteInt>()) + if (std::optional<nonloc::ConcreteInt> IntVal = + V.getAs<nonloc::ConcreteInt>()) os << "an integer (" << IntVal->getValue() << ")"; - else if (Optional<loc::ConcreteInt> ConstAddr = V.getAs<loc::ConcreteInt>()) + else if (std::optional<loc::ConcreteInt> ConstAddr = + V.getAs<loc::ConcreteInt>()) os << "a constant address (" << ConstAddr->getValue() << ")"; - else if (Optional<loc::GotoLabel> Label = V.getAs<loc::GotoLabel>()) + else if (std::optional<loc::GotoLabel> Label = V.getAs<loc::GotoLabel>()) os << "the address of the label '" << Label->getLabel()->getName() << "'"; else return false; @@ -2217,7 +2208,7 @@ void MallocChecker::HandleNonHeapDealloc(CheckerContext &C, SVal ArgVal, return; } - Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family); + std::optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family); if (!CheckKind) return; @@ -2258,7 +2249,7 @@ void MallocChecker::HandleNonHeapDealloc(CheckerContext &C, SVal ArgVal, void MallocChecker::HandleFreeAlloca(CheckerContext &C, SVal ArgVal, SourceRange Range) const { - Optional<MallocChecker::CheckKind> CheckKind; + std::optional<MallocChecker::CheckKind> CheckKind; if (ChecksEnabled[CK_MallocChecker]) CheckKind = CK_MallocChecker; @@ -2350,7 +2341,7 @@ void MallocChecker::HandleOffsetFree(CheckerContext &C, SVal ArgVal, return; } - Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family); + std::optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family); if (!CheckKind) return; @@ -2407,7 +2398,7 @@ void MallocChecker::HandleUseAfterFree(CheckerContext &C, SourceRange Range, return; } - Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym); + std::optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym); if (!CheckKind) return; @@ -2446,7 +2437,7 @@ void MallocChecker::HandleDoubleFree(CheckerContext &C, SourceRange Range, return; } - Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym); + std::optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym); if (!CheckKind) return; @@ -2476,7 +2467,7 @@ void MallocChecker::HandleDoubleDelete(CheckerContext &C, SymbolRef Sym) const { return; } - Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym); + std::optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym); if (!CheckKind) return; @@ -2503,7 +2494,7 @@ void MallocChecker::HandleUseZeroAlloc(CheckerContext &C, SourceRange Range, return; } - Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym); + std::optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym); if (!CheckKind) return; @@ -2536,7 +2527,7 @@ void MallocChecker::HandleFunctionPtrFree(CheckerContext &C, SVal ArgVal, return; } - Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family); + std::optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family); if (!CheckKind) return; @@ -2747,8 +2738,8 @@ void MallocChecker::HandleLeak(SymbolRef Sym, ExplodedNode *N, if (Family == AF_Alloca) return; - Optional<MallocChecker::CheckKind> - CheckKind = getCheckIfTracked(Family, true); + std::optional<MallocChecker::CheckKind> CheckKind = + getCheckIfTracked(Family, true); if (!CheckKind) return; @@ -3151,7 +3142,7 @@ bool MallocChecker::mayFreeAnyEscapedMemoryOrIsModeledExplicitly( // about, we can't be sure that the object will use free() to deallocate the // memory, so we can't model it explicitly. The best we can do is use it to // decide whether the pointer escapes. - if (Optional<bool> FreeWhenDone = getFreeWhenDoneArg(*Msg)) + if (std::optional<bool> FreeWhenDone = getFreeWhenDoneArg(*Msg)) return *FreeWhenDone; // If the first selector piece ends with "NoCopy", and there is no @@ -3570,7 +3561,8 @@ void MallocChecker::printState(raw_ostream &Out, ProgramStateRef State, for (RegionStateTy::iterator I = RS.begin(), E = RS.end(); I != E; ++I) { const RefState *RefS = State->get<RegionState>(I.getKey()); AllocationFamily Family = RefS->getAllocationFamily(); - Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family); + std::optional<MallocChecker::CheckKind> CheckKind = + getCheckIfTracked(Family); if (!CheckKind) CheckKind = getCheckIfTracked(Family, true); diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp index a6e8fcd425d5..5266df2ae6a6 100644 --- a/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp @@ -24,6 +24,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "llvm/ADT/APSInt.h" #include "llvm/ADT/SmallVector.h" +#include <optional> #include <utility> using namespace clang; @@ -308,26 +309,27 @@ void MallocOverflowSecurityChecker::checkASTCodeBody(const Decl *D, CFGBlock *block = *it; for (CFGBlock::iterator bi = block->begin(), be = block->end(); bi != be; ++bi) { - if (Optional<CFGStmt> CS = bi->getAs<CFGStmt>()) { - if (const CallExpr *TheCall = dyn_cast<CallExpr>(CS->getStmt())) { - // Get the callee. - const FunctionDecl *FD = TheCall->getDirectCallee(); - - if (!FD) - continue; - - // Get the name of the callee. If it's a builtin, strip off the prefix. - IdentifierInfo *FnInfo = FD->getIdentifier(); - if (!FnInfo) - continue; - - if (FnInfo->isStr ("malloc") || FnInfo->isStr ("_MALLOC")) { - if (TheCall->getNumArgs() == 1) - CheckMallocArgument(PossibleMallocOverflows, TheCall, - mgr.getASTContext()); + if (std::optional<CFGStmt> CS = bi->getAs<CFGStmt>()) { + if (const CallExpr *TheCall = dyn_cast<CallExpr>(CS->getStmt())) { + // Get the callee. + const FunctionDecl *FD = TheCall->getDirectCallee(); + + if (!FD) + continue; + + // Get the name of the callee. If it's a builtin, strip off the + // prefix. + IdentifierInfo *FnInfo = FD->getIdentifier(); + if (!FnInfo) + continue; + + if (FnInfo->isStr("malloc") || FnInfo->isStr("_MALLOC")) { + if (TheCall->getNumArgs() == 1) + CheckMallocArgument(PossibleMallocOverflows, TheCall, + mgr.getASTContext()); + } } } - } } } diff --git a/clang/lib/StaticAnalyzer/Checkers/MismatchedIteratorChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MismatchedIteratorChecker.cpp index 1960873599f7..2020dc7cc791 100644 --- a/clang/lib/StaticAnalyzer/Checkers/MismatchedIteratorChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MismatchedIteratorChecker.cpp @@ -176,8 +176,10 @@ void MismatchedIteratorChecker::checkPreCall(const CallEvent &Call, const auto *Param = Func->getParamDecl(J); const auto *ParamType = Param->getType()->getAs<SubstTemplateTypeParmType>(); - if (!ParamType || - ParamType->getReplacedParameter()->getDecl() != TPDecl) + if (!ParamType) + continue; + const TemplateTypeParmDecl *D = ParamType->getReplacedParameter(); + if (D != TPDecl) continue; if (LHS.isUndef()) { LHS = Call.getArgSVal(J); diff --git a/clang/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp index 1906ca5c8f55..0b3d635a50a3 100644 --- a/clang/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp @@ -33,7 +33,7 @@ class MmapWriteExecChecker : public Checker<check::PreCall> { static int ProtRead; mutable std::unique_ptr<BugType> BT; public: - MmapWriteExecChecker() : MmapFn("mmap", 6), MprotectFn("mprotect", 3) {} + MmapWriteExecChecker() : MmapFn({"mmap"}, 6), MprotectFn({"mprotect"}, 3) {} void checkPreCall(const CallEvent &Call, CheckerContext &C) const; int ProtExecOv; int ProtReadOv; diff --git a/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp index e78c130a9c22..c8ddf3b2c14f 100644 --- a/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp @@ -309,7 +309,7 @@ MoveChecker::MovedBugVisitor::VisitNode(const ExplodedNode *N, // If it's not a dereference, we don't care if it was reset to null // or that it is even a smart pointer. - LLVM_FALLTHROUGH; + [[fallthrough]]; case SK_NonStd: case SK_Safe: OS << "Object"; @@ -587,7 +587,7 @@ void MoveChecker::explainObject(llvm::raw_ostream &OS, const MemRegion *MR, break; // We only care about the type if it's a dereference. - LLVM_FALLTHROUGH; + [[fallthrough]]; case SK_Unsafe: OS << " of type '" << RD->getQualifiedNameAsString() << "'"; break; @@ -618,10 +618,6 @@ void MoveChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { if (!IC) return; - // Calling a destructor on a moved object is fine. - if (isa<CXXDestructorCall>(IC)) - return; - const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion(); if (!ThisRegion) return; @@ -631,6 +627,10 @@ void MoveChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { if (!MethodDecl) return; + // Calling a destructor on a moved object is fine. + if (isa<CXXDestructorDecl>(MethodDecl)) + return; + // We want to investigate the whole object, not only sub-object of a parent // class in which the encountered method defined. ThisRegion = ThisRegion->getMostDerivedObjectRegion(); diff --git a/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp index fea35d03cb81..59741dde1eea 100644 --- a/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp @@ -24,6 +24,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" +#include <optional> using namespace clang; using namespace ento; @@ -118,7 +119,7 @@ void CFErrorFunctionChecker::checkASTDecl(const FunctionDecl *D, II = &D->getASTContext().Idents.get("CFErrorRef"); bool hasCFError = false; - for (auto I : D->parameters()) { + for (auto *I : D->parameters()) { if (IsCFError(I->getType(), II)) { hasCFError = true; break; @@ -197,7 +198,7 @@ static void setFlag(ProgramStateRef state, SVal val, CheckerContext &C) { static QualType parameterTypeFromSVal(SVal val, CheckerContext &C) { const StackFrameContext * SFC = C.getStackFrame(); - if (Optional<loc::MemRegionVal> X = val.getAs<loc::MemRegionVal>()) { + if (std::optional<loc::MemRegionVal> X = val.getAs<loc::MemRegionVal>()) { const MemRegion* R = X->getRegion(); if (const VarRegion *VR = R->getAs<VarRegion>()) if (const StackArgumentsSpaceRegion * diff --git a/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp index af208e867318..17c3cb4e9e04 100644 --- a/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp @@ -44,9 +44,11 @@ void NoReturnFunctionChecker::checkPostCall(const CallEvent &CE, if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(CE.getDecl())) BuildSinks = FD->hasAttr<AnalyzerNoReturnAttr>() || FD->isNoReturn(); - const Expr *Callee = CE.getOriginExpr(); - if (!BuildSinks && Callee) - BuildSinks = getFunctionExtInfo(Callee->getType()).getNoReturn(); + if (const CallExpr *CExpr = dyn_cast_or_null<CallExpr>(CE.getOriginExpr()); + CExpr && !BuildSinks) { + if (const Expr *C = CExpr->getCallee()) + BuildSinks = getFunctionExtInfo(C->getType()).getNoReturn(); + } if (!BuildSinks && CE.isGlobalCFunction()) { if (const IdentifierInfo *II = CE.getCalleeIdentifier()) { diff --git a/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp index fb6afd0fdabc..fd47e19cb786 100644 --- a/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp @@ -136,10 +136,10 @@ void NonNullParamChecker::checkPreCall(const CallEvent &Call, if (!DV) continue; - assert(!HasRefTypeParam || isa<Loc>(DV.value())); + assert(!HasRefTypeParam || isa<Loc>(*DV)); // Process the case when the argument is not a location. - if (ExpectedToBeNonNull && !isa<Loc>(DV.value())) { + if (ExpectedToBeNonNull && !isa<Loc>(*DV)) { // If the argument is a union type, we want to handle a potential // transparent_union GCC extension. if (!ArgE) diff --git a/clang/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp index c5437b16c688..72c6a869d225 100644 --- a/clang/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp @@ -26,6 +26,7 @@ #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include <optional> using namespace clang; using namespace ento; @@ -77,7 +78,8 @@ void NonnullGlobalConstantsChecker::checkLocation(SVal location, bool isLoad, if (isGlobalConstString(location)) { SVal V = State->getSVal(location.castAs<Loc>()); - Optional<DefinedOrUnknownSVal> Constr = V.getAs<DefinedOrUnknownSVal>(); + std::optional<DefinedOrUnknownSVal> Constr = + V.getAs<DefinedOrUnknownSVal>(); if (Constr) { @@ -91,7 +93,7 @@ void NonnullGlobalConstantsChecker::checkLocation(SVal location, bool isLoad, /// \param V loaded lvalue. /// \return whether @c val is a string-like const global. bool NonnullGlobalConstantsChecker::isGlobalConstString(SVal V) const { - Optional<loc::MemRegionVal> RegionVal = V.getAs<loc::MemRegionVal>(); + std::optional<loc::MemRegionVal> RegionVal = V.getAs<loc::MemRegionVal>(); if (!RegionVal) return false; auto *Region = dyn_cast<VarRegion>(RegionVal->getAsRegion()); @@ -109,17 +111,20 @@ bool NonnullGlobalConstantsChecker::isGlobalConstString(SVal V) const { // Look through the typedefs. while (const Type *T = Ty.getTypePtr()) { - if (const auto *TT = dyn_cast<TypedefType>(T)) { + if (const auto *AT = dyn_cast<AttributedType>(T)) { + if (AT->getAttrKind() == attr::TypeNonNull) + return true; + Ty = AT->getModifiedType(); + } else if (const auto *ET = dyn_cast<ElaboratedType>(T)) { + const auto *TT = dyn_cast<TypedefType>(ET->getNamedType()); + if (!TT) + return false; Ty = TT->getDecl()->getUnderlyingType(); // It is sufficient for any intermediate typedef // to be classified const. HasConst = HasConst || Ty.isConstQualified(); if (isNonnullType(Ty) && HasConst) return true; - } else if (const auto *AT = dyn_cast<AttributedType>(T)) { - if (AT->getAttrKind() == attr::TypeNonNull) - return true; - Ty = AT->getModifiedType(); } else { return false; } @@ -136,7 +141,7 @@ bool NonnullGlobalConstantsChecker::isNonnullType(QualType Ty) const { if (auto *T = dyn_cast<ObjCObjectPointerType>(Ty)) { return T->getInterfaceDecl() && T->getInterfaceDecl()->getIdentifier() == NSStringII; - } else if (auto *T = dyn_cast<TypedefType>(Ty)) { + } else if (auto *T = Ty->getAs<TypedefType>()) { IdentifierInfo* II = T->getDecl()->getIdentifier(); return II == CFStringRefII || II == CFBooleanRefII || II == CFNullRefII; } diff --git a/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp index 1d8835f6b474..da8529f4ea81 100644 --- a/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp @@ -80,7 +80,7 @@ enum class ErrorKind : int { class NullabilityChecker : public Checker<check::Bind, check::PreCall, check::PreStmt<ReturnStmt>, check::PostCall, check::PostStmt<ExplicitCastExpr>, - check::PostObjCMessage, check::DeadSymbols, + check::PostObjCMessage, check::DeadSymbols, eval::Assume, check::Location, check::Event<ImplicitNullDerefEvent>> { public: @@ -102,6 +102,8 @@ public: void checkEvent(ImplicitNullDerefEvent Event) const; void checkLocation(SVal Location, bool IsLoad, const Stmt *S, CheckerContext &C) const; + ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond, + bool Assumption) const; void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, const char *Sep) const override; @@ -129,7 +131,7 @@ public: // When set to false no nullability information will be tracked in // NullabilityMap. It is possible to catch errors like passing a null pointer // to a callee that expects nonnull argument without the information that is - // stroed in the NullabilityMap. This is an optimization. + // stored in the NullabilityMap. This is an optimization. bool NeedTracking = false; private: @@ -230,10 +232,41 @@ bool operator==(NullabilityState Lhs, NullabilityState Rhs) { Lhs.getNullabilitySource() == Rhs.getNullabilitySource(); } +// For the purpose of tracking historical property accesses, the key for lookup +// is an object pointer (could be an instance or a class) paired with the unique +// identifier for the property being invoked on that object. +using ObjectPropPair = std::pair<const MemRegion *, const IdentifierInfo *>; + +// Metadata associated with the return value from a recorded property access. +struct ConstrainedPropertyVal { + // This will reference the conjured return SVal for some call + // of the form [object property] + DefinedOrUnknownSVal Value; + + // If the SVal has been determined to be nonnull, that is recorded here + bool isConstrainedNonnull; + + ConstrainedPropertyVal(DefinedOrUnknownSVal SV) + : Value(SV), isConstrainedNonnull(false) {} + + void Profile(llvm::FoldingSetNodeID &ID) const { + Value.Profile(ID); + ID.AddInteger(isConstrainedNonnull ? 1 : 0); + } +}; + +bool operator==(const ConstrainedPropertyVal &Lhs, + const ConstrainedPropertyVal &Rhs) { + return Lhs.Value == Rhs.Value && + Lhs.isConstrainedNonnull == Rhs.isConstrainedNonnull; +} + } // end anonymous namespace REGISTER_MAP_WITH_PROGRAMSTATE(NullabilityMap, const MemRegion *, NullabilityState) +REGISTER_MAP_WITH_PROGRAMSTATE(PropertyAccessesMap, ObjectPropPair, + ConstrainedPropertyVal) // We say "the nullability type invariant is violated" when a location with a // non-null type contains NULL or a function with a non-null return type returns @@ -285,8 +318,11 @@ NullabilityChecker::getTrackRegion(SVal Val, bool CheckSuperRegion) const { const MemRegion *Region = RegionSVal->getRegion(); if (CheckSuperRegion) { - if (auto FieldReg = Region->getAs<FieldRegion>()) + if (const SubRegion *FieldReg = Region->getAs<FieldRegion>()) { + if (const auto *ER = dyn_cast<ElementRegion>(FieldReg->getSuperRegion())) + FieldReg = ER; return dyn_cast<SymbolicRegion>(FieldReg->getSuperRegion()); + } if (auto ElementReg = Region->getAs<ElementRegion>()) return dyn_cast<SymbolicRegion>(ElementReg->getSuperRegion()); } @@ -464,6 +500,19 @@ void NullabilityChecker::checkDeadSymbols(SymbolReaper &SR, State = State->remove<NullabilityMap>(I->first); } } + + // When an object goes out of scope, we can free the history associated + // with any property accesses on that object + PropertyAccessesMapTy PropertyAccesses = State->get<PropertyAccessesMap>(); + for (PropertyAccessesMapTy::iterator I = PropertyAccesses.begin(), + E = PropertyAccesses.end(); + I != E; ++I) { + const MemRegion *ReceiverRegion = I->first.first; + if (!SR.isLiveRegion(ReceiverRegion)) { + State = State->remove<PropertyAccessesMap>(I->first); + } + } + // When one of the nonnull arguments are constrained to be null, nullability // preconditions are violated. It is not enough to check this only when we // actually report an error, because at that time interesting symbols might be @@ -851,6 +900,32 @@ static Nullability getReceiverNullability(const ObjCMethodCall &M, return Nullability::Unspecified; } +// The return value of a property access is typically a temporary value which +// will not be tracked in a persistent manner by the analyzer. We use +// evalAssume() in order to immediately record constraints on those temporaries +// at the time they are imposed (e.g. by a nil-check conditional). +ProgramStateRef NullabilityChecker::evalAssume(ProgramStateRef State, SVal Cond, + bool Assumption) const { + PropertyAccessesMapTy PropertyAccesses = State->get<PropertyAccessesMap>(); + for (PropertyAccessesMapTy::iterator I = PropertyAccesses.begin(), + E = PropertyAccesses.end(); + I != E; ++I) { + if (!I->second.isConstrainedNonnull) { + ConditionTruthVal IsNonNull = State->isNonNull(I->second.Value); + if (IsNonNull.isConstrainedTrue()) { + ConstrainedPropertyVal Replacement = I->second; + Replacement.isConstrainedNonnull = true; + State = State->set<PropertyAccessesMap>(I->first, Replacement); + } else if (IsNonNull.isConstrainedFalse()) { + // Space optimization: no point in tracking constrained-null cases + State = State->remove<PropertyAccessesMap>(I->first); + } + } + } + + return State; +} + /// Calculate the nullability of the result of a message expr based on the /// nullability of the receiver, the nullability of the return value, and the /// constraints. @@ -907,7 +982,7 @@ void NullabilityChecker::checkPostObjCMessage(const ObjCMethodCall &M, // this class of methods reduced the emitted diagnostics by about 30% on // some projects (and all of that was false positives). if (Name.contains("String")) { - for (auto Param : M.parameters()) { + for (auto *Param : M.parameters()) { if (Param->getName() == "encoding") { State = State->set<NullabilityMap>(ReturnRegion, Nullability::Contradicted); @@ -947,12 +1022,53 @@ void NullabilityChecker::checkPostObjCMessage(const ObjCMethodCall &M, // No tracked information. Use static type information for return value. Nullability RetNullability = getNullabilityAnnotation(RetType); - // Properties might be computed. For this reason the static analyzer creates a - // new symbol each time an unknown property is read. To avoid false pozitives - // do not treat unknown properties as nullable, even when they explicitly - // marked nullable. - if (M.getMessageKind() == OCM_PropertyAccess && !C.wasInlined) - RetNullability = Nullability::Nonnull; + // Properties might be computed, which means the property value could + // theoretically change between calls even in commonly-observed cases like + // this: + // + // if (foo.prop) { // ok, it's nonnull here... + // [bar doStuffWithNonnullVal:foo.prop]; // ...but what about + // here? + // } + // + // If the property is nullable-annotated, a naive analysis would lead to many + // false positives despite the presence of probably-correct nil-checks. To + // reduce the false positive rate, we maintain a history of the most recently + // observed property value. For each property access, if the prior value has + // been constrained to be not nil then we will conservatively assume that the + // next access can be inferred as nonnull. + if (RetNullability != Nullability::Nonnull && + M.getMessageKind() == OCM_PropertyAccess && !C.wasInlined) { + bool LookupResolved = false; + if (const MemRegion *ReceiverRegion = getTrackRegion(M.getReceiverSVal())) { + if (IdentifierInfo *Ident = M.getSelector().getIdentifierInfoForSlot(0)) { + LookupResolved = true; + ObjectPropPair Key = std::make_pair(ReceiverRegion, Ident); + const ConstrainedPropertyVal *PrevPropVal = + State->get<PropertyAccessesMap>(Key); + if (PrevPropVal && PrevPropVal->isConstrainedNonnull) { + RetNullability = Nullability::Nonnull; + } else { + // If a previous property access was constrained as nonnull, we hold + // on to that constraint (effectively inferring that all subsequent + // accesses on that code path can be inferred as nonnull). If the + // previous property access was *not* constrained as nonnull, then + // let's throw it away in favor of keeping the SVal associated with + // this more recent access. + if (auto ReturnSVal = + M.getReturnValue().getAs<DefinedOrUnknownSVal>()) { + State = State->set<PropertyAccessesMap>( + Key, ConstrainedPropertyVal(*ReturnSVal)); + } + } + } + } + + if (!LookupResolved) { + // Fallback: err on the side of suppressing the false positive. + RetNullability = Nullability::Nonnull; + } + } Nullability ComputedNullab = getMostNullable(RetNullability, SelfNullability); if (ComputedNullab == Nullability::Nullable) { diff --git a/clang/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp index 3e9fc696f8e6..f217520d8f4a 100644 --- a/clang/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp @@ -196,12 +196,10 @@ void NumberObjectConversionChecker::checkASTCodeBody(const Decl *D, AnalysisManager &AM, BugReporter &BR) const { // Currently this matches CoreFoundation opaque pointer typedefs. - auto CSuspiciousNumberObjectExprM = - expr(ignoringParenImpCasts( - expr(hasType( - typedefType(hasDeclaration(anyOf( - typedefDecl(hasName("CFNumberRef")), - typedefDecl(hasName("CFBooleanRef"))))))) + auto CSuspiciousNumberObjectExprM = expr(ignoringParenImpCasts( + expr(hasType(elaboratedType(namesType(typedefType( + hasDeclaration(anyOf(typedefDecl(hasName("CFNumberRef")), + typedefDecl(hasName("CFBooleanRef"))))))))) .bind("c_object"))); // Currently this matches XNU kernel number-object pointers. @@ -240,8 +238,9 @@ void NumberObjectConversionChecker::checkASTCodeBody(const Decl *D, // The .bind here is in order to compose the error message more accurately. auto ObjCSuspiciousScalarBooleanTypeM = - qualType(typedefType(hasDeclaration( - typedefDecl(hasName("BOOL"))))).bind("objc_bool_type"); + qualType(elaboratedType(namesType( + typedefType(hasDeclaration(typedefDecl(hasName("BOOL"))))))) + .bind("objc_bool_type"); // The .bind here is in order to compose the error message more accurately. auto SuspiciousScalarBooleanTypeM = @@ -253,9 +252,9 @@ void NumberObjectConversionChecker::checkASTCodeBody(const Decl *D, // for storing pointers. auto SuspiciousScalarNumberTypeM = qualType(hasCanonicalType(isInteger()), - unless(typedefType(hasDeclaration( - typedefDecl(matchesName("^::u?intptr_t$")))))) - .bind("int_type"); + unless(elaboratedType(namesType(typedefType(hasDeclaration( + typedefDecl(matchesName("^::u?intptr_t$")))))))) + .bind("int_type"); auto SuspiciousScalarTypeM = qualType(anyOf(SuspiciousScalarBooleanTypeM, diff --git a/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp index 6688278a7a33..2b008d1c775a 100644 --- a/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp @@ -72,7 +72,7 @@ class WalkAST : public StmtVisitor<WalkAST> { public: WalkAST(BugReporter &br, const CheckerBase *checker, AnalysisDeclContext *ac) : BR(br), Checker(checker), AC(ac), ASTC(AC->getASTContext()), - PtrWidth(ASTC.getTargetInfo().getPointerWidth(0)) {} + PtrWidth(ASTC.getTargetInfo().getPointerWidth(LangAS::Default)) {} // Statement visitor methods. void VisitChildren(Stmt *S); diff --git a/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp index e877dd119ff6..929bd6bc3eb3 100644 --- a/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp @@ -87,7 +87,7 @@ private: CheckerKind CheckKind) const; CallDescriptionMap<FnCheck> PThreadCallbacks = { // Init. - {{"pthread_mutex_init", 2}, &PthreadLockChecker::InitAnyLock}, + {{{"pthread_mutex_init"}, 2}, &PthreadLockChecker::InitAnyLock}, // TODO: pthread_rwlock_init(2 arguments). // TODO: lck_mtx_init(3 arguments). // TODO: lck_mtx_alloc_init(2 arguments) => returns the mutex. @@ -95,74 +95,74 @@ private: // TODO: lck_rw_alloc_init(2 arguments) => returns the mutex. // Acquire. - {{"pthread_mutex_lock", 1}, &PthreadLockChecker::AcquirePthreadLock}, - {{"pthread_rwlock_rdlock", 1}, &PthreadLockChecker::AcquirePthreadLock}, - {{"pthread_rwlock_wrlock", 1}, &PthreadLockChecker::AcquirePthreadLock}, - {{"lck_mtx_lock", 1}, &PthreadLockChecker::AcquireXNULock}, - {{"lck_rw_lock_exclusive", 1}, &PthreadLockChecker::AcquireXNULock}, - {{"lck_rw_lock_shared", 1}, &PthreadLockChecker::AcquireXNULock}, + {{{"pthread_mutex_lock"}, 1}, &PthreadLockChecker::AcquirePthreadLock}, + {{{"pthread_rwlock_rdlock"}, 1}, &PthreadLockChecker::AcquirePthreadLock}, + {{{"pthread_rwlock_wrlock"}, 1}, &PthreadLockChecker::AcquirePthreadLock}, + {{{"lck_mtx_lock"}, 1}, &PthreadLockChecker::AcquireXNULock}, + {{{"lck_rw_lock_exclusive"}, 1}, &PthreadLockChecker::AcquireXNULock}, + {{{"lck_rw_lock_shared"}, 1}, &PthreadLockChecker::AcquireXNULock}, // Try. - {{"pthread_mutex_trylock", 1}, &PthreadLockChecker::TryPthreadLock}, - {{"pthread_rwlock_tryrdlock", 1}, &PthreadLockChecker::TryPthreadLock}, - {{"pthread_rwlock_trywrlock", 1}, &PthreadLockChecker::TryPthreadLock}, - {{"lck_mtx_try_lock", 1}, &PthreadLockChecker::TryXNULock}, - {{"lck_rw_try_lock_exclusive", 1}, &PthreadLockChecker::TryXNULock}, - {{"lck_rw_try_lock_shared", 1}, &PthreadLockChecker::TryXNULock}, + {{{"pthread_mutex_trylock"}, 1}, &PthreadLockChecker::TryPthreadLock}, + {{{"pthread_rwlock_tryrdlock"}, 1}, &PthreadLockChecker::TryPthreadLock}, + {{{"pthread_rwlock_trywrlock"}, 1}, &PthreadLockChecker::TryPthreadLock}, + {{{"lck_mtx_try_lock"}, 1}, &PthreadLockChecker::TryXNULock}, + {{{"lck_rw_try_lock_exclusive"}, 1}, &PthreadLockChecker::TryXNULock}, + {{{"lck_rw_try_lock_shared"}, 1}, &PthreadLockChecker::TryXNULock}, // Release. - {{"pthread_mutex_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock}, - {{"pthread_rwlock_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock}, - {{"lck_mtx_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock}, - {{"lck_rw_unlock_exclusive", 1}, &PthreadLockChecker::ReleaseAnyLock}, - {{"lck_rw_unlock_shared", 1}, &PthreadLockChecker::ReleaseAnyLock}, - {{"lck_rw_done", 1}, &PthreadLockChecker::ReleaseAnyLock}, + {{{"pthread_mutex_unlock"}, 1}, &PthreadLockChecker::ReleaseAnyLock}, + {{{"pthread_rwlock_unlock"}, 1}, &PthreadLockChecker::ReleaseAnyLock}, + {{{"lck_mtx_unlock"}, 1}, &PthreadLockChecker::ReleaseAnyLock}, + {{{"lck_rw_unlock_exclusive"}, 1}, &PthreadLockChecker::ReleaseAnyLock}, + {{{"lck_rw_unlock_shared"}, 1}, &PthreadLockChecker::ReleaseAnyLock}, + {{{"lck_rw_done"}, 1}, &PthreadLockChecker::ReleaseAnyLock}, // Destroy. - {{"pthread_mutex_destroy", 1}, &PthreadLockChecker::DestroyPthreadLock}, - {{"lck_mtx_destroy", 2}, &PthreadLockChecker::DestroyXNULock}, + {{{"pthread_mutex_destroy"}, 1}, &PthreadLockChecker::DestroyPthreadLock}, + {{{"lck_mtx_destroy"}, 2}, &PthreadLockChecker::DestroyXNULock}, // TODO: pthread_rwlock_destroy(1 argument). // TODO: lck_rw_destroy(2 arguments). }; CallDescriptionMap<FnCheck> FuchsiaCallbacks = { // Init. - {{"spin_lock_init", 1}, &PthreadLockChecker::InitAnyLock}, + {{{"spin_lock_init"}, 1}, &PthreadLockChecker::InitAnyLock}, // Acquire. - {{"spin_lock", 1}, &PthreadLockChecker::AcquirePthreadLock}, - {{"spin_lock_save", 3}, &PthreadLockChecker::AcquirePthreadLock}, - {{"sync_mutex_lock", 1}, &PthreadLockChecker::AcquirePthreadLock}, - {{"sync_mutex_lock_with_waiter", 1}, + {{{"spin_lock"}, 1}, &PthreadLockChecker::AcquirePthreadLock}, + {{{"spin_lock_save"}, 3}, &PthreadLockChecker::AcquirePthreadLock}, + {{{"sync_mutex_lock"}, 1}, &PthreadLockChecker::AcquirePthreadLock}, + {{{"sync_mutex_lock_with_waiter"}, 1}, &PthreadLockChecker::AcquirePthreadLock}, // Try. - {{"spin_trylock", 1}, &PthreadLockChecker::TryFuchsiaLock}, - {{"sync_mutex_trylock", 1}, &PthreadLockChecker::TryFuchsiaLock}, - {{"sync_mutex_timedlock", 2}, &PthreadLockChecker::TryFuchsiaLock}, + {{{"spin_trylock"}, 1}, &PthreadLockChecker::TryFuchsiaLock}, + {{{"sync_mutex_trylock"}, 1}, &PthreadLockChecker::TryFuchsiaLock}, + {{{"sync_mutex_timedlock"}, 2}, &PthreadLockChecker::TryFuchsiaLock}, // Release. - {{"spin_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock}, - {{"spin_unlock_restore", 3}, &PthreadLockChecker::ReleaseAnyLock}, - {{"sync_mutex_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock}, + {{{"spin_unlock"}, 1}, &PthreadLockChecker::ReleaseAnyLock}, + {{{"spin_unlock_restore"}, 3}, &PthreadLockChecker::ReleaseAnyLock}, + {{{"sync_mutex_unlock"}, 1}, &PthreadLockChecker::ReleaseAnyLock}, }; CallDescriptionMap<FnCheck> C11Callbacks = { // Init. - {{"mtx_init", 2}, &PthreadLockChecker::InitAnyLock}, + {{{"mtx_init"}, 2}, &PthreadLockChecker::InitAnyLock}, // Acquire. - {{"mtx_lock", 1}, &PthreadLockChecker::AcquirePthreadLock}, + {{{"mtx_lock"}, 1}, &PthreadLockChecker::AcquirePthreadLock}, // Try. - {{"mtx_trylock", 1}, &PthreadLockChecker::TryC11Lock}, - {{"mtx_timedlock", 2}, &PthreadLockChecker::TryC11Lock}, + {{{"mtx_trylock"}, 1}, &PthreadLockChecker::TryC11Lock}, + {{{"mtx_timedlock"}, 2}, &PthreadLockChecker::TryC11Lock}, // Release. - {{"mtx_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock}, + {{{"mtx_unlock"}, 1}, &PthreadLockChecker::ReleaseAnyLock}, // Destroy - {{"mtx_destroy", 1}, &PthreadLockChecker::DestroyPthreadLock}, + {{{"mtx_destroy"}, 1}, &PthreadLockChecker::DestroyPthreadLock}, }; ProgramStateRef resolvePossiblyDestroyedMutex(ProgramStateRef state, diff --git a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp index bee7c468812c..01c71d91d1a1 100644 --- a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp @@ -14,6 +14,7 @@ #include "RetainCountChecker.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include <optional> using namespace clang; using namespace ento; @@ -284,7 +285,7 @@ void RetainCountChecker::checkPostStmt(const ObjCBoxedExpr *Ex, void RetainCountChecker::checkPostStmt(const ObjCIvarRefExpr *IRE, CheckerContext &C) const { - Optional<Loc> IVarLoc = C.getSVal(IRE).getAs<Loc>(); + std::optional<Loc> IVarLoc = C.getSVal(IRE).getAs<Loc>(); if (!IVarLoc) return; @@ -412,15 +413,15 @@ static QualType GetReturnType(const Expr *RetE, ASTContext &Ctx) { return RetTy; } -static Optional<RefVal> refValFromRetEffect(RetEffect RE, - QualType ResultTy) { +static std::optional<RefVal> refValFromRetEffect(RetEffect RE, + QualType ResultTy) { if (RE.isOwned()) { return RefVal::makeOwned(RE.getObjKind(), ResultTy); } else if (RE.notOwned()) { return RefVal::makeNotOwned(RE.getObjKind(), ResultTy); } - return None; + return std::nullopt; } static bool isPointerToObject(QualType QT) { @@ -692,7 +693,7 @@ void RetainCountChecker::checkSummary(const RetainSummary &Summ, assert(Ex); ResultTy = GetReturnType(Ex, C.getASTContext()); } - if (Optional<RefVal> updatedRefVal = refValFromRetEffect(RE, ResultTy)) + if (std::optional<RefVal> updatedRefVal = refValFromRetEffect(RE, ResultTy)) state = setRefBinding(state, Sym, *updatedRefVal); } @@ -767,7 +768,7 @@ ProgramStateRef RetainCountChecker::updateSymbol(ProgramStateRef state, break; } - LLVM_FALLTHROUGH; + [[fallthrough]]; case DoNothing: return state; @@ -907,7 +908,7 @@ bool RetainCountChecker::evalCall(const CallEvent &Call, const LocationContext *LCtx = C.getLocationContext(); using BehaviorSummary = RetainSummaryManager::BehaviorSummary; - Optional<BehaviorSummary> BSmr = + std::optional<BehaviorSummary> BSmr = SmrMgr.canEval(CE, FD, hasTrustedImplementationAnnotation); // See if it's one of the specific functions we know how to eval. @@ -1336,7 +1337,7 @@ void RetainCountChecker::checkBeginFunction(CheckerContext &Ctx) const { RetainSummaryManager &SmrMgr = getSummaryManager(Ctx); const LocationContext *LCtx = Ctx.getLocationContext(); const Decl *D = LCtx->getDecl(); - Optional<AnyCall> C = AnyCall::forDecl(D); + std::optional<AnyCall> C = AnyCall::forDecl(D); if (!C || SmrMgr.isTrustedReferenceCountImplementation(D)) return; diff --git a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp index 5109ae668686..e11e509f159d 100644 --- a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp @@ -15,6 +15,7 @@ #include "RetainCountChecker.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" +#include <optional> using namespace clang; using namespace ento; @@ -165,13 +166,12 @@ static bool shouldGenerateNote(llvm::raw_string_ostream &os, /// Finds argument index of the out paramter in the call @c S /// corresponding to the symbol @c Sym. -/// If none found, returns None. -static Optional<unsigned> findArgIdxOfSymbol(ProgramStateRef CurrSt, - const LocationContext *LCtx, - SymbolRef &Sym, - Optional<CallEventRef<>> CE) { +/// If none found, returns std::nullopt. +static std::optional<unsigned> +findArgIdxOfSymbol(ProgramStateRef CurrSt, const LocationContext *LCtx, + SymbolRef &Sym, std::optional<CallEventRef<>> CE) { if (!CE) - return None; + return std::nullopt; for (unsigned Idx = 0; Idx < (*CE)->getNumArgs(); Idx++) if (const MemRegion *MR = (*CE)->getArgSVal(Idx).getAsRegion()) @@ -179,25 +179,25 @@ static Optional<unsigned> findArgIdxOfSymbol(ProgramStateRef CurrSt, if (CurrSt->getSVal(MR, TR->getValueType()).getAsSymbol() == Sym) return Idx; - return None; + return std::nullopt; } -static Optional<std::string> findMetaClassAlloc(const Expr *Callee) { +static std::optional<std::string> findMetaClassAlloc(const Expr *Callee) { if (const auto *ME = dyn_cast<MemberExpr>(Callee)) { if (ME->getMemberDecl()->getNameAsString() != "alloc") - return None; + return std::nullopt; const Expr *This = ME->getBase()->IgnoreParenImpCasts(); if (const auto *DRE = dyn_cast<DeclRefExpr>(This)) { const ValueDecl *VD = DRE->getDecl(); if (VD->getNameAsString() != "metaClass") - return None; + return std::nullopt; if (const auto *RD = dyn_cast<CXXRecordDecl>(VD->getDeclContext())) return RD->getNameAsString(); } } - return None; + return std::nullopt; } static std::string findAllocatedObjectName(const Stmt *S, QualType QT) { @@ -250,7 +250,7 @@ static void generateDiagnosticsForCallLike(ProgramStateRef CurrSt, } } - Optional<CallEventRef<>> CE = Mgr.getCall(S, CurrSt, LCtx); + std::optional<CallEventRef<>> CE = Mgr.getCall(S, CurrSt, LCtx); auto Idx = findArgIdxOfSymbol(CurrSt, LCtx, Sym, CE); // If index is not found, we assume that the symbol was returned. @@ -602,16 +602,17 @@ RefCountReportVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC, return std::move(P); } -static Optional<std::string> describeRegion(const MemRegion *MR) { +static std::optional<std::string> describeRegion(const MemRegion *MR) { if (const auto *VR = dyn_cast_or_null<VarRegion>(MR)) return std::string(VR->getDecl()->getName()); // Once we support more storage locations for bindings, // this would need to be improved. - return None; + return std::nullopt; } using Bindings = llvm::SmallVector<std::pair<const MemRegion *, SVal>, 4>; +namespace { class VarBindingsCollector : public StoreManager::BindingsHandler { SymbolRef Sym; Bindings &Result; @@ -632,6 +633,7 @@ public: return true; } }; +} // namespace Bindings getAllVarBindingsForSymbol(ProgramStateManager &Manager, const ExplodedNode *Node, SymbolRef Sym) { @@ -728,7 +730,7 @@ static AllocationInfo GetAllocationSite(ProgramStateManager &StateMgr, const LocationContext *InterestingMethodContext = nullptr; if (InitMethodContext) { const ProgramPoint AllocPP = AllocationNode->getLocation(); - if (Optional<StmtPoint> SP = AllocPP.getAs<StmtPoint>()) + if (std::optional<StmtPoint> SP = AllocPP.getAs<StmtPoint>()) if (const ObjCMessageExpr *ME = SP->getStmtAs<ObjCMessageExpr>()) if (ME->getMethodFamily() == OMF_alloc) InterestingMethodContext = InitMethodContext; @@ -771,7 +773,7 @@ RefLeakReportVisitor::getEndPath(BugReporterContext &BRC, os << "Object leaked: "; - Optional<std::string> RegionDescription = describeRegion(LastBinding); + std::optional<std::string> RegionDescription = describeRegion(LastBinding); if (RegionDescription) { os << "object allocated and stored into '" << *RegionDescription << '\''; } else { @@ -917,7 +919,7 @@ void RefLeakReport::createDescription(CheckerContext &Ctx) { llvm::raw_string_ostream os(Description); os << "Potential leak of an object"; - Optional<std::string> RegionDescription = + std::optional<std::string> RegionDescription = describeRegion(AllocBindingToReport); if (RegionDescription) { os << " stored into '" << *RegionDescription << '\''; @@ -969,7 +971,7 @@ void RefLeakReport::findBindingToReport(CheckerContext &Ctx, // Let's pick one of them at random (if there is something to pick from). AllocBindingToReport = AllVarBindings[0].first; - // Because 'AllocBindingToReport' is not the the same as + // Because 'AllocBindingToReport' is not the same as // 'AllocFirstBinding', we need to explain how the leaking object // got from one to another. // diff --git a/clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp index cf97439a468d..c3112ebe4e79 100644 --- a/clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp @@ -17,8 +17,8 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" -#include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" +#include <optional> using namespace clang; using namespace ento; @@ -70,11 +70,11 @@ static std::string getName(const CallEvent &Call) { // The predefinitions ('CDM') could break due to the ever growing code base. // Check for the expected invariants and see whether they apply. -static Optional<bool> isInvariantBreak(bool ExpectedValue, SVal ReturnV, - CheckerContext &C) { +static std::optional<bool> isInvariantBreak(bool ExpectedValue, SVal ReturnV, + CheckerContext &C) { auto ReturnDV = ReturnV.getAs<DefinedOrUnknownSVal>(); if (!ReturnDV) - return None; + return std::nullopt; if (ExpectedValue) return C.getState()->isNull(*ReturnDV).isConstrainedTrue(); @@ -90,7 +90,8 @@ void ReturnValueChecker::checkPostCall(const CallEvent &Call, SVal ReturnV = Call.getReturnValue(); bool ExpectedValue = *RawExpectedValue; - Optional<bool> IsInvariantBreak = isInvariantBreak(ExpectedValue, ReturnV, C); + std::optional<bool> IsInvariantBreak = + isInvariantBreak(ExpectedValue, ReturnV, C); if (!IsInvariantBreak) return; @@ -137,7 +138,8 @@ void ReturnValueChecker::checkEndFunction(const ReturnStmt *RS, SVal ReturnV = State->getSVal(RS->getRetValue(), C.getLocationContext()); bool ExpectedValue = *RawExpectedValue; - Optional<bool> IsInvariantBreak = isInvariantBreak(ExpectedValue, ReturnV, C); + std::optional<bool> IsInvariantBreak = + isInvariantBreak(ExpectedValue, ReturnV, C); if (!IsInvariantBreak) return; diff --git a/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp index 8c87a548fd91..9251c895614c 100644 --- a/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp @@ -90,7 +90,7 @@ public: REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState) SimpleStreamChecker::SimpleStreamChecker() - : OpenFn("fopen"), CloseFn("fclose", 1) { + : OpenFn({"fopen"}), CloseFn({"fclose"}, 1) { // Initialize the bug types. DoubleCloseBugType.reset( new BugType(this, "Double fclose", "Unix Stream API Error")); diff --git a/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp index 92eef20d2daa..5689a63f8dd8 100644 --- a/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp @@ -33,6 +33,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" #include "llvm/ADT/StringMap.h" #include "llvm/Support/ErrorHandling.h" +#include <optional> #include <string> using namespace clang; @@ -85,10 +86,10 @@ private: using SmartPtrMethodHandlerFn = void (SmartPtrModeling::*)(const CallEvent &Call, CheckerContext &) const; CallDescriptionMap<SmartPtrMethodHandlerFn> SmartPtrMethodHandlers{ - {{"reset"}, &SmartPtrModeling::handleReset}, - {{"release"}, &SmartPtrModeling::handleRelease}, - {{"swap", 1}, &SmartPtrModeling::handleSwapMethod}, - {{"get"}, &SmartPtrModeling::handleGet}}; + {{{"reset"}}, &SmartPtrModeling::handleReset}, + {{{"release"}}, &SmartPtrModeling::handleRelease}, + {{{"swap"}, 1}, &SmartPtrModeling::handleSwapMethod}, + {{{"get"}}, &SmartPtrModeling::handleGet}}; const CallDescription StdSwapCall{{"std", "swap"}, 2}; const CallDescription StdMakeUniqueCall{{"std", "make_unique"}}; const CallDescription StdMakeUniqueForOverwriteCall{ @@ -298,8 +299,9 @@ bool SmartPtrModeling::evalCall(const CallEvent &Call, if (matchesAny(Call, StdMakeUniqueCall, StdMakeUniqueForOverwriteCall)) { if (!ModelSmartPtrDereference) return false; - - const Optional<SVal> ThisRegionOpt = Call.getReturnValueUnderConstruction(); + + const std::optional<SVal> ThisRegionOpt = + Call.getReturnValueUnderConstruction(); if (!ThisRegionOpt) return false; diff --git a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp index e6cea0fbff8c..c4b7411e9401 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp @@ -61,7 +61,6 @@ private: ASTContext &Ctx); static SmallVector<const MemRegion *, 4> getCapturedStackRegions(const BlockDataRegion &B, CheckerContext &C); - static bool isArcManagedBlock(const MemRegion *R, CheckerContext &C); static bool isNotInCurrentFrame(const MemRegion *R, CheckerContext &C); }; } // namespace @@ -110,13 +109,6 @@ SourceRange StackAddrEscapeChecker::genName(raw_ostream &os, const MemRegion *R, return range; } -bool StackAddrEscapeChecker::isArcManagedBlock(const MemRegion *R, - CheckerContext &C) { - assert(R && "MemRegion should not be null"); - return C.getASTContext().getLangOpts().ObjCAutoRefCount && - isa<BlockDataRegion>(R); -} - bool StackAddrEscapeChecker::isNotInCurrentFrame(const MemRegion *R, CheckerContext &C) { const StackSpaceRegion *S = cast<StackSpaceRegion>(R->getMemorySpace()); @@ -214,7 +206,7 @@ void StackAddrEscapeChecker::checkAsyncExecutedBlockCaptures( void StackAddrEscapeChecker::checkReturnedBlockCaptures( const BlockDataRegion &B, CheckerContext &C) const { for (const MemRegion *Region : getCapturedStackRegions(B, C)) { - if (isArcManagedBlock(Region, C) || isNotInCurrentFrame(Region, C)) + if (isNotInCurrentFrame(Region, C)) continue; ExplodedNode *N = C.generateNonFatalErrorNode(); if (!N) @@ -267,8 +259,7 @@ void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS, if (const BlockDataRegion *B = dyn_cast<BlockDataRegion>(R)) checkReturnedBlockCaptures(*B, C); - if (!isa<StackSpaceRegion>(R->getMemorySpace()) || - isNotInCurrentFrame(R, C) || isArcManagedBlock(R, C)) + if (!isa<StackSpaceRegion>(R->getMemorySpace()) || isNotInCurrentFrame(R, C)) return; // Returning a record by value is fine. (In this case, the returned @@ -348,8 +339,7 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS, // Check the globals for the same. if (!isa<GlobalsSpaceRegion>(Region->getMemorySpace())) return true; - if (VR && VR->hasStackStorage() && !isArcManagedBlock(VR, Ctx) && - !isNotInCurrentFrame(VR, Ctx)) + if (VR && VR->hasStackStorage() && !isNotInCurrentFrame(VR, Ctx)) V.emplace_back(Region, VR); return true; } diff --git a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp index 5897e5096461..49b3db560843 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp @@ -52,24 +52,12 @@ #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" +#include <optional> #include <string> using namespace clang; using namespace clang::ento; -/// Produce a textual description of the state of \c errno (this describes the -/// way how it is allowed to be used). -/// The returned string is insertable into a longer warning message (in the form -/// "the value 'errno' <...>"). -/// Currently only the \c errno_modeling::MustNotBeChecked state is supported. -/// But later other kind of errno state may be needed if functions with special -/// handling of \c errno are added. -static const char *describeErrnoCheckState(errno_modeling::ErrnoCheckState CS) { - assert(CS == errno_modeling::MustNotBeChecked && - "Errno description not applicable."); - return "may be undefined after the call and should not be used"; -} - namespace { class StdLibraryFunctionsChecker : public Checker<check::PreCall, check::PostCall, eval::Call> { @@ -147,9 +135,18 @@ class StdLibraryFunctionsChecker virtual StringRef getName() const = 0; + // Represents that in which context do we require a description of the + // constraint. + enum class DescriptionKind { + // The constraint is violated. + Violation, + // We assume that the constraint is satisfied. + Assumption + }; + // Give a description that explains the constraint to the user. Used when // the bug is reported. - virtual std::string describe(ProgramStateRef State, + virtual std::string describe(DescriptionKind DK, ProgramStateRef State, const Summary &Summary) const { // There are some descendant classes that are not used as argument // constraints, e.g. ComparisonConstraint. In that case we can safely @@ -187,7 +184,7 @@ class StdLibraryFunctionsChecker RangeConstraint(ArgNo ArgN, RangeKind Kind, const IntRangeVector &Ranges) : ValueConstraint(ArgN), Kind(Kind), Ranges(Ranges) {} - std::string describe(ProgramStateRef State, + std::string describe(DescriptionKind DK, ProgramStateRef State, const Summary &Summary) const override; const IntRangeVector &getRanges() const { return Ranges; } @@ -257,7 +254,9 @@ class StdLibraryFunctionsChecker bool CannotBeNull = true; public: - std::string describe(ProgramStateRef State, + NotNullConstraint(ArgNo ArgN, bool CannotBeNull = true) + : ValueConstraint(ArgN), CannotBeNull(CannotBeNull) {} + std::string describe(DescriptionKind DK, ProgramStateRef State, const Summary &Summary) const override; StringRef getName() const override { return "NonNull"; } ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, @@ -300,13 +299,13 @@ class StdLibraryFunctionsChecker // // Here, ptr is the buffer, and its minimum size is `size * nmemb`. class BufferSizeConstraint : public ValueConstraint { // The concrete value which is the minimum size for the buffer. - llvm::Optional<llvm::APSInt> ConcreteSize; + std::optional<llvm::APSInt> ConcreteSize; // The argument which holds the size of the buffer. - llvm::Optional<ArgNo> SizeArgN; + std::optional<ArgNo> SizeArgN; // The argument which is a multiplier to size. This is set in case of // `fread` like functions where the size is computed as a multiplication of // two arguments. - llvm::Optional<ArgNo> SizeMultiplierArgN; + std::optional<ArgNo> SizeMultiplierArgN; // The operator we use in apply. This is negated in negate(). BinaryOperator::Opcode Op = BO_LE; @@ -329,7 +328,7 @@ class StdLibraryFunctionsChecker return Result; } - std::string describe(ProgramStateRef State, + std::string describe(DescriptionKind DK, ProgramStateRef State, const Summary &Summary) const override; ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, @@ -392,45 +391,67 @@ class StdLibraryFunctionsChecker using ConstraintSet = std::vector<ValueConstraintPtr>; /// Define how a function affects the system variable 'errno'. - /// This works together with the ErrnoModeling and ErrnoChecker classes. + /// This works together with the \c ErrnoModeling and \c ErrnoChecker classes. + /// Currently 3 use cases exist: success, failure, irrelevant. + /// In the future the failure case can be customized to set \c errno to a + /// more specific constraint (for example > 0), or new case can be added + /// for functions which require check of \c errno in both success and failure + /// case. class ErrnoConstraintBase { public: /// Apply specific state changes related to the errno variable. virtual ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, const Summary &Summary, CheckerContext &C) const = 0; - /// Get a description about what is applied to 'errno' and how is it allowed - /// to be used. If ErrnoChecker generates a bug then this message is - /// displayed as a note at the function call. - /// It may return empty string if no note tag is to be added. - virtual std::string describe(StringRef FunctionName) const { return ""; } + /// Get a NoteTag about the changes made to 'errno' and the possible bug. + /// It may return \c nullptr (if no bug report from \c ErrnoChecker is + /// expected). + virtual const NoteTag *describe(CheckerContext &C, + StringRef FunctionName) const { + return nullptr; + } virtual ~ErrnoConstraintBase() {} protected: - /// Many of the descendant classes use this value. - const errno_modeling::ErrnoCheckState CheckState; - - ErrnoConstraintBase(errno_modeling::ErrnoCheckState CS) : CheckState(CS) {} + ErrnoConstraintBase() = default; /// This is used for conjure symbol for errno to differentiate from the /// original call expression (same expression is used for the errno symbol). static int Tag; }; - /// Set value of 'errno' to be related to 0 in a specified way, with a - /// specified "errno check state". For example with \c BO_GT 'errno' is - /// constrained to be greater than 0. Use this for failure cases of functions. - class ZeroRelatedErrnoConstraint : public ErrnoConstraintBase { - BinaryOperatorKind Op; + /// Reset errno constraints to irrelevant. + /// This is applicable to functions that may change 'errno' and are not + /// modeled elsewhere. + class ResetErrnoConstraint : public ErrnoConstraintBase { + public: + ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, + const Summary &Summary, + CheckerContext &C) const override { + return errno_modeling::setErrnoState(State, errno_modeling::Irrelevant); + } + }; + /// Do not change errno constraints. + /// This is applicable to functions that are modeled in another checker + /// and the already set errno constraints should not be changed in the + /// post-call event. + class NoErrnoConstraint : public ErrnoConstraintBase { public: - ZeroRelatedErrnoConstraint(clang::BinaryOperatorKind OpK, - errno_modeling::ErrnoCheckState CS) - : ErrnoConstraintBase(CS), Op(OpK) { - assert(BinaryOperator::isComparisonOp(OpK)); + ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, + const Summary &Summary, + CheckerContext &C) const override { + return State; } + }; + /// Set errno constraint at failure cases of standard functions. + /// Failure case: 'errno' becomes not equal to 0 and may or may not be checked + /// by the program. \c ErrnoChecker does not emit a bug report after such a + /// function call. + class FailureErrnoConstraint : public ErrnoConstraintBase { + public: ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, const Summary &Summary, CheckerContext &C) const override { @@ -440,62 +461,40 @@ class StdLibraryFunctionsChecker C.getLocationContext(), C.getASTContext().IntTy, C.blockCount()) .castAs<NonLoc>(); - NonLoc ZeroVal = - SVB.makeZeroVal(C.getASTContext().IntTy).castAs<NonLoc>(); - DefinedOrUnknownSVal Cond = - SVB.evalBinOp(State, Op, ErrnoSVal, ZeroVal, SVB.getConditionType()) - .castAs<DefinedOrUnknownSVal>(); - State = State->assume(Cond, true); - if (!State) - return State; - return errno_modeling::setErrnoValue(State, C.getLocationContext(), - ErrnoSVal, CheckState); - } - - std::string describe(StringRef FunctionName) const override { - if (CheckState == errno_modeling::Irrelevant) - return ""; - return (Twine("Assuming that function '") + FunctionName.str() + - "' fails, in this case the value 'errno' becomes " + - BinaryOperator::getOpcodeStr(Op).str() + " 0 and " + - describeErrnoCheckState(CheckState)) - .str(); + return errno_modeling::setErrnoForStdFailure(State, C, ErrnoSVal); } }; - /// Applies the constraints to 'errno' for a common case when a standard - /// function is successful. The value of 'errno' after the call is not - /// specified by the standard (it may change or not). The \c ErrnoChecker can - /// generate a bug if 'errno' is read afterwards. + /// Set errno constraint at success cases of standard functions. + /// Success case: 'errno' is not allowed to be used. + /// \c ErrnoChecker can emit bug report after such a function call if errno + /// is used. class SuccessErrnoConstraint : public ErrnoConstraintBase { public: - SuccessErrnoConstraint() - : ErrnoConstraintBase(errno_modeling::MustNotBeChecked) {} - ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, const Summary &Summary, CheckerContext &C) const override { - return errno_modeling::setErrnoState(State, CheckState); + return errno_modeling::setErrnoForStdSuccess(State, C); } - std::string describe(StringRef FunctionName) const override { - return (Twine("Assuming that function '") + FunctionName.str() + - "' is successful, in this case the value 'errno' " + - describeErrnoCheckState(CheckState)) - .str(); + const NoteTag *describe(CheckerContext &C, + StringRef FunctionName) const override { + return errno_modeling::getNoteTagForStdSuccess(C, FunctionName); } }; - /// Set errno constraints if use of 'errno' is completely irrelevant to the - /// modeled function or modeling is not possible. - class NoErrnoConstraint : public ErrnoConstraintBase { + class ErrnoMustBeCheckedConstraint : public ErrnoConstraintBase { public: - NoErrnoConstraint() : ErrnoConstraintBase(errno_modeling::Irrelevant) {} - ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, const Summary &Summary, CheckerContext &C) const override { - return errno_modeling::setErrnoState(State, CheckState); + return errno_modeling::setErrnoStdMustBeChecked(State, C, + Call.getOriginExpr()); + } + + const NoteTag *describe(CheckerContext &C, + StringRef FunctionName) const override { + return errno_modeling::getNoteTagForStdMustBeChecked(C, FunctionName); } }; @@ -542,8 +541,8 @@ class StdLibraryFunctionsChecker StringRef getNote() const { return Note; } }; - using ArgTypes = std::vector<Optional<QualType>>; - using RetType = Optional<QualType>; + using ArgTypes = std::vector<std::optional<QualType>>; + using RetType = std::optional<QualType>; // A placeholder type, we use it whenever we do not care about the concrete // type in a Signature. @@ -565,7 +564,7 @@ class StdLibraryFunctionsChecker // Construct a signature from optional types. If any of the optional types // are not set then the signature will be invalid. Signature(ArgTypes ArgTys, RetType RetTy) { - for (Optional<QualType> Arg : ArgTys) { + for (std::optional<QualType> Arg : ArgTys) { if (!Arg) { Invalid = true; return; @@ -718,10 +717,10 @@ public: bool ShouldAssumeControlledEnvironment = false; private: - Optional<Summary> findFunctionSummary(const FunctionDecl *FD, - CheckerContext &C) const; - Optional<Summary> findFunctionSummary(const CallEvent &Call, - CheckerContext &C) const; + std::optional<Summary> findFunctionSummary(const FunctionDecl *FD, + CheckerContext &C) const; + std::optional<Summary> findFunctionSummary(const CallEvent &Call, + CheckerContext &C) const; void initFunctionSummaries(CheckerContext &C) const; @@ -746,9 +745,12 @@ private: // Highlight the range of the argument that was violated. R->addRange(Call.getArgSourceRange(VC->getArgNo())); - // Describe the argument constraint in a note. - R->addNote(VC->describe(C.getState(), Summary), R->getLocation(), - Call.getArgSourceRange(VC->getArgNo())); + // Describe the argument constraint violation in a note. + std::string Descr = VC->describe( + ValueConstraint::DescriptionKind::Violation, C.getState(), Summary); + // Capitalize the first letter b/c we want a full sentence. + Descr[0] = toupper(Descr[0]); + R->addNote(Descr, R->getLocation(), Call.getArgSourceRange(VC->getArgNo())); C.emitReport(std::move(R)); } @@ -758,10 +760,11 @@ private: /// Usually if a failure return value exists for function, that function /// needs different cases for success and failure with different errno /// constraints (and different return value constraints). - const NoErrnoConstraint ErrnoIrrelevant; - const SuccessErrnoConstraint ErrnoMustNotBeChecked; - const ZeroRelatedErrnoConstraint ErrnoNEZeroIrrelevant{ - clang::BinaryOperatorKind::BO_NE, errno_modeling::Irrelevant}; + const NoErrnoConstraint ErrnoUnchanged{}; + const ResetErrnoConstraint ErrnoIrrelevant{}; + const ErrnoMustBeCheckedConstraint ErrnoMustBeChecked{}; + const SuccessErrnoConstraint ErrnoMustNotBeChecked{}; + const FailureErrnoConstraint ErrnoNEZeroIrrelevant{}; }; int StdLibraryFunctionsChecker::ErrnoConstraintBase::Tag = 0; @@ -778,24 +781,26 @@ static BasicValueFactory &getBVF(ProgramStateRef State) { } std::string StdLibraryFunctionsChecker::NotNullConstraint::describe( - ProgramStateRef State, const Summary &Summary) const { + DescriptionKind DK, ProgramStateRef State, const Summary &Summary) const { SmallString<48> Result; - Result += "The "; + const auto Violation = ValueConstraint::DescriptionKind::Violation; + Result += "the "; Result += getArgDesc(ArgN); - Result += " should not be NULL"; + Result += DK == Violation ? " should not be NULL" : " is not NULL"; return Result.c_str(); } std::string StdLibraryFunctionsChecker::RangeConstraint::describe( - ProgramStateRef State, const Summary &Summary) const { + DescriptionKind DK, ProgramStateRef State, const Summary &Summary) const { BasicValueFactory &BVF = getBVF(State); QualType T = Summary.getArgType(getArgNo()); SmallString<48> Result; - Result += "The "; + const auto Violation = ValueConstraint::DescriptionKind::Violation; + Result += "the "; Result += getArgDesc(ArgN); - Result += " should be "; + Result += DK == Violation ? " should be " : " is "; // Range kind as a string. Kind == OutOfRange ? Result += "out of" : Result += "within"; @@ -827,16 +832,18 @@ StdLibraryFunctionsChecker::getArgDesc(StdLibraryFunctionsChecker::ArgNo ArgN) { SmallString<8> Result; Result += std::to_string(ArgN + 1); Result += llvm::getOrdinalSuffix(ArgN + 1); - Result += " arg"; + Result += " argument"; return Result; } std::string StdLibraryFunctionsChecker::BufferSizeConstraint::describe( - ProgramStateRef State, const Summary &Summary) const { + DescriptionKind DK, ProgramStateRef State, const Summary &Summary) const { SmallString<96> Result; - Result += "The size of the "; + const auto Violation = ValueConstraint::DescriptionKind::Violation; + Result += "the size of the "; Result += getArgDesc(ArgN); - Result += " should be equal to or less than the value of "; + Result += DK == Violation ? " should be " : " is "; + Result += "equal to or greater than the value of "; if (ConcreteSize) { ConcreteSize->toString(Result); } else if (SizeArgN) { @@ -962,7 +969,7 @@ ProgramStateRef StdLibraryFunctionsChecker::ComparisonConstraint::apply( void StdLibraryFunctionsChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { - Optional<Summary> FoundSummary = findFunctionSummary(Call, C); + std::optional<Summary> FoundSummary = findFunctionSummary(Call, C); if (!FoundSummary) return; @@ -970,31 +977,43 @@ void StdLibraryFunctionsChecker::checkPreCall(const CallEvent &Call, ProgramStateRef State = C.getState(); ProgramStateRef NewState = State; + ExplodedNode *NewNode = C.getPredecessor(); for (const ValueConstraintPtr &Constraint : Summary.getArgConstraints()) { ProgramStateRef SuccessSt = Constraint->apply(NewState, Call, Summary, C); ProgramStateRef FailureSt = Constraint->negate()->apply(NewState, Call, Summary, C); // The argument constraint is not satisfied. if (FailureSt && !SuccessSt) { - if (ExplodedNode *N = C.generateErrorNode(NewState)) + if (ExplodedNode *N = C.generateErrorNode(NewState, NewNode)) reportBug(Call, N, Constraint.get(), Summary, C); break; - } else { - // We will apply the constraint even if we cannot reason about the - // argument. This means both SuccessSt and FailureSt can be true. If we - // weren't applying the constraint that would mean that symbolic - // execution continues on a code whose behaviour is undefined. - assert(SuccessSt); - NewState = SuccessSt; + } + // We will apply the constraint even if we cannot reason about the + // argument. This means both SuccessSt and FailureSt can be true. If we + // weren't applying the constraint that would mean that symbolic + // execution continues on a code whose behaviour is undefined. + assert(SuccessSt); + NewState = SuccessSt; + if (NewState != State) { + SmallString<64> Msg; + Msg += "Assuming "; + Msg += Constraint->describe(ValueConstraint::DescriptionKind::Assumption, + NewState, Summary); + const auto ArgSVal = Call.getArgSVal(Constraint->getArgNo()); + NewNode = C.addTransition( + NewState, NewNode, + C.getNoteTag([Msg = std::move(Msg), ArgSVal]( + PathSensitiveBugReport &BR, llvm::raw_ostream &OS) { + if (BR.isInteresting(ArgSVal)) + OS << Msg; + })); } } - if (NewState && NewState != State) - C.addTransition(NewState); } void StdLibraryFunctionsChecker::checkPostCall(const CallEvent &Call, CheckerContext &C) const { - Optional<Summary> FoundSummary = findFunctionSummary(Call, C); + std::optional<Summary> FoundSummary = findFunctionSummary(Call, C); if (!FoundSummary) return; @@ -1017,13 +1036,10 @@ void StdLibraryFunctionsChecker::checkPostCall(const CallEvent &Call, if (NewState && NewState != State) { if (Case.getNote().empty()) { - std::string Note; + const NoteTag *NT = nullptr; if (const auto *D = dyn_cast_or_null<FunctionDecl>(Call.getDecl())) - Note = Case.getErrnoConstraint().describe(D->getNameAsString()); - if (Note.empty()) - C.addTransition(NewState); - else - C.addTransition(NewState, errno_modeling::getErrnoNoteTag(C, Note)); + NT = Case.getErrnoConstraint().describe(C, D->getNameAsString()); + C.addTransition(NewState, NT); } else { StringRef Note = Case.getNote(); const NoteTag *Tag = C.getNoteTag( @@ -1036,13 +1052,23 @@ void StdLibraryFunctionsChecker::checkPostCall(const CallEvent &Call, /*IsPrunable=*/true); C.addTransition(NewState, Tag); } + } else if (NewState == State) { + // It is possible that the function was evaluated in a checker callback + // where the state constraints are already applied, then no change happens + // here to the state (if the ErrnoConstraint did not change it either). + // If the evaluated function requires a NoteTag for errno change, it is + // added here. + if (const auto *D = dyn_cast_or_null<FunctionDecl>(Call.getDecl())) + if (const NoteTag *NT = + Case.getErrnoConstraint().describe(C, D->getNameAsString())) + C.addTransition(NewState, NT); } } } bool StdLibraryFunctionsChecker::evalCall(const CallEvent &Call, CheckerContext &C) const { - Optional<Summary> FoundSummary = findFunctionSummary(Call, C); + std::optional<Summary> FoundSummary = findFunctionSummary(Call, C); if (!FoundSummary) return false; @@ -1109,26 +1135,26 @@ bool StdLibraryFunctionsChecker::Signature::matches( return true; } -Optional<StdLibraryFunctionsChecker::Summary> +std::optional<StdLibraryFunctionsChecker::Summary> StdLibraryFunctionsChecker::findFunctionSummary(const FunctionDecl *FD, CheckerContext &C) const { if (!FD) - return None; + return std::nullopt; initFunctionSummaries(C); auto FSMI = FunctionSummaryMap.find(FD->getCanonicalDecl()); if (FSMI == FunctionSummaryMap.end()) - return None; + return std::nullopt; return FSMI->second; } -Optional<StdLibraryFunctionsChecker::Summary> +std::optional<StdLibraryFunctionsChecker::Summary> StdLibraryFunctionsChecker::findFunctionSummary(const CallEvent &Call, CheckerContext &C) const { const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); if (!FD) - return None; + return std::nullopt; return findFunctionSummary(FD, C); } @@ -1149,11 +1175,11 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( LookupType(const ASTContext &ACtx) : ACtx(ACtx) {} // Find the type. If not found then the optional is not set. - llvm::Optional<QualType> operator()(StringRef Name) { + std::optional<QualType> operator()(StringRef Name) { IdentifierInfo &II = ACtx.Idents.get(Name); auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(&II); if (LookupRes.empty()) - return None; + return std::nullopt; // Prioritze typedef declarations. // This is needed in case of C struct typedefs. E.g.: @@ -1171,7 +1197,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( for (Decl *D : LookupRes) if (auto *TD = dyn_cast<TypeDecl>(D)) return ACtx.getTypeDeclType(TD).getCanonicalType(); - return None; + return std::nullopt; } } lookupTy(ACtx); @@ -1185,10 +1211,10 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( QualType operator()(QualType Ty) { return ACtx.getLangOpts().C99 ? ACtx.getRestrictType(Ty) : Ty; } - Optional<QualType> operator()(Optional<QualType> Ty) { + std::optional<QualType> operator()(std::optional<QualType> Ty) { if (Ty) return operator()(*Ty); - return None; + return std::nullopt; } } getRestrictTy(ACtx); class GetPointerTy { @@ -1197,16 +1223,16 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( public: GetPointerTy(const ASTContext &ACtx) : ACtx(ACtx) {} QualType operator()(QualType Ty) { return ACtx.getPointerType(Ty); } - Optional<QualType> operator()(Optional<QualType> Ty) { + std::optional<QualType> operator()(std::optional<QualType> Ty) { if (Ty) return operator()(*Ty); - return None; + return std::nullopt; } } getPointerTy(ACtx); class { public: - Optional<QualType> operator()(Optional<QualType> Ty) { - return Ty ? Optional<QualType>(Ty->withConst()) : None; + std::optional<QualType> operator()(std::optional<QualType> Ty) { + return Ty ? std::optional<QualType>(Ty->withConst()) : std::nullopt; } QualType operator()(QualType Ty) { return Ty.withConst(); } } getConstTy; @@ -1215,14 +1241,14 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( public: GetMaxValue(BasicValueFactory &BVF) : BVF(BVF) {} - Optional<RangeInt> operator()(QualType Ty) { + std::optional<RangeInt> operator()(QualType Ty) { return BVF.getMaxValue(Ty).getLimitedValue(); } - Optional<RangeInt> operator()(Optional<QualType> Ty) { + std::optional<RangeInt> operator()(std::optional<QualType> Ty) { if (Ty) { return operator()(*Ty); } - return None; + return std::nullopt; } } getMaxValue(BVF); @@ -1278,7 +1304,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( // The platform dependent value of EOF. // Try our best to parse this from the Preprocessor, otherwise fallback to -1. const auto EOFv = [&C]() -> RangeInt { - if (const llvm::Optional<int> OptInt = + if (const std::optional<int> OptInt = tryExpandAsInteger("EOF", C.getPreprocessor())) return *OptInt; return -1; @@ -1351,13 +1377,13 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( auto operator()(RangeInt b, RangeInt e) { return IntRangeVector{std::pair<RangeInt, RangeInt>{b, e}}; } - auto operator()(RangeInt b, Optional<RangeInt> e) { + auto operator()(RangeInt b, std::optional<RangeInt> e) { if (e) return IntRangeVector{std::pair<RangeInt, RangeInt>{b, *e}}; return IntRangeVector{}; } auto operator()(std::pair<RangeInt, RangeInt> i0, - std::pair<RangeInt, Optional<RangeInt>> i1) { + std::pair<RangeInt, std::optional<RangeInt>> i1) { if (i1.second) return IntRangeVector{i0, {i1.first, *(i1.second)}}; return IntRangeVector{i0}; @@ -1370,10 +1396,18 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( auto NotNull = [&](ArgNo ArgN) { return std::make_shared<NotNullConstraint>(ArgN); }; + auto IsNull = [&](ArgNo ArgN) { + return std::make_shared<NotNullConstraint>(ArgN, false); + }; - Optional<QualType> FileTy = lookupTy("FILE"); - Optional<QualType> FilePtrTy = getPointerTy(FileTy); - Optional<QualType> FilePtrRestrictTy = getRestrictTy(FilePtrTy); + std::optional<QualType> FileTy = lookupTy("FILE"); + std::optional<QualType> FilePtrTy = getPointerTy(FileTy); + std::optional<QualType> FilePtrRestrictTy = getRestrictTy(FilePtrTy); + + std::optional<QualType> FPosTTy = lookupTy("fpos_t"); + std::optional<QualType> FPosTPtrTy = getPointerTy(FPosTTy); + std::optional<QualType> ConstFPosTPtrTy = getPointerTy(getConstTy(FPosTTy)); + std::optional<QualType> FPosTPtrRestrictTy = getRestrictTy(FPosTPtrTy); // We are finally ready to define specifications for all supported functions. // @@ -1599,11 +1633,23 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( // read()-like functions that never return more than buffer size. auto FreadSummary = Summary(NoEvalCall) - .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)), + .Case({ArgumentCondition(1U, WithinRange, Range(1, SizeMax)), + ArgumentCondition(2U, WithinRange, Range(1, SizeMax)), + ReturnValueCondition(BO_LT, ArgNo(2)), ReturnValueCondition(WithinRange, Range(0, SizeMax))}, - ErrnoIrrelevant) + ErrnoNEZeroIrrelevant) + .Case({ArgumentCondition(1U, WithinRange, Range(1, SizeMax)), + ReturnValueCondition(BO_EQ, ArgNo(2)), + ReturnValueCondition(WithinRange, Range(0, SizeMax))}, + ErrnoMustNotBeChecked) + .Case({ArgumentCondition(1U, WithinRange, SingleValue(0)), + ReturnValueCondition(WithinRange, SingleValue(0))}, + ErrnoMustNotBeChecked) .ArgConstraint(NotNull(ArgNo(0))) .ArgConstraint(NotNull(ArgNo(3))) + // FIXME: It should be allowed to have a null buffer if any of + // args 1 or 2 are zero. Remove NotNull check of arg 0, add a check + // for non-null buffer if non-zero size to BufferSizeConstraint? .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(0), /*BufSize=*/ArgNo(1), /*BufSizeMultiplier=*/ArgNo(2))); @@ -1622,8 +1668,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( RetType{SizeTy}), FreadSummary); - Optional<QualType> Ssize_tTy = lookupTy("ssize_t"); - Optional<RangeInt> Ssize_tMax = getMaxValue(Ssize_tTy); + std::optional<QualType> Ssize_tTy = lookupTy("ssize_t"); + std::optional<RangeInt> Ssize_tMax = getMaxValue(Ssize_tTy); auto ReadSummary = Summary(NoEvalCall) @@ -1689,6 +1735,142 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( } if (ModelPOSIX) { + const auto ReturnsZeroOrMinusOne = + ConstraintSet{ReturnValueCondition(WithinRange, Range(-1, 0))}; + const auto ReturnsZero = + ConstraintSet{ReturnValueCondition(WithinRange, SingleValue(0))}; + const auto ReturnsMinusOne = + ConstraintSet{ReturnValueCondition(WithinRange, SingleValue(-1))}; + const auto ReturnsNonnegative = + ConstraintSet{ReturnValueCondition(WithinRange, Range(0, IntMax))}; + const auto ReturnsNonZero = + ConstraintSet{ReturnValueCondition(OutOfRange, SingleValue(0))}; + const auto ReturnsFileDescriptor = + ConstraintSet{ReturnValueCondition(WithinRange, Range(-1, IntMax))}; + const auto &ReturnsValidFileDescriptor = ReturnsNonnegative; + + // FILE *fopen(const char *restrict pathname, const char *restrict mode); + addToFunctionSummaryMap( + "fopen", + Signature(ArgTypes{ConstCharPtrRestrictTy, ConstCharPtrRestrictTy}, + RetType{FilePtrTy}), + Summary(NoEvalCall) + .Case({NotNull(Ret)}, ErrnoMustNotBeChecked) + .Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant) + .ArgConstraint(NotNull(ArgNo(0))) + .ArgConstraint(NotNull(ArgNo(1)))); + + // FILE *tmpfile(void); + addToFunctionSummaryMap("tmpfile", + Signature(ArgTypes{}, RetType{FilePtrTy}), + Summary(NoEvalCall) + .Case({NotNull(Ret)}, ErrnoMustNotBeChecked) + .Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant)); + + // FILE *freopen(const char *restrict pathname, const char *restrict mode, + // FILE *restrict stream); + addToFunctionSummaryMap( + "freopen", + Signature(ArgTypes{ConstCharPtrRestrictTy, ConstCharPtrRestrictTy, + FilePtrRestrictTy}, + RetType{FilePtrTy}), + Summary(NoEvalCall) + .Case({ReturnValueCondition(BO_EQ, ArgNo(2))}, + ErrnoMustNotBeChecked) + .Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant) + .ArgConstraint(NotNull(ArgNo(1))) + .ArgConstraint(NotNull(ArgNo(2)))); + + // int fclose(FILE *stream); + addToFunctionSummaryMap( + "fclose", Signature(ArgTypes{FilePtrTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZero, ErrnoMustNotBeChecked) + .Case({ReturnValueCondition(WithinRange, SingleValue(EOFv))}, + ErrnoNEZeroIrrelevant) + .ArgConstraint(NotNull(ArgNo(0)))); + + // int fseek(FILE *stream, long offset, int whence); + // FIXME: It can be possible to get the 'SEEK_' values (like EOFv) and use + // these for condition of arg 2. + // Now the range [0,2] is used (the `SEEK_*` constants are usually 0,1,2). + addToFunctionSummaryMap( + "fseek", Signature(ArgTypes{FilePtrTy, LongTy, IntTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZero, ErrnoMustNotBeChecked) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant) + .ArgConstraint(NotNull(ArgNo(0))) + .ArgConstraint(ArgumentCondition(2, WithinRange, {{0, 2}}))); + + // int fgetpos(FILE *restrict stream, fpos_t *restrict pos); + // From 'The Open Group Base Specifications Issue 7, 2018 edition': + // "The fgetpos() function shall not change the setting of errno if + // successful." + addToFunctionSummaryMap( + "fgetpos", + Signature(ArgTypes{FilePtrRestrictTy, FPosTPtrRestrictTy}, + RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZero, ErrnoUnchanged) + .Case(ReturnsNonZero, ErrnoNEZeroIrrelevant) + .ArgConstraint(NotNull(ArgNo(0))) + .ArgConstraint(NotNull(ArgNo(1)))); + + // int fsetpos(FILE *stream, const fpos_t *pos); + // From 'The Open Group Base Specifications Issue 7, 2018 edition': + // "The fsetpos() function shall not change the setting of errno if + // successful." + addToFunctionSummaryMap( + "fsetpos", + Signature(ArgTypes{FilePtrTy, ConstFPosTPtrTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZero, ErrnoUnchanged) + .Case(ReturnsNonZero, ErrnoNEZeroIrrelevant) + .ArgConstraint(NotNull(ArgNo(0))) + .ArgConstraint(NotNull(ArgNo(1)))); + + // long ftell(FILE *stream); + // From 'The Open Group Base Specifications Issue 7, 2018 edition': + // "The ftell() function shall not change the setting of errno if + // successful." + addToFunctionSummaryMap( + "ftell", Signature(ArgTypes{FilePtrTy}, RetType{LongTy}), + Summary(NoEvalCall) + .Case({ReturnValueCondition(WithinRange, Range(1, LongMax))}, + ErrnoUnchanged) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant) + .ArgConstraint(NotNull(ArgNo(0)))); + + // int fileno(FILE *stream); + addToFunctionSummaryMap( + "fileno", Signature(ArgTypes{FilePtrTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsValidFileDescriptor, ErrnoMustNotBeChecked) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant) + .ArgConstraint(NotNull(ArgNo(0)))); + + // void rewind(FILE *stream); + // This function indicates error only by setting of 'errno'. + addToFunctionSummaryMap("rewind", + Signature(ArgTypes{FilePtrTy}, RetType{VoidTy}), + Summary(NoEvalCall) + .Case({}, ErrnoMustBeChecked) + .ArgConstraint(NotNull(ArgNo(0)))); + + // void clearerr(FILE *stream); + addToFunctionSummaryMap( + "clearerr", Signature(ArgTypes{FilePtrTy}, RetType{VoidTy}), + Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); + + // int feof(FILE *stream); + addToFunctionSummaryMap( + "feof", Signature(ArgTypes{FilePtrTy}, RetType{IntTy}), + Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); + + // int ferror(FILE *stream); + addToFunctionSummaryMap( + "ferror", Signature(ArgTypes{FilePtrTy}, RetType{IntTy}), + Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); // long a64l(const char *str64); addToFunctionSummaryMap( @@ -1702,18 +1884,6 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .ArgConstraint(ArgumentCondition( 0, WithinRange, Range(0, LongMax)))); - const auto ReturnsZeroOrMinusOne = - ConstraintSet{ReturnValueCondition(WithinRange, Range(-1, 0))}; - const auto ReturnsZero = - ConstraintSet{ReturnValueCondition(WithinRange, SingleValue(0))}; - const auto ReturnsMinusOne = - ConstraintSet{ReturnValueCondition(WithinRange, SingleValue(-1))}; - const auto ReturnsNonnegative = - ConstraintSet{ReturnValueCondition(WithinRange, Range(0, IntMax))}; - const auto ReturnsFileDescriptor = - ConstraintSet{ReturnValueCondition(WithinRange, Range(-1, IntMax))}; - const auto &ReturnsValidFileDescriptor = ReturnsNonnegative; - // int access(const char *pathname, int amode); addToFunctionSummaryMap( "access", Signature(ArgTypes{ConstCharPtrTy, IntTy}, RetType{IntTy}), @@ -1777,7 +1947,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .ArgConstraint(ArgumentCondition( 0, WithinRange, Range(0, IntMax)))); - Optional<QualType> Off_tTy = lookupTy("off_t"); + std::optional<QualType> Off_tTy = lookupTy("off_t"); // int truncate(const char *path, off_t length); addToFunctionSummaryMap( @@ -1819,7 +1989,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .ArgConstraint( ArgumentCondition(0, WithinRange, Range(0, IntMax)))); - Optional<QualType> Mode_tTy = lookupTy("mode_t"); + std::optional<QualType> Mode_tTy = lookupTy("mode_t"); // int creat(const char *pathname, mode_t mode); addToFunctionSummaryMap( @@ -1836,8 +2006,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .ArgConstraint( ArgumentCondition(0, WithinRange, Range(0, UnsignedIntMax)))); - Optional<QualType> DirTy = lookupTy("DIR"); - Optional<QualType> DirPtrTy = getPointerTy(DirTy); + std::optional<QualType> DirTy = lookupTy("DIR"); + std::optional<QualType> DirPtrTy = getPointerTy(DirTy); // int dirfd(DIR *dirp); addToFunctionSummaryMap( @@ -1920,7 +2090,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant) .ArgConstraint(NotNull(ArgNo(1)))); - Optional<QualType> Dev_tTy = lookupTy("dev_t"); + std::optional<QualType> Dev_tTy = lookupTy("dev_t"); // int mknod(const char *pathname, mode_t mode, dev_t dev); addToFunctionSummaryMap( @@ -1969,8 +2139,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .ArgConstraint( ArgumentCondition(0, WithinRange, Range(0, IntMax)))); - Optional<QualType> Uid_tTy = lookupTy("uid_t"); - Optional<QualType> Gid_tTy = lookupTy("gid_t"); + std::optional<QualType> Uid_tTy = lookupTy("uid_t"); + std::optional<QualType> Gid_tTy = lookupTy("gid_t"); // int fchownat(int dirfd, const char *pathname, uid_t owner, gid_t group, // int flags); @@ -2069,9 +2239,10 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) .ArgConstraint(NotNull(ArgNo(1)))); - Optional<QualType> StructStatTy = lookupTy("stat"); - Optional<QualType> StructStatPtrTy = getPointerTy(StructStatTy); - Optional<QualType> StructStatPtrRestrictTy = getRestrictTy(StructStatPtrTy); + std::optional<QualType> StructStatTy = lookupTy("stat"); + std::optional<QualType> StructStatPtrTy = getPointerTy(StructStatTy); + std::optional<QualType> StructStatPtrRestrictTy = + getRestrictTy(StructStatPtrTy); // int fstat(int fd, struct stat *statbuf); addToFunctionSummaryMap( @@ -2200,14 +2371,6 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( "rand_r", Signature(ArgTypes{UnsignedIntPtrTy}, RetType{IntTy}), Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); - // int fileno(FILE *stream); - addToFunctionSummaryMap( - "fileno", Signature(ArgTypes{FilePtrTy}, RetType{IntTy}), - Summary(NoEvalCall) - .Case(ReturnsValidFileDescriptor, ErrnoMustNotBeChecked) - .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant) - .ArgConstraint(NotNull(ArgNo(0)))); - // int fseeko(FILE *stream, off_t offset, int whence); addToFunctionSummaryMap( "fseeko", @@ -2233,7 +2396,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .ArgConstraint( ArgumentCondition(4, WithinRange, Range(-1, IntMax)))); - Optional<QualType> Off64_tTy = lookupTy("off64_t"); + std::optional<QualType> Off64_tTy = lookupTy("off64_t"); // void *mmap64(void *addr, size_t length, int prot, int flags, int fd, // off64_t offset); // FIXME: Improve for errno modeling. @@ -2358,18 +2521,20 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .ArgConstraint(NotNull(ArgNo(1))) .ArgConstraint(NotNull(ArgNo(2)))); - Optional<QualType> StructSockaddrTy = lookupTy("sockaddr"); - Optional<QualType> StructSockaddrPtrTy = getPointerTy(StructSockaddrTy); - Optional<QualType> ConstStructSockaddrPtrTy = + std::optional<QualType> StructSockaddrTy = lookupTy("sockaddr"); + std::optional<QualType> StructSockaddrPtrTy = + getPointerTy(StructSockaddrTy); + std::optional<QualType> ConstStructSockaddrPtrTy = getPointerTy(getConstTy(StructSockaddrTy)); - Optional<QualType> StructSockaddrPtrRestrictTy = + std::optional<QualType> StructSockaddrPtrRestrictTy = getRestrictTy(StructSockaddrPtrTy); - Optional<QualType> ConstStructSockaddrPtrRestrictTy = + std::optional<QualType> ConstStructSockaddrPtrRestrictTy = getRestrictTy(ConstStructSockaddrPtrTy); - Optional<QualType> Socklen_tTy = lookupTy("socklen_t"); - Optional<QualType> Socklen_tPtrTy = getPointerTy(Socklen_tTy); - Optional<QualType> Socklen_tPtrRestrictTy = getRestrictTy(Socklen_tPtrTy); - Optional<RangeInt> Socklen_tMax = getMaxValue(Socklen_tTy); + std::optional<QualType> Socklen_tTy = lookupTy("socklen_t"); + std::optional<QualType> Socklen_tPtrTy = getPointerTy(Socklen_tTy); + std::optional<QualType> Socklen_tPtrRestrictTy = + getRestrictTy(Socklen_tPtrTy); + std::optional<RangeInt> Socklen_tMax = getMaxValue(Socklen_tTy); // In 'socket.h' of some libc implementations with C99, sockaddr parameter // is a transparent union of the underlying sockaddr_ family of pointers @@ -2568,9 +2733,9 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1), /*BufSize=*/ArgNo(2)))); - Optional<QualType> StructMsghdrTy = lookupTy("msghdr"); - Optional<QualType> StructMsghdrPtrTy = getPointerTy(StructMsghdrTy); - Optional<QualType> ConstStructMsghdrPtrTy = + std::optional<QualType> StructMsghdrTy = lookupTy("msghdr"); + std::optional<QualType> StructMsghdrPtrTy = getPointerTy(StructMsghdrTy); + std::optional<QualType> ConstStructMsghdrPtrTy = getPointerTy(getConstTy(StructMsghdrTy)); // ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags); @@ -2676,8 +2841,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .ArgConstraint( ArgumentCondition(5, WithinRange, Range(0, Socklen_tMax)))); - Optional<QualType> StructUtimbufTy = lookupTy("utimbuf"); - Optional<QualType> StructUtimbufPtrTy = getPointerTy(StructUtimbufTy); + std::optional<QualType> StructUtimbufTy = lookupTy("utimbuf"); + std::optional<QualType> StructUtimbufPtrTy = getPointerTy(StructUtimbufTy); // int utime(const char *filename, struct utimbuf *buf); addToFunctionSummaryMap( @@ -2688,9 +2853,10 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant) .ArgConstraint(NotNull(ArgNo(0)))); - Optional<QualType> StructTimespecTy = lookupTy("timespec"); - Optional<QualType> StructTimespecPtrTy = getPointerTy(StructTimespecTy); - Optional<QualType> ConstStructTimespecPtrTy = + std::optional<QualType> StructTimespecTy = lookupTy("timespec"); + std::optional<QualType> StructTimespecPtrTy = + getPointerTy(StructTimespecTy); + std::optional<QualType> ConstStructTimespecPtrTy = getPointerTy(getConstTy(StructTimespecTy)); // int futimens(int fd, const struct timespec times[2]); @@ -2714,8 +2880,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant) .ArgConstraint(NotNull(ArgNo(1)))); - Optional<QualType> StructTimevalTy = lookupTy("timeval"); - Optional<QualType> ConstStructTimevalPtrTy = + std::optional<QualType> StructTimevalTy = lookupTy("timeval"); + std::optional<QualType> ConstStructTimevalPtrTy = getPointerTy(getConstTy(StructTimevalTy)); // int utimes(const char *filename, const struct timeval times[2]); @@ -2738,17 +2904,19 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant) .ArgConstraint(NotNull(ArgNo(0)))); - Optional<QualType> Time_tTy = lookupTy("time_t"); - Optional<QualType> ConstTime_tPtrTy = getPointerTy(getConstTy(Time_tTy)); - Optional<QualType> ConstTime_tPtrRestrictTy = + std::optional<QualType> Time_tTy = lookupTy("time_t"); + std::optional<QualType> ConstTime_tPtrTy = + getPointerTy(getConstTy(Time_tTy)); + std::optional<QualType> ConstTime_tPtrRestrictTy = getRestrictTy(ConstTime_tPtrTy); - Optional<QualType> StructTmTy = lookupTy("tm"); - Optional<QualType> StructTmPtrTy = getPointerTy(StructTmTy); - Optional<QualType> StructTmPtrRestrictTy = getRestrictTy(StructTmPtrTy); - Optional<QualType> ConstStructTmPtrTy = + std::optional<QualType> StructTmTy = lookupTy("tm"); + std::optional<QualType> StructTmPtrTy = getPointerTy(StructTmTy); + std::optional<QualType> StructTmPtrRestrictTy = + getRestrictTy(StructTmPtrTy); + std::optional<QualType> ConstStructTmPtrTy = getPointerTy(getConstTy(StructTmTy)); - Optional<QualType> ConstStructTmPtrRestrictTy = + std::optional<QualType> ConstStructTmPtrRestrictTy = getRestrictTy(ConstStructTmPtrTy); // struct tm * localtime(const time_t *tp); @@ -2804,7 +2972,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( "gmtime", Signature(ArgTypes{ConstTime_tPtrTy}, RetType{StructTmPtrTy}), Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); - Optional<QualType> Clockid_tTy = lookupTy("clockid_t"); + std::optional<QualType> Clockid_tTy = lookupTy("clockid_t"); // int clock_gettime(clockid_t clock_id, struct timespec *tp); addToFunctionSummaryMap( @@ -2815,8 +2983,9 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant) .ArgConstraint(NotNull(ArgNo(1)))); - Optional<QualType> StructItimervalTy = lookupTy("itimerval"); - Optional<QualType> StructItimervalPtrTy = getPointerTy(StructItimervalTy); + std::optional<QualType> StructItimervalTy = lookupTy("itimerval"); + std::optional<QualType> StructItimervalPtrTy = + getPointerTy(StructItimervalTy); // int getitimer(int which, struct itimerval *curr_value); addToFunctionSummaryMap( @@ -2827,25 +2996,30 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant) .ArgConstraint(NotNull(ArgNo(1)))); - Optional<QualType> Pthread_cond_tTy = lookupTy("pthread_cond_t"); - Optional<QualType> Pthread_cond_tPtrTy = getPointerTy(Pthread_cond_tTy); - Optional<QualType> Pthread_tTy = lookupTy("pthread_t"); - Optional<QualType> Pthread_tPtrTy = getPointerTy(Pthread_tTy); - Optional<QualType> Pthread_tPtrRestrictTy = getRestrictTy(Pthread_tPtrTy); - Optional<QualType> Pthread_mutex_tTy = lookupTy("pthread_mutex_t"); - Optional<QualType> Pthread_mutex_tPtrTy = getPointerTy(Pthread_mutex_tTy); - Optional<QualType> Pthread_mutex_tPtrRestrictTy = + std::optional<QualType> Pthread_cond_tTy = lookupTy("pthread_cond_t"); + std::optional<QualType> Pthread_cond_tPtrTy = + getPointerTy(Pthread_cond_tTy); + std::optional<QualType> Pthread_tTy = lookupTy("pthread_t"); + std::optional<QualType> Pthread_tPtrTy = getPointerTy(Pthread_tTy); + std::optional<QualType> Pthread_tPtrRestrictTy = + getRestrictTy(Pthread_tPtrTy); + std::optional<QualType> Pthread_mutex_tTy = lookupTy("pthread_mutex_t"); + std::optional<QualType> Pthread_mutex_tPtrTy = + getPointerTy(Pthread_mutex_tTy); + std::optional<QualType> Pthread_mutex_tPtrRestrictTy = getRestrictTy(Pthread_mutex_tPtrTy); - Optional<QualType> Pthread_attr_tTy = lookupTy("pthread_attr_t"); - Optional<QualType> Pthread_attr_tPtrTy = getPointerTy(Pthread_attr_tTy); - Optional<QualType> ConstPthread_attr_tPtrTy = + std::optional<QualType> Pthread_attr_tTy = lookupTy("pthread_attr_t"); + std::optional<QualType> Pthread_attr_tPtrTy = + getPointerTy(Pthread_attr_tTy); + std::optional<QualType> ConstPthread_attr_tPtrTy = getPointerTy(getConstTy(Pthread_attr_tTy)); - Optional<QualType> ConstPthread_attr_tPtrRestrictTy = + std::optional<QualType> ConstPthread_attr_tPtrRestrictTy = getRestrictTy(ConstPthread_attr_tPtrTy); - Optional<QualType> Pthread_mutexattr_tTy = lookupTy("pthread_mutexattr_t"); - Optional<QualType> ConstPthread_mutexattr_tPtrTy = + std::optional<QualType> Pthread_mutexattr_tTy = + lookupTy("pthread_mutexattr_t"); + std::optional<QualType> ConstPthread_mutexattr_tPtrTy = getPointerTy(getConstTy(Pthread_mutexattr_tTy)); - Optional<QualType> ConstPthread_mutexattr_tPtrRestrictTy = + std::optional<QualType> ConstPthread_mutexattr_tPtrRestrictTy = getRestrictTy(ConstPthread_mutexattr_tPtrTy); QualType PthreadStartRoutineTy = getPointerTy( @@ -2929,6 +3103,10 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( // Test range values. addToFunctionSummaryMap( + "__single_val_0", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition(0U, WithinRange, SingleValue(0)))); + addToFunctionSummaryMap( "__single_val_1", Signature(ArgTypes{IntTy}, RetType{IntTy}), Summary(EvalCallAsPure) .ArgConstraint(ArgumentCondition(0U, WithinRange, SingleValue(1)))); @@ -2996,6 +3174,15 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( "__test_restrict_param_2"}, Signature(ArgTypes{VoidPtrRestrictTy}, RetType{VoidTy}), Summary(EvalCallAsPure)); + + // Test the application of cases. + addToFunctionSummaryMap( + "__test_case_note", Signature(ArgTypes{}, RetType{IntTy}), + Summary(EvalCallAsPure) + .Case({ReturnValueCondition(WithinRange, SingleValue(0))}, + ErrnoIrrelevant, "Function returns 0") + .Case({ReturnValueCondition(WithinRange, SingleValue(1))}, + ErrnoIrrelevant, "Function returns 1")); } SummariesInitialized = true; diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 1aa665f0ef45..3f61dd823940 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -17,10 +17,12 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" #include <functional> +#include <optional> using namespace clang; using namespace ento; @@ -85,10 +87,10 @@ const StreamErrorState ErrorFError{false, false, true}; /// Full state information about a stream pointer. struct StreamState { /// The last file operation called in the stream. + /// Can be nullptr. const FnDescription *LastOperation; /// State of a stream symbol. - /// FIXME: We need maybe an "escaped" state later. enum KindTy { Opened, /// Stream is opened. Closed, /// Closed stream (an invalid stream pointer after it was closed). @@ -202,13 +204,12 @@ ProgramStateRef bindAndAssumeTrue(ProgramStateRef State, CheckerContext &C, ProgramStateRef bindInt(uint64_t Value, ProgramStateRef State, CheckerContext &C, const CallExpr *CE) { State = State->BindExpr(CE, C.getLocationContext(), - C.getSValBuilder().makeIntVal(Value, false)); + C.getSValBuilder().makeIntVal(Value, CE->getType())); return State; } class StreamChecker : public Checker<check::PreCall, eval::Call, check::DeadSymbols, check::PointerEscape> { - BugType BT_FileNull{this, "NULL stream pointer", "Stream handling error"}; BugType BT_UseAfterClose{this, "Closed stream", "Stream handling error"}; BugType BT_UseAfterOpenFailed{this, "Invalid stream", "Stream handling error"}; @@ -236,48 +237,55 @@ public: private: CallDescriptionMap<FnDescription> FnDescriptions = { - {{"fopen"}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, - {{"freopen", 3}, + {{{"fopen"}}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, + {{{"freopen"}, 3}, {&StreamChecker::preFreopen, &StreamChecker::evalFreopen, 2}}, - {{"tmpfile"}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, - {{"fclose", 1}, + {{{"tmpfile"}}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, + {{{"fclose"}, 1}, {&StreamChecker::preDefault, &StreamChecker::evalFclose, 0}}, - {{"fread", 4}, + {{{"fread"}, 4}, {&StreamChecker::preFread, std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, true), 3}}, - {{"fwrite", 4}, + {{{"fwrite"}, 4}, {&StreamChecker::preFwrite, std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, false), 3}}, - {{"fseek", 3}, {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}}, - {{"ftell", 1}, {&StreamChecker::preDefault, nullptr, 0}}, - {{"rewind", 1}, {&StreamChecker::preDefault, nullptr, 0}}, - {{"fgetpos", 2}, {&StreamChecker::preDefault, nullptr, 0}}, - {{"fsetpos", 2}, {&StreamChecker::preDefault, nullptr, 0}}, - {{"clearerr", 1}, + {{{"fseek"}, 3}, + {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}}, + {{{"ftell"}, 1}, + {&StreamChecker::preDefault, &StreamChecker::evalFtell, 0}}, + {{{"rewind"}, 1}, + {&StreamChecker::preDefault, &StreamChecker::evalRewind, 0}}, + {{{"fgetpos"}, 2}, + {&StreamChecker::preDefault, &StreamChecker::evalFgetpos, 0}}, + {{{"fsetpos"}, 2}, + {&StreamChecker::preDefault, &StreamChecker::evalFsetpos, 0}}, + {{{"clearerr"}, 1}, {&StreamChecker::preDefault, &StreamChecker::evalClearerr, 0}}, - {{"feof", 1}, + {{{"feof"}, 1}, {&StreamChecker::preDefault, std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFEof), 0}}, - {{"ferror", 1}, + {{{"ferror"}, 1}, {&StreamChecker::preDefault, std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFError), 0}}, - {{"fileno", 1}, {&StreamChecker::preDefault, nullptr, 0}}, + {{{"fileno"}, 1}, {&StreamChecker::preDefault, nullptr, 0}}, }; CallDescriptionMap<FnDescription> FnTestDescriptions = { - {{"StreamTesterChecker_make_feof_stream", 1}, + {{{"StreamTesterChecker_make_feof_stream"}, 1}, {nullptr, std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, ErrorFEof), 0}}, - {{"StreamTesterChecker_make_ferror_stream", 1}, + {{{"StreamTesterChecker_make_ferror_stream"}, 1}, {nullptr, std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, ErrorFError), 0}}, }; + mutable std::optional<int> EofVal; + void evalFopen(const FnDescription *Desc, const CallEvent &Call, CheckerContext &C) const; @@ -303,6 +311,18 @@ private: void evalFseek(const FnDescription *Desc, const CallEvent &Call, CheckerContext &C) const; + void evalFgetpos(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C) const; + + void evalFsetpos(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C) const; + + void evalFtell(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C) const; + + void evalRewind(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C) const; + void preDefault(const FnDescription *Desc, const CallEvent &Call, CheckerContext &C) const; @@ -318,7 +338,7 @@ private: const StreamErrorState &ErrorKind) const; /// Check that the stream (in StreamVal) is not NULL. - /// If it can only be NULL a fatal error is emitted and nullptr returned. + /// If it can only be NULL a sink node is generated and nullptr returned. /// Otherwise the return value is a new state where the stream is constrained /// to be non-null. ProgramStateRef ensureStreamNonNull(SVal StreamVal, const Expr *StreamE, @@ -368,7 +388,7 @@ private: // (and matching name) as stream functions. if (!Call.isGlobalCFunction()) return nullptr; - for (auto P : Call.parameters()) { + for (auto *P : Call.parameters()) { QualType T = P->getType(); if (!T->isIntegralOrEnumerationType() && !T->isPointerType()) return nullptr; @@ -411,6 +431,17 @@ private: }); } + void initEof(CheckerContext &C) const { + if (EofVal) + return; + + if (const std::optional<int> OptInt = + tryExpandAsInteger("EOF", C.getPreprocessor())) + EofVal = *OptInt; + else + EofVal = -1; + } + /// Searches for the ExplodedNode where the file descriptor was acquired for /// StreamSym. static const ExplodedNode *getAcquisitionSite(const ExplodedNode *N, @@ -426,8 +457,7 @@ private: REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState) inline void assertStreamStateOpened(const StreamState *SS) { - assert(SS->isOpened() && - "Previous create of error node for non-opened stream failed?"); + assert(SS->isOpened() && "Stream is expected to be opened"); } const ExplodedNode *StreamChecker::getAcquisitionSite(const ExplodedNode *N, @@ -457,6 +487,8 @@ const ExplodedNode *StreamChecker::getAcquisitionSite(const ExplodedNode *N, void StreamChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { + initEof(C); + const FnDescription *Desc = lookupFn(Call); if (!Desc || !Desc->PreFn) return; @@ -526,7 +558,7 @@ void StreamChecker::evalFreopen(const FnDescription *Desc, if (!CE) return; - Optional<DefinedSVal> StreamVal = + std::optional<DefinedSVal> StreamVal = getStreamArg(Desc, Call).getAs<DefinedSVal>(); if (!StreamVal) return; @@ -574,6 +606,10 @@ void StreamChecker::evalFclose(const FnDescription *Desc, const CallEvent &Call, if (!SS) return; + auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); + if (!CE) + return; + assertStreamStateOpened(SS); // Close the File Descriptor. @@ -581,7 +617,16 @@ void StreamChecker::evalFclose(const FnDescription *Desc, const CallEvent &Call, // and can not be used any more. State = State->set<StreamMap>(Sym, StreamState::getClosed(Desc)); - C.addTransition(State); + // Return 0 on success, EOF on failure. + SValBuilder &SVB = C.getSValBuilder(); + ProgramStateRef StateSuccess = State->BindExpr( + CE, C.getLocationContext(), SVB.makeIntVal(0, C.getASTContext().IntTy)); + ProgramStateRef StateFailure = + State->BindExpr(CE, C.getLocationContext(), + SVB.makeIntVal(*EofVal, C.getASTContext().IntTy)); + + C.addTransition(StateSuccess); + C.addTransition(StateFailure); } void StreamChecker::preFread(const FnDescription *Desc, const CallEvent &Call, @@ -639,10 +684,10 @@ void StreamChecker::evalFreadFwrite(const FnDescription *Desc, if (!CE) return; - Optional<NonLoc> SizeVal = Call.getArgSVal(1).getAs<NonLoc>(); + std::optional<NonLoc> SizeVal = Call.getArgSVal(1).getAs<NonLoc>(); if (!SizeVal) return; - Optional<NonLoc> NMembVal = Call.getArgSVal(2).getAs<NonLoc>(); + std::optional<NonLoc> NMembVal = Call.getArgSVal(2).getAs<NonLoc>(); if (!NMembVal) return; @@ -766,6 +811,131 @@ void StreamChecker::evalFseek(const FnDescription *Desc, const CallEvent &Call, C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym)); } +void StreamChecker::evalFgetpos(const FnDescription *Desc, + const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol(); + if (!Sym) + return; + + // Do not evaluate if stream is not found. + if (!State->get<StreamMap>(Sym)) + return; + + auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); + if (!CE) + return; + + DefinedSVal RetVal = makeRetVal(C, CE); + State = State->BindExpr(CE, C.getLocationContext(), RetVal); + ProgramStateRef StateNotFailed, StateFailed; + std::tie(StateFailed, StateNotFailed) = + C.getConstraintManager().assumeDual(State, RetVal); + + // This function does not affect the stream state. + // Still we add success and failure state with the appropriate return value. + // StdLibraryFunctionsChecker can change these states (set the 'errno' state). + C.addTransition(StateNotFailed); + C.addTransition(StateFailed); +} + +void StreamChecker::evalFsetpos(const FnDescription *Desc, + const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); + if (!StreamSym) + return; + + const StreamState *SS = State->get<StreamMap>(StreamSym); + if (!SS) + return; + + auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); + if (!CE) + return; + + assertStreamStateOpened(SS); + + DefinedSVal RetVal = makeRetVal(C, CE); + State = State->BindExpr(CE, C.getLocationContext(), RetVal); + ProgramStateRef StateNotFailed, StateFailed; + std::tie(StateFailed, StateNotFailed) = + C.getConstraintManager().assumeDual(State, RetVal); + + StateNotFailed = StateNotFailed->set<StreamMap>( + StreamSym, StreamState::getOpened(Desc, ErrorNone, false)); + + // At failure ferror could be set. + // The standards do not tell what happens with the file position at failure. + // But we can assume that it is dangerous to make a next I/O operation after + // the position was not set correctly (similar to 'fseek'). + StateFailed = StateFailed->set<StreamMap>( + StreamSym, StreamState::getOpened(Desc, ErrorNone | ErrorFError, true)); + + C.addTransition(StateNotFailed); + C.addTransition(StateFailed); +} + +void StreamChecker::evalFtell(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol(); + if (!Sym) + return; + + if (!State->get<StreamMap>(Sym)) + return; + + auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); + if (!CE) + return; + + SValBuilder &SVB = C.getSValBuilder(); + NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>(); + ProgramStateRef StateNotFailed = + State->BindExpr(CE, C.getLocationContext(), RetVal); + auto Cond = SVB.evalBinOp(State, BO_GE, RetVal, + SVB.makeZeroVal(C.getASTContext().LongTy), + SVB.getConditionType()) + .getAs<DefinedOrUnknownSVal>(); + if (!Cond) + return; + StateNotFailed = StateNotFailed->assume(*Cond, true); + if (!StateNotFailed) + return; + + ProgramStateRef StateFailed = State->BindExpr( + CE, C.getLocationContext(), SVB.makeIntVal(-1, C.getASTContext().LongTy)); + + C.addTransition(StateNotFailed); + C.addTransition(StateFailed); +} + +void StreamChecker::evalRewind(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); + if (!StreamSym) + return; + + const StreamState *SS = State->get<StreamMap>(StreamSym); + if (!SS) + return; + + auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); + if (!CE) + return; + + assertStreamStateOpened(SS); + + State = State->set<StreamMap>(StreamSym, + StreamState::getOpened(Desc, ErrorNone, false)); + + C.addTransition(State); +} + void StreamChecker::evalClearerr(const FnDescription *Desc, const CallEvent &Call, CheckerContext &C) const { @@ -869,13 +1039,11 @@ StreamChecker::ensureStreamNonNull(SVal StreamVal, const Expr *StreamE, std::tie(StateNotNull, StateNull) = CM.assumeDual(C.getState(), *Stream); if (!StateNotNull && StateNull) { - if (ExplodedNode *N = C.generateErrorNode(StateNull)) { - auto R = std::make_unique<PathSensitiveBugReport>( - BT_FileNull, "Stream pointer might be NULL.", N); - if (StreamE) - bugreporter::trackExpressionValue(N, StreamE, *R); - C.emitReport(std::move(R)); - } + // Stream argument is NULL, stop analysis on this path. + // This case should occur only if StdLibraryFunctionsChecker (or ModelPOSIX + // option of it) is not turned on, otherwise that checker ensures non-null + // argument. + C.generateSink(StateNull, C.getPredecessor()); return nullptr; } @@ -976,7 +1144,8 @@ ProgramStateRef StreamChecker::ensureNoFilePositionIndeterminate( ProgramStateRef StreamChecker::ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C, ProgramStateRef State) const { - Optional<nonloc::ConcreteInt> CI = WhenceVal.getAs<nonloc::ConcreteInt>(); + std::optional<nonloc::ConcreteInt> CI = + WhenceVal.getAs<nonloc::ConcreteInt>(); if (!CI) return State; diff --git a/clang/lib/StaticAnalyzer/Checkers/Taint.cpp b/clang/lib/StaticAnalyzer/Checkers/Taint.cpp index 44162094e49a..a95c0e183284 100644 --- a/clang/lib/StaticAnalyzer/Checkers/Taint.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/Taint.cpp @@ -13,6 +13,7 @@ #include "clang/StaticAnalyzer/Checkers/Taint.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include <optional> using namespace clang; using namespace ento; @@ -63,7 +64,7 @@ ProgramStateRef taint::addTaint(ProgramStateRef State, SVal V, // their parent region, which is a conjured symbol default-bound to the base // region of the parent region. if (auto LCV = V.getAs<nonloc::LazyCompoundVal>()) { - if (Optional<SVal> binding = + if (std::optional<SVal> binding = State->getStateManager().getStoreManager().getDefaultBinding( *LCV)) { if (SymbolRef Sym = binding->getAsSymbol()) diff --git a/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp index eeec807ccee4..28fe11d5ed06 100644 --- a/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp @@ -17,6 +17,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "llvm/ADT/FoldingSet.h" +#include <optional> using namespace clang; using namespace ento; @@ -100,7 +101,7 @@ DivisionBRVisitor::VisitNode(const ExplodedNode *Succ, BugReporterContext &BRC, const Expr *E = nullptr; - if (Optional<PostStmt> P = Succ->getLocationAs<PostStmt>()) + if (std::optional<PostStmt> P = Succ->getLocationAs<PostStmt>()) if (const BinaryOperator *BO = P->getStmtAs<BinaryOperator>()) { BinaryOperator::Opcode Op = BO->getOpcode(); if (Op == BO_Div || Op == BO_Rem || Op == BO_DivAssign || @@ -132,7 +133,7 @@ DivisionBRVisitor::VisitNode(const ExplodedNode *Succ, BugReporterContext &BRC, } bool TestAfterDivZeroChecker::isZero(SVal S, CheckerContext &C) const { - Optional<DefinedSVal> DSV = S.getAs<DefinedSVal>(); + std::optional<DefinedSVal> DSV = S.getAs<DefinedSVal>(); if (!DSV) return false; diff --git a/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp index ebe5ad53cc30..b17b983f0345 100644 --- a/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp @@ -18,6 +18,7 @@ #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include <optional> #include <utility> using namespace clang; @@ -93,7 +94,7 @@ void UndefBranchChecker::checkBranchCondition(const Stmt *Condition, ProgramPoint P = PrevN->getLocation(); ProgramStateRef St = N->getState(); - if (Optional<PostStmt> PS = P.getAs<PostStmt>()) + if (std::optional<PostStmt> PS = P.getAs<PostStmt>()) if (PS->getStmt() == Ex) St = PrevN->getState(); diff --git a/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp index 816a547cadc3..27f3345e67ac 100644 --- a/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp @@ -19,6 +19,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" +#include <optional> using namespace clang; using namespace ento; @@ -69,8 +70,8 @@ UndefCapturedBlockVarChecker::checkPostStmt(const BlockExpr *BE, continue; // Get the VarRegion associated with VD in the local stack frame. - if (Optional<UndefinedVal> V = - state->getSVal(I.getOriginalRegion()).getAs<UndefinedVal>()) { + if (std::optional<UndefinedVal> V = + state->getSVal(I.getOriginalRegion()).getAs<UndefinedVal>()) { if (ExplodedNode *N = C.generateErrorNode()) { if (!BT) BT.reset( diff --git a/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp index 05f8f6084c0b..0fa3d6043971 100644 --- a/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp @@ -92,7 +92,7 @@ void UndefinedAssignmentChecker::checkBind(SVal location, SVal val, if (const auto *CD = dyn_cast<CXXConstructorDecl>(C.getStackFrame()->getDecl())) { if (CD->isImplicit()) { - for (auto I : CD->inits()) { + for (auto *I : CD->inits()) { if (I->getInit()->IgnoreImpCasts() == StoreE) { OS << "Value assigned to field '" << I->getMember()->getName() << "' in implicit constructor is garbage or undefined"; diff --git a/clang/lib/StaticAnalyzer/Checkers/UndefinedNewArraySizeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/UndefinedNewArraySizeChecker.cpp new file mode 100644 index 000000000000..f053ee887a1a --- /dev/null +++ b/clang/lib/StaticAnalyzer/Checkers/UndefinedNewArraySizeChecker.cpp @@ -0,0 +1,80 @@ +//===--- UndefinedNewArraySizeChecker.cpp -----------------------*- C++ -*--==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This defines UndefinedNewArraySizeChecker, a builtin check in ExprEngine +// that checks if the size of the array in a new[] expression is undefined. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace ento; + +namespace { +class UndefinedNewArraySizeChecker : public Checker<check::PreCall> { + +private: + BugType BT{this, "Undefined array element count in new[]", + categories::LogicError}; + +public: + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + void HandleUndefinedArrayElementCount(CheckerContext &C, SVal ArgVal, + const Expr *Init, + SourceRange Range) const; +}; +} // namespace + +void UndefinedNewArraySizeChecker::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + if (const auto *AC = dyn_cast<CXXAllocatorCall>(&Call)) { + if (!AC->isArray()) + return; + + auto *SizeEx = *AC->getArraySizeExpr(); + auto SizeVal = AC->getArraySizeVal(); + + if (SizeVal.isUndef()) + HandleUndefinedArrayElementCount(C, SizeVal, SizeEx, + SizeEx->getSourceRange()); + } +} + +void UndefinedNewArraySizeChecker::HandleUndefinedArrayElementCount( + CheckerContext &C, SVal ArgVal, const Expr *Init, SourceRange Range) const { + + if (ExplodedNode *N = C.generateErrorNode()) { + + SmallString<100> buf; + llvm::raw_svector_ostream os(buf); + + os << "Element count in new[] is a garbage value"; + + auto R = std::make_unique<PathSensitiveBugReport>(BT, os.str(), N); + R->markInteresting(ArgVal); + R->addRange(Range); + bugreporter::trackExpressionValue(N, Init, *R); + + C.emitReport(std::move(R)); + } +} + +void ento::registerUndefinedNewArraySizeChecker(CheckerManager &mgr) { + mgr.registerChecker<UndefinedNewArraySizeChecker>(); +} + +bool ento::shouldRegisterUndefinedNewArraySizeChecker( + const CheckerManager &mgr) { + return mgr.getLangOpts().CPlusPlus; +} diff --git a/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp b/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp index f5bd765ff679..54e1e0e11909 100644 --- a/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp @@ -19,6 +19,7 @@ #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h" +#include <optional> using namespace clang; using namespace clang::ento; @@ -121,9 +122,9 @@ struct DereferenceInfo { /// Dereferences \p FR and returns with the pointee's region, and whether it /// needs to be casted back to it's location type. If for whatever reason -/// dereferencing fails, returns with None. -static llvm::Optional<DereferenceInfo> dereference(ProgramStateRef State, - const FieldRegion *FR); +/// dereferencing fails, returns std::nullopt. +static std::optional<DereferenceInfo> dereference(ProgramStateRef State, + const FieldRegion *FR); /// Returns whether \p T can be (transitively) dereferenced to a void pointer /// type (void*, void**, ...). @@ -159,7 +160,7 @@ bool FindUninitializedFields::isDereferencableUninit( // At this point the pointer itself is initialized and points to a valid // location, we'll now check the pointee. - llvm::Optional<DereferenceInfo> DerefInfo = dereference(State, FR); + std::optional<DereferenceInfo> DerefInfo = dereference(State, FR); if (!DerefInfo) { IsAnyFieldInitialized = true; return false; @@ -217,8 +218,8 @@ bool FindUninitializedFields::isDereferencableUninit( // Utility functions. //===----------------------------------------------------------------------===// -static llvm::Optional<DereferenceInfo> dereference(ProgramStateRef State, - const FieldRegion *FR) { +static std::optional<DereferenceInfo> dereference(ProgramStateRef State, + const FieldRegion *FR) { llvm::SmallSet<const TypedValueRegion *, 5> VisitedRegions; @@ -234,7 +235,7 @@ static llvm::Optional<DereferenceInfo> dereference(ProgramStateRef State, // The region we'd like to acquire. const auto *R = V.getAsRegion()->getAs<TypedValueRegion>(); if (!R) - return None; + return std::nullopt; VisitedRegions.insert(R); @@ -245,7 +246,7 @@ static llvm::Optional<DereferenceInfo> dereference(ProgramStateRef State, R = Tmp->getAs<TypedValueRegion>(); if (!R) - return None; + return std::nullopt; // We found a cyclic pointer, like int *ptr = (int *)&ptr. if (!VisitedRegions.insert(R).second) diff --git a/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp index aa3f4524798a..f503b3e88bb3 100644 --- a/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp @@ -17,11 +17,11 @@ #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" -#include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/raw_ostream.h" +#include <optional> using namespace clang; using namespace ento; @@ -40,7 +40,7 @@ namespace { class UnixAPIMisuseChecker : public Checker< check::PreStmt<CallExpr> > { mutable std::unique_ptr<BugType> BT_open, BT_pthreadOnce; - mutable Optional<uint64_t> Val_O_CREAT; + mutable std::optional<uint64_t> Val_O_CREAT; public: void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; @@ -234,7 +234,7 @@ void UnixAPIMisuseChecker::CheckOpenVariant(CheckerContext &C, } NonLoc oflags = V.castAs<NonLoc>(); NonLoc ocreateFlag = C.getSValBuilder() - .makeIntVal(Val_O_CREAT.value(), oflagsEx->getType()) + .makeIntVal(*Val_O_CREAT, oflagsEx->getType()) .castAs<NonLoc>(); SVal maskedFlagsUC = C.getSValBuilder().evalBinOpNN(state, BO_And, oflags, ocreateFlag, diff --git a/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp index d231be64c2e1..3ad6858ead46 100644 --- a/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp @@ -24,6 +24,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" #include "llvm/ADT/SmallSet.h" +#include <optional> using namespace clang; using namespace ento; @@ -74,7 +75,7 @@ void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G, if (!PM) PM = &LC->getParentMap(); - if (Optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) { + if (std::optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) { const CFGBlock *CB = BE->getBlock(); reachable.insert(CB->getBlockID()); } @@ -129,7 +130,7 @@ void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G, bool foundUnreachable = false; for (CFGBlock::const_iterator ci = CB->begin(), ce = CB->end(); ci != ce; ++ci) { - if (Optional<CFGStmt> S = (*ci).getAs<CFGStmt>()) + if (std::optional<CFGStmt> S = (*ci).getAs<CFGStmt>()) if (const CallExpr *CE = dyn_cast<CallExpr>(S->getStmt())) { if (CE->getBuiltinCallee() == Builtin::BI__builtin_unreachable || CE->isBuiltinAssumeFalse(Eng.getContext())) { @@ -199,7 +200,7 @@ void UnreachableCodeChecker::FindUnreachableEntryPoints(const CFGBlock *CB, // Find the Stmt* in a CFGBlock for reporting a warning const Stmt *UnreachableCodeChecker::getUnreachableStmt(const CFGBlock *CB) { for (CFGBlock::const_iterator I = CB->begin(), E = CB->end(); I != E; ++I) { - if (Optional<CFGStmt> S = I->getAs<CFGStmt>()) { + if (std::optional<CFGStmt> S = I->getAs<CFGStmt>()) { if (!isa<DeclStmt>(S->getStmt())) return S->getStmt(); } diff --git a/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp index cf519b085892..fe910ce35302 100644 --- a/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp @@ -24,6 +24,7 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" +#include <optional> using namespace clang; using namespace ento; @@ -192,7 +193,7 @@ ProgramStateRef VLASizeChecker::checkVLAIndexSize(CheckerContext &C, DefinedOrUnknownSVal Zero = SVB.makeZeroVal(SizeTy); SVal LessThanZeroVal = SVB.evalBinOp(State, BO_LT, SizeD, Zero, SizeTy); - if (Optional<DefinedSVal> LessThanZeroDVal = + if (std::optional<DefinedSVal> LessThanZeroDVal = LessThanZeroVal.getAs<DefinedSVal>()) { ConstraintManager &CM = C.getConstraintManager(); ProgramStateRef StatePos, StateNeg; diff --git a/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp index fbefd5f9ffdc..2d1b873abf73 100644 --- a/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp @@ -100,27 +100,26 @@ private: }; const SmallVector<ValistChecker::VAListAccepter, 15> - ValistChecker::VAListAccepters = { - {{"vfprintf", 3}, 2}, - {{"vfscanf", 3}, 2}, - {{"vprintf", 2}, 1}, - {{"vscanf", 2}, 1}, - {{"vsnprintf", 4}, 3}, - {{"vsprintf", 3}, 2}, - {{"vsscanf", 3}, 2}, - {{"vfwprintf", 3}, 2}, - {{"vfwscanf", 3}, 2}, - {{"vwprintf", 2}, 1}, - {{"vwscanf", 2}, 1}, - {{"vswprintf", 4}, 3}, - // vswprintf is the wide version of vsnprintf, - // vsprintf has no wide version - {{"vswscanf", 3}, 2}}; - -const CallDescription - ValistChecker::VaStart("__builtin_va_start", /*Args=*/2, /*Params=*/1), - ValistChecker::VaCopy("__builtin_va_copy", 2), - ValistChecker::VaEnd("__builtin_va_end", 1); + ValistChecker::VAListAccepters = {{{{"vfprintf"}, 3}, 2}, + {{{"vfscanf"}, 3}, 2}, + {{{"vprintf"}, 2}, 1}, + {{{"vscanf"}, 2}, 1}, + {{{"vsnprintf"}, 4}, 3}, + {{{"vsprintf"}, 3}, 2}, + {{{"vsscanf"}, 3}, 2}, + {{{"vfwprintf"}, 3}, 2}, + {{{"vfwscanf"}, 3}, 2}, + {{{"vwprintf"}, 2}, 1}, + {{{"vwscanf"}, 2}, 1}, + {{{"vswprintf"}, 4}, 3}, + // vswprintf is the wide version of + // vsnprintf, vsprintf has no wide version + {{{"vswscanf"}, 3}, 2}}; + +const CallDescription ValistChecker::VaStart({"__builtin_va_start"}, /*Args=*/2, + /*Params=*/1), + ValistChecker::VaCopy({"__builtin_va_copy"}, 2), + ValistChecker::VaEnd({"__builtin_va_end"}, 1); } // end anonymous namespace void ValistChecker::checkPreCall(const CallEvent &Call, diff --git a/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp index 04e6603b4cbe..86a4e6fbcd6a 100644 --- a/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp @@ -35,6 +35,7 @@ #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/AST/ParentMap.h" +#include <optional> using namespace clang; using namespace ento; @@ -154,8 +155,8 @@ void VforkChecker::checkPostCall(const CallEvent &Call, // Get return value of vfork. SVal VforkRetVal = Call.getReturnValue(); - Optional<DefinedOrUnknownSVal> DVal = - VforkRetVal.getAs<DefinedOrUnknownSVal>(); + std::optional<DefinedOrUnknownSVal> DVal = + VforkRetVal.getAs<DefinedOrUnknownSVal>(); if (!DVal) return; diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp index 9c7a59971763..64028b277021 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp @@ -12,8 +12,8 @@ #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/ExprCXX.h" +#include <optional> -using llvm::Optional; namespace clang { std::pair<const Expr *, bool> @@ -34,8 +34,7 @@ tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) { } if (auto *call = dyn_cast<CallExpr>(E)) { if (auto *memberCall = dyn_cast<CXXMemberCallExpr>(call)) { - Optional<bool> IsGetterOfRefCt = - isGetterOfRefCounted(memberCall->getMethodDecl()); + std::optional<bool> IsGetterOfRefCt = isGetterOfRefCounted(memberCall->getMethodDecl()); if (IsGetterOfRefCt && *IsGetterOfRefCt) { E = memberCall->getImplicitObjectArgument(); if (StopAtFirstRefCountedObj) { diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp index 97f75135bf92..66d8588e2531 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp @@ -19,6 +19,7 @@ #include "clang/StaticAnalyzer/Core/Checker.h" #include "llvm/ADT/DenseSet.h" #include "llvm/Support/Casting.h" +#include <optional> using namespace clang; using namespace ento; @@ -69,7 +70,7 @@ public: if (shouldSkipDecl(RD)) return; - for (auto Member : RD->fields()) { + for (auto *Member : RD->fields()) { const Type *MemberType = Member->getType().getTypePtrOrNull(); if (!MemberType) continue; @@ -77,9 +78,9 @@ public: if (auto *MemberCXXRD = MemberType->getPointeeCXXRecordDecl()) { // If we don't see the definition we just don't know. if (MemberCXXRD->hasDefinition()) { - llvm::Optional<bool> isRCAble = isRefCountable(MemberCXXRD); - if (isRCAble && *isRCAble) - reportBug(Member, MemberType, MemberCXXRD, RD); + std::optional<bool> isRCAble = isRefCountable(MemberCXXRD); + if (isRCAble && *isRCAble) + reportBug(Member, MemberType, MemberCXXRD, RD); } } } diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index a198943c9433..9b1d7ae3e6a3 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -12,9 +12,8 @@ #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/ExprCXX.h" -#include "llvm/ADT/Optional.h" +#include <optional> -using llvm::Optional; using namespace clang; namespace { @@ -45,29 +44,31 @@ bool hasPublicRefAndDeref(const CXXRecordDecl *R) { namespace clang { -llvm::Optional<const clang::CXXRecordDecl *> -isRefCountable(const CXXBaseSpecifier *Base) { +std::optional<const clang::CXXRecordDecl*> +isRefCountable(const CXXBaseSpecifier* Base) +{ assert(Base); const Type *T = Base->getType().getTypePtrOrNull(); if (!T) - return llvm::None; + return std::nullopt; const CXXRecordDecl *R = T->getAsCXXRecordDecl(); if (!R) - return llvm::None; + return std::nullopt; if (!R->hasDefinition()) - return llvm::None; + return std::nullopt; return hasPublicRefAndDeref(R) ? R : nullptr; } -llvm::Optional<bool> isRefCountable(const CXXRecordDecl *R) { +std::optional<bool> isRefCountable(const CXXRecordDecl* R) +{ assert(R); R = R->getDefinition(); if (!R) - return llvm::None; + return std::nullopt; if (hasPublicRefAndDeref(R)) return true; @@ -77,20 +78,19 @@ llvm::Optional<bool> isRefCountable(const CXXRecordDecl *R) { bool AnyInconclusiveBase = false; const auto isRefCountableBase = - [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) { - Optional<const clang::CXXRecordDecl *> IsRefCountable = - clang::isRefCountable(Base); - if (!IsRefCountable) { - AnyInconclusiveBase = true; - return false; - } - return (*IsRefCountable) != nullptr; + [&AnyInconclusiveBase](const CXXBaseSpecifier* Base, CXXBasePath&) { + std::optional<const clang::CXXRecordDecl*> IsRefCountable = clang::isRefCountable(Base); + if (!IsRefCountable) { + AnyInconclusiveBase = true; + return false; + } + return (*IsRefCountable) != nullptr; }; bool BasesResult = R->lookupInBases(isRefCountableBase, Paths, /*LookupInDependent =*/true); if (AnyInconclusiveBase) - return llvm::None; + return std::nullopt; return BasesResult; } @@ -112,19 +112,21 @@ bool isCtorOfRefCounted(const clang::FunctionDecl *F) { || FunctionName == "Identifier"; } -llvm::Optional<bool> isUncounted(const CXXRecordDecl *Class) { +std::optional<bool> isUncounted(const CXXRecordDecl* Class) +{ // Keep isRefCounted first as it's cheaper. if (isRefCounted(Class)) return false; - llvm::Optional<bool> IsRefCountable = isRefCountable(Class); + std::optional<bool> IsRefCountable = isRefCountable(Class); if (!IsRefCountable) - return llvm::None; + return std::nullopt; return (*IsRefCountable); } -llvm::Optional<bool> isUncountedPtr(const Type *T) { +std::optional<bool> isUncountedPtr(const Type* T) +{ assert(T); if (T->isPointerType() || T->isReferenceType()) { @@ -135,7 +137,8 @@ llvm::Optional<bool> isUncountedPtr(const Type *T) { return false; } -Optional<bool> isGetterOfRefCounted(const CXXMethodDecl *M) { +std::optional<bool> isGetterOfRefCounted(const CXXMethodDecl* M) +{ assert(M); if (isa<CXXMethodDecl>(M)) { diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h index 753adea0d14d..91e3ccf2ec30 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h @@ -10,6 +10,7 @@ #define LLVM_CLANG_ANALYZER_WEBKIT_PTRTYPESEMANTICS_H #include "llvm/ADT/APInt.h" +#include <optional> namespace clang { class CXXBaseSpecifier; @@ -26,31 +27,31 @@ class Type; // Ref<T>. /// \returns CXXRecordDecl of the base if the type is ref-countable, nullptr if -/// not, None if inconclusive. -llvm::Optional<const clang::CXXRecordDecl *> -isRefCountable(const clang::CXXBaseSpecifier *Base); +/// not, std::nullopt if inconclusive. +std::optional<const clang::CXXRecordDecl*> +isRefCountable(const clang::CXXBaseSpecifier* Base); -/// \returns true if \p Class is ref-countable, false if not, None if +/// \returns true if \p Class is ref-countable, false if not, std::nullopt if /// inconclusive. -llvm::Optional<bool> isRefCountable(const clang::CXXRecordDecl *Class); +std::optional<bool> isRefCountable(const clang::CXXRecordDecl* Class); /// \returns true if \p Class is ref-counted, false if not. bool isRefCounted(const clang::CXXRecordDecl *Class); /// \returns true if \p Class is ref-countable AND not ref-counted, false if -/// not, None if inconclusive. -llvm::Optional<bool> isUncounted(const clang::CXXRecordDecl *Class); +/// not, std::nullopt if inconclusive. +std::optional<bool> isUncounted(const clang::CXXRecordDecl* Class); /// \returns true if \p T is either a raw pointer or reference to an uncounted -/// class, false if not, None if inconclusive. -llvm::Optional<bool> isUncountedPtr(const clang::Type *T); +/// class, false if not, std::nullopt if inconclusive. +std::optional<bool> isUncountedPtr(const clang::Type* T); /// \returns true if \p F creates ref-countable object from uncounted parameter, /// false if not. bool isCtorOfRefCounted(const clang::FunctionDecl *F); /// \returns true if \p M is getter of a ref-counted class, false if not. -llvm::Optional<bool> isGetterOfRefCounted(const clang::CXXMethodDecl *Method); +std::optional<bool> isGetterOfRefCounted(const clang::CXXMethodDecl* Method); /// \returns true if \p F is a conversion between ref-countable or ref-counted /// pointer types. diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp index fa9ece217cc0..48dcfc4a3c46 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp @@ -14,6 +14,7 @@ #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" +#include <optional> using namespace clang; using namespace ento; @@ -76,8 +77,7 @@ public: (AccSpec == AS_none && RD->isClass())) return false; - llvm::Optional<const CXXRecordDecl *> RefCntblBaseRD = - isRefCountable(Base); + std::optional<const CXXRecordDecl*> RefCntblBaseRD = isRefCountable(Base); if (!RefCntblBaseRD || !(*RefCntblBaseRD)) return false; diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp index e6d0948f71bb..4ae8c442fa70 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp @@ -19,6 +19,7 @@ #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "llvm/ADT/DenseSet.h" +#include <optional> using namespace clang; using namespace ento; @@ -85,7 +86,7 @@ public: continue; // FIXME? Should we bail? // FIXME: more complex types (arrays, references to raw pointers, etc) - Optional<bool> IsUncounted = isUncountedPtr(ArgType); + std::optional<bool> IsUncounted = isUncountedPtr(ArgType); if (!IsUncounted || !(*IsUncounted)) continue; diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp index deebbd603b2c..004b0b9d398b 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp @@ -14,6 +14,7 @@ #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" +#include <optional> using namespace clang; using namespace ento; @@ -57,18 +58,18 @@ public: void visitLambdaExpr(LambdaExpr *L) const { for (const LambdaCapture &C : L->captures()) { if (C.capturesVariable()) { - VarDecl *CapturedVar = C.getCapturedVar(); + ValueDecl *CapturedVar = C.getCapturedVar(); if (auto *CapturedVarType = CapturedVar->getType().getTypePtrOrNull()) { - Optional<bool> IsUncountedPtr = isUncountedPtr(CapturedVarType); - if (IsUncountedPtr && *IsUncountedPtr) { - reportBug(C, CapturedVar, CapturedVarType); - } + std::optional<bool> IsUncountedPtr = isUncountedPtr(CapturedVarType); + if (IsUncountedPtr && *IsUncountedPtr) { + reportBug(C, CapturedVar, CapturedVarType); + } } } } } - void reportBug(const LambdaCapture &Capture, VarDecl *CapturedVar, + void reportBug(const LambdaCapture &Capture, ValueDecl *CapturedVar, const Type *T) const { assert(CapturedVar); diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLocalVarsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLocalVarsChecker.cpp index 7e86f28cb70f..fa7475934981 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLocalVarsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLocalVarsChecker.cpp @@ -20,6 +20,7 @@ #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "llvm/ADT/DenseSet.h" +#include <optional> using namespace clang; using namespace ento; @@ -169,7 +170,7 @@ public: if (!ArgType) return; - Optional<bool> IsUncountedPtr = isUncountedPtr(ArgType); + std::optional<bool> IsUncountedPtr = isUncountedPtr(ArgType); if (IsUncountedPtr && *IsUncountedPtr) { const Expr *const InitExpr = V->getInit(); if (!InitExpr) diff --git a/clang/lib/StaticAnalyzer/Checkers/Yaml.h b/clang/lib/StaticAnalyzer/Checkers/Yaml.h index 497189f4c160..4159e14105f8 100644 --- a/clang/lib/StaticAnalyzer/Checkers/Yaml.h +++ b/clang/lib/StaticAnalyzer/Checkers/Yaml.h @@ -17,6 +17,7 @@ #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "llvm/Support/VirtualFileSystem.h" #include "llvm/Support/YAMLTraits.h" +#include <optional> namespace clang { namespace ento { @@ -25,10 +26,10 @@ namespace ento { /// template parameter must have a yaml MappingTraits. /// Emit diagnostic error in case of any failure. template <class T, class Checker> -llvm::Optional<T> getConfiguration(CheckerManager &Mgr, Checker *Chk, - StringRef Option, StringRef ConfigFile) { +std::optional<T> getConfiguration(CheckerManager &Mgr, Checker *Chk, + StringRef Option, StringRef ConfigFile) { if (ConfigFile.trim().empty()) - return None; + return std::nullopt; llvm::vfs::FileSystem *FS = llvm::vfs::getRealFileSystem().get(); llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Buffer = @@ -38,7 +39,7 @@ llvm::Optional<T> getConfiguration(CheckerManager &Mgr, Checker *Chk, Mgr.reportInvalidCheckerOptionValue(Chk, Option, "a valid filename instead of '" + std::string(ConfigFile) + "'"); - return None; + return std::nullopt; } llvm::yaml::Input Input(Buffer.get()->getBuffer()); @@ -48,7 +49,7 @@ llvm::Optional<T> getConfiguration(CheckerManager &Mgr, Checker *Chk, if (std::error_code ec = Input.error()) { Mgr.reportInvalidCheckerOptionValue(Chk, Option, "a valid yaml file: " + ec.message()); - return None; + return std::nullopt; } return Config; diff --git a/clang/lib/StaticAnalyzer/Checkers/cert/InvalidPtrChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/cert/InvalidPtrChecker.cpp index 084789509533..aae1a17bc0ae 100644 --- a/clang/lib/StaticAnalyzer/Checkers/cert/InvalidPtrChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/cert/InvalidPtrChecker.cpp @@ -39,11 +39,11 @@ private: // SEI CERT ENV31-C const CallDescriptionMap<HandlerFn> EnvpInvalidatingFunctions = { - {{"setenv", 3}, &InvalidPtrChecker::EnvpInvalidatingCall}, - {{"unsetenv", 1}, &InvalidPtrChecker::EnvpInvalidatingCall}, - {{"putenv", 1}, &InvalidPtrChecker::EnvpInvalidatingCall}, - {{"_putenv_s", 2}, &InvalidPtrChecker::EnvpInvalidatingCall}, - {{"_wputenv_s", 2}, &InvalidPtrChecker::EnvpInvalidatingCall}, + {{{"setenv"}, 3}, &InvalidPtrChecker::EnvpInvalidatingCall}, + {{{"unsetenv"}, 1}, &InvalidPtrChecker::EnvpInvalidatingCall}, + {{{"putenv"}, 1}, &InvalidPtrChecker::EnvpInvalidatingCall}, + {{{"_putenv_s"}, 2}, &InvalidPtrChecker::EnvpInvalidatingCall}, + {{{"_wputenv_s"}, 2}, &InvalidPtrChecker::EnvpInvalidatingCall}, }; void postPreviousReturnInvalidatingCall(const CallEvent &Call, @@ -51,13 +51,15 @@ private: // SEI CERT ENV34-C const CallDescriptionMap<HandlerFn> PreviousCallInvalidatingFunctions = { - {{"getenv", 1}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall}, - {{"setlocale", 2}, + {{{"getenv"}, 1}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall}, + {{{"setlocale"}, 2}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall}, - {{"strerror", 1}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall}, - {{"localeconv", 0}, + {{{"strerror"}, 1}, + &InvalidPtrChecker::postPreviousReturnInvalidatingCall}, + {{{"localeconv"}, 0}, + &InvalidPtrChecker::postPreviousReturnInvalidatingCall}, + {{{"asctime"}, 1}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall}, - {{"asctime", 1}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall}, }; public: diff --git a/clang/lib/StaticAnalyzer/Checkers/cert/PutenvWithAutoChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/cert/PutenvWithAutoChecker.cpp index ed3bdafad084..eae162cda693 100644 --- a/clang/lib/StaticAnalyzer/Checkers/cert/PutenvWithAutoChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/cert/PutenvWithAutoChecker.cpp @@ -30,7 +30,7 @@ class PutenvWithAutoChecker : public Checker<check::PostCall> { private: BugType BT{this, "'putenv' function should not be called with auto variables", categories::SecurityError}; - const CallDescription Putenv{"putenv", 1}; + const CallDescription Putenv{{"putenv"}, 1}; public: void checkPostCall(const CallEvent &Call, CheckerContext &C) const; 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; + } + + 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 "; + } +} - auto &Context = getContext(); +/// 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); - - return Size <= AMgr.options.maxBlockVisitOnPath; + if (const auto *CAT = dyn_cast<ConstantArrayType>(CE->getType())) { + unsigned ArrSize = getContext().getConstantArrayElementCount(CAT); + + // 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); - - for (RegionStoreManager::SValListTy::const_iterator I = Vals.begin(), - E = Vals.end(); - I != E; ++I) - VisitBinding(*I); + // 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 (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}}; - - std::string RuleURI = std::string(getRuleHelpURIStr(CheckName)); - if (!RuleURI.empty()) - Ret["helpUri"] = RuleURI; - - return Ret; +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); + + 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); +} + +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; diff --git a/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp index ca0a66f54ced..54e0f4bee5eb 100644 --- a/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp +++ b/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp @@ -27,7 +27,6 @@ #include "clang/Frontend/CompilerInstance.h" #include "clang/Lex/Preprocessor.h" #include "clang/Rewrite/Core/Rewriter.h" -#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h" #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" diff --git a/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp b/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp index 9ee6ef4f9519..4618d17577dd 100644 --- a/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp +++ b/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp @@ -15,7 +15,6 @@ #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/SetVector.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/DynamicLibrary.h" |