=?utf-8?q?Donát?= Nagy <donat.n...@ericsson.com>, =?utf-8?q?Donát?= Nagy <donat.n...@ericsson.com>, =?utf-8?q?Donát?= Nagy <donat.n...@ericsson.com> Message-ID: In-Reply-To: <llvm.org/llvm/llvm-project/pull/132...@github.com>
================ @@ -112,25 +112,30 @@ class NullabilityChecker void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, const char *Sep) const override; - enum CheckKind { - CK_NullPassedToNonnull, - CK_NullReturnedFromNonnull, - CK_NullableDereferenced, - CK_NullablePassedToNonnull, - CK_NullableReturnedFromNonnull, - CK_NumCheckKinds + // FIXME: This enumeration of checker parts is extremely similar to the + // ErrorKind enum. It would be nice to unify them to simplify the code. + enum : CheckerPartIdx { + NullPassedToNonnullChecker, + NullReturnedFromNonnullChecker, + NullableDereferencedChecker, + NullablePassedToNonnullChecker, + NullableReturnedFromNonnullChecker, + NumCheckerParts }; - bool ChecksEnabled[CK_NumCheckKinds] = {false}; - CheckerNameRef CheckNames[CK_NumCheckKinds]; - mutable std::unique_ptr<BugType> BTs[CK_NumCheckKinds]; - - const std::unique_ptr<BugType> &getBugType(CheckKind Kind) const { - if (!BTs[Kind]) - BTs[Kind].reset(new BugType(CheckNames[Kind], "Nullability", - categories::MemoryError)); - return BTs[Kind]; - } + // FIXME: Currently the `Description` fields of these `BugType`s are all + // identical ("Nullability") -- they should be more descriptive than this. + BugType BugTypes[NumCheckerParts] = { + {this, NullPassedToNonnullChecker, "Nullability", + categories::MemoryError}, + {this, NullReturnedFromNonnullChecker, "Nullability", + categories::MemoryError}, + {this, NullableDereferencedChecker, "Nullability", + categories::MemoryError}, + {this, NullablePassedToNonnullChecker, "Nullability", + categories::MemoryError}, + {this, NullableReturnedFromNonnullChecker, "Nullability", + categories::MemoryError}}; ---------------- steakhal wrote: I have something like this in mind: ```diff diff --git a/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugType.h b/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugType.h index 3a635e0d0125..75c7786aea3e 100644 --- a/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugType.h +++ b/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugType.h @@ -29,7 +29,7 @@ class BugType { private: struct CheckerPartRef { const CheckerBase *Checker; - CheckerPartIdx Idx; + const CheckerPart &Part; }; using CheckerNameInfo = std::variant<CheckerNameRef, CheckerPartRef>; @@ -55,14 +55,14 @@ public: // pointer to the checker and later use that to query the name. BugType(const CheckerBase *Checker, StringRef Desc, StringRef Cat = categories::LogicError, bool SuppressOnSink = false) - : CheckerName(CheckerPartRef{Checker, DefaultPart}), Description(Desc), - Category(Cat), SuppressOnSink(SuppressOnSink) {} + : CheckerName(Checker->getName()), Description(Desc), Category(Cat), + SuppressOnSink(SuppressOnSink) {} // Constructor that can be called from the constructor of a checker object // when it has multiple parts with separate names. We save the name and the // part index to be able to query the name of that part later. - BugType(const CheckerBase *Checker, CheckerPartIdx Idx, StringRef Desc, + BugType(const CheckerBase *Checker, const CheckerPart &Part, StringRef Desc, StringRef Cat = categories::LogicError, bool SuppressOnSink = false) - : CheckerName(CheckerPartRef{Checker, Idx}), Description(Desc), + : CheckerName(CheckerPartRef{Checker, Part}), Description(Desc), Category(Cat), SuppressOnSink(SuppressOnSink) {} virtual ~BugType() = default; @@ -72,8 +72,8 @@ public: if (const auto *CNR = std::get_if<CheckerNameRef>(&CheckerName)) return *CNR; - auto [Checker, Idx] = std::get<CheckerPartRef>(CheckerName); - return Checker->getName(Idx); + auto [Checker, Part] = std::get<CheckerPartRef>(CheckerName); + return Checker->getName(Part); } /// isSuppressOnSink - Returns true if bug reports associated with this bug diff --git a/clang/include/clang/StaticAnalyzer/Core/Checker.h b/clang/include/clang/StaticAnalyzer/Core/Checker.h index a54c5bee612f..e63b3454b853 100644 --- a/clang/include/clang/StaticAnalyzer/Core/Checker.h +++ b/clang/include/clang/StaticAnalyzer/Core/Checker.h @@ -17,6 +17,7 @@ #include "clang/Basic/LangOptions.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "llvm/ADT/DenseMap.h" #include "llvm/Support/Casting.h" namespace clang { @@ -488,31 +489,30 @@ class CheckerBase : public ProgramPointTag { /// A single checker class (i.e. a subclass of `CheckerBase`) can implement /// multiple user-facing checkers that have separate names and can be enabled /// separately, but are backed by the same singleton checker object. - SmallVector<std::optional<CheckerNameRef>, 1> RegisteredNames; + llvm::SmallDenseMap<const CheckerPart *, CheckerNameRef> RegisteredNames; + CheckerNameRef FirstRegisteredName; friend class ::clang::ento::CheckerManager; public: - CheckerNameRef getName(CheckerPartIdx Idx = DefaultPart) const { - assert(Idx < RegisteredNames.size() && "Checker part index is too large!"); - std::optional<CheckerNameRef> Name = RegisteredNames[Idx]; - assert(Name && "Requested checker part is not registered!"); - return *Name; + CheckerNameRef getName() const { + assert(!RegisteredNames.empty()); + return FirstRegisteredName; } - bool isPartEnabled(CheckerPartIdx Idx) const { - return Idx < RegisteredNames.size() && RegisteredNames[Idx].has_value(); + CheckerNameRef getName(const CheckerPart &Part) const { + auto It = RegisteredNames.find(&Part); + assert(It != RegisteredNames.end() && + "Requested checker part is not registered!"); + return It->second; } - void registerCheckerPart(CheckerPartIdx Idx, CheckerNameRef Name) { - // Paranoia: notice if e.g. UINT_MAX is passed as a checker part index. - assert(Idx < 256 && "Checker part identifiers should be small integers."); + void registerCheckerPart(const CheckerPart &Part, CheckerNameRef Name) { + if (RegisteredNames.empty()) + FirstRegisteredName = Name; - if (Idx >= RegisteredNames.size()) - RegisteredNames.resize(Idx + 1, std::nullopt); - - assert(!RegisteredNames[Idx] && "Repeated registration of checker a part!"); - RegisteredNames[Idx] = Name; + bool Inserted = RegisteredNames.insert({&Part, Name}).second; + assert(Inserted && "Repeated registration of checker a part!"); } StringRef getTagDescription() const override { @@ -521,11 +521,10 @@ public: // checker _class_. Ideally this should be recognizable identifier of the // whole class, but for this debugging purpose it's sufficient to use the // name of the first registered checker part. - for (const auto &OptName : RegisteredNames) - if (OptName) - return *OptName; - - return "Unregistered checker"; + assert(!RegisteredNames.empty()); + if (RegisteredNames.empty()) + return "Unregistered checker"; + return FirstRegisteredName; } /// Debug state dump callback, see CheckerManager::runCheckersForPrintState. diff --git a/clang/include/clang/StaticAnalyzer/Core/CheckerManager.h b/clang/include/clang/StaticAnalyzer/Core/CheckerManager.h index 03ffadd346d0..b0ffef197246 100644 --- a/clang/include/clang/StaticAnalyzer/Core/CheckerManager.h +++ b/clang/include/clang/StaticAnalyzer/Core/CheckerManager.h @@ -116,18 +116,18 @@ public: operator StringRef() const { return Name; } }; -/// A single checker class (and its singleton instance) can act as the -/// implementation of several (user-facing or modeling) checker parts that -/// have shared state and logic, but have their own names and can be enabled or -/// disabled separately. -/// Each checker class that implement multiple parts introduces its own enum -/// type to assign small numerical indices (0, 1, 2 ...) to their parts. The -/// type alias 'CheckerPartIdx' is conceptually the union of these enum types. -using CheckerPartIdx = unsigned; - -/// If a checker doesn't have multiple parts, then its single part is -/// represented by this index. -constexpr inline CheckerPartIdx DefaultPart = 0; +// / A single checker class (and its singleton instance) can act as the +// / implementation of several (user-facing or modeling) checker parts that +// / have shared state and logic, but have their own names and can be enabled or +// / disabled separately. +// / Each checker class that implement multiple parts introduces its own enum +// / type to assign small numerical indices (0, 1, 2 ...) to their parts. The +// / type alias 'CheckerPartIdx' is conceptually the union of these enum types. +struct CheckerPart { + const CheckerBase &Checker; + bool IsEnabled = false; + explicit CheckerPart(const CheckerBase *Checker) : Checker(*Checker) {} +}; enum class ObjCMessageVisitKind { Pre, @@ -196,11 +196,12 @@ public: void reportInvalidCheckerOptionValue(const CheckerBase *C, StringRef OptionName, StringRef ExpectedValueDesc) const { - reportInvalidCheckerOptionValue(C, DefaultPart, OptionName, + reportInvalidCheckerOptionValue(C, /*Part=*/nullptr, OptionName, ExpectedValueDesc); } - void reportInvalidCheckerOptionValue(const CheckerBase *C, CheckerPartIdx Idx, + void reportInvalidCheckerOptionValue(const CheckerBase *C, + const CheckerPart *Part, StringRef OptionName, StringRef ExpectedValueDesc) const; @@ -221,12 +222,13 @@ public: /// existing checker object (while registering their names). /// /// \returns a pointer to the checker object. - template <typename CHECKER, CheckerPartIdx Idx = DefaultPart, typename... AT> + template <typename CHECKER, CheckerPart(CHECKER::*PartSelector) = nullptr, + typename... AT> CHECKER *registerChecker(AT &&...Args) { // This assert could be removed but then we need to make sure that calls // registering different parts of the same checker pass the same arguments. static_assert( - Idx == DefaultPart || !sizeof...(AT), + PartSelector == nullptr || !sizeof...(AT), "Argument forwarding isn't supported with multi-part checkers!"); CheckerTag Tag = getTag<CHECKER>(); @@ -240,7 +242,10 @@ public: } CHECKER *Result = static_cast<CHECKER *>(Ref.get()); - Result->registerCheckerPart(Idx, CurrentCheckerName); + CheckerPart &Part = Result->*PartSelector; + Result->registerCheckerPart(Part, CurrentCheckerName); + assert(!Part.IsEnabled); + Part.IsEnabled = true; return Result; } diff --git a/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp index 3dd57732305b..a765f30a71a1 100644 --- a/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp @@ -34,14 +34,11 @@ class DivZeroChecker : public Checker<check::PreStmt<BinaryOperator>> { public: /// This checker class implements several user facing checkers - enum : CheckerPartIdx { - DivideZeroChecker, - TaintedDivChecker, - NumCheckerParts - }; - BugType BugTypes[NumCheckerParts] = { - {this, DivideZeroChecker, "Division by zero"}, - {this, TaintedDivChecker, "Division by zero", categories::TaintedData}}; + CheckerPart DivideZeroChecker{this}; + CheckerPart TaintedDivChecker{this}; + const BugType DivisionByZeroBug{this, DivideZeroChecker, "Division by zero"}; + const BugType DivisionByTaintedBug{ + this, TaintedDivChecker, "Division by zero", categories::TaintedData}; void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const; }; @@ -56,11 +53,11 @@ static const Expr *getDenomExpr(const ExplodedNode *N) { void DivZeroChecker::reportBug(StringRef Msg, ProgramStateRef StateZero, CheckerContext &C) const { - if (!isPartEnabled(DivideZeroChecker)) + if (!DivideZeroChecker.IsEnabled) return; if (ExplodedNode *N = C.generateErrorNode(StateZero)) { - auto R = std::make_unique<PathSensitiveBugReport>( - BugTypes[DivideZeroChecker], Msg, N); + auto R = + std::make_unique<PathSensitiveBugReport>(DivisionByZeroBug, Msg, N); bugreporter::trackExpressionValue(N, getDenomExpr(N), *R); C.emitReport(std::move(R)); } @@ -69,11 +66,11 @@ void DivZeroChecker::reportBug(StringRef Msg, ProgramStateRef StateZero, void DivZeroChecker::reportTaintBug( StringRef Msg, ProgramStateRef StateZero, CheckerContext &C, llvm::ArrayRef<SymbolRef> TaintedSyms) const { - if (!isPartEnabled(TaintedDivChecker)) + if (!TaintedDivChecker.IsEnabled) return; if (ExplodedNode *N = C.generateNonFatalErrorNode(StateZero)) { - auto R = std::make_unique<PathSensitiveBugReport>( - BugTypes[TaintedDivChecker], Msg, N); + auto R = + std::make_unique<PathSensitiveBugReport>(DivisionByTaintedBug, Msg, N); bugreporter::trackExpressionValue(N, getDenomExpr(N), *R); for (auto Sym : TaintedSyms) R->markInteresting(Sym); @@ -127,13 +124,13 @@ void DivZeroChecker::checkPreStmt(const BinaryOperator *B, } void ento::registerDivZeroChecker(CheckerManager &Mgr) { - Mgr.registerChecker<DivZeroChecker, DivZeroChecker::DivideZeroChecker>(); + Mgr.registerChecker<DivZeroChecker, &DivZeroChecker::DivideZeroChecker>(); } bool ento::shouldRegisterDivZeroChecker(const CheckerManager &) { return true; } void ento::registerTaintedDivChecker(CheckerManager &Mgr) { - Mgr.registerChecker<DivZeroChecker, DivZeroChecker::TaintedDivChecker>(); + Mgr.registerChecker<DivZeroChecker, &DivZeroChecker::TaintedDivChecker>(); } bool ento::shouldRegisterTaintedDivChecker(const CheckerManager &) { diff --git a/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp index 5b0d303ee5bb..5a58e4cc6553 100644 --- a/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp @@ -42,13 +42,14 @@ namespace { class VirtualCallChecker : public Checker<check::BeginFunction, check::EndFunction, check::PreCall> { public: - enum : CheckerPartIdx { PureChecker, ImpureChecker, NumCheckerParts }; - - BugType BugTypes[NumCheckerParts] = { - {this, PureChecker, "Pure virtual method call", - categories::CXXObjectLifecycle}, - {this, ImpureChecker, "Unexpected loss of virtual dispatch", - categories::CXXObjectLifecycle}}; + CheckerPart PureChecker{this}; + CheckerPart ImpureChecker{this}; + const BugType CallingPureVirtualMethodBug{this, PureChecker, + "Pure virtual method call", + categories::CXXObjectLifecycle}; + const BugType UnexpectedVirtualDispatchBug{ + this, ImpureChecker, "Unexpected loss of virtual dispatch", + categories::CXXObjectLifecycle}; bool ShowFixIts = false; @@ -147,15 +148,14 @@ void VirtualCallChecker::checkPreCall(const CallEvent &Call, if (!N) return; - const CheckerPartIdx Part = IsPure ? PureChecker : ImpureChecker; - - if (!isPartEnabled(Part)) { + const CheckerPart &Part = IsPure ? PureChecker : ImpureChecker; + if (!Part.IsEnabled) { // The respective check is disabled. return; } - - auto Report = - std::make_unique<PathSensitiveBugReport>(BugTypes[Part], OS.str(), N); + const BugType &Bug = + IsPure ? CallingPureVirtualMethodBug : UnexpectedVirtualDispatchBug; + auto Report = std::make_unique<PathSensitiveBugReport>(Bug, OS.str(), N); if (ShowFixIts && !IsPure) { // FIXME: These hints are valid only when the virtual call is made @@ -210,7 +210,7 @@ void VirtualCallChecker::registerCtorDtorCallInState(bool IsBeginFunction, } void ento::registerPureVirtualCallChecker(CheckerManager &Mgr) { - Mgr.registerChecker<VirtualCallChecker, VirtualCallChecker::PureChecker>(); + Mgr.registerChecker<VirtualCallChecker, &VirtualCallChecker::PureChecker>(); } bool ento::shouldRegisterPureVirtualCallChecker(const CheckerManager &Mgr) { @@ -219,7 +219,7 @@ bool ento::shouldRegisterPureVirtualCallChecker(const CheckerManager &Mgr) { void ento::registerVirtualCallChecker(CheckerManager &Mgr) { auto *Chk = Mgr.registerChecker<VirtualCallChecker, - VirtualCallChecker::ImpureChecker>(); + &VirtualCallChecker::ImpureChecker>(); Chk->ShowFixIts = Mgr.getAnalyzerOptions().getCheckerBooleanOption( Mgr.getCurrentCheckerName(), "ShowFixIts"); } diff --git a/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp b/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp index 7ae86f133904..c8281df38427 100644 --- a/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -50,11 +50,11 @@ bool CheckerManager::hasPathSensitiveCheckers() const { } void CheckerManager::reportInvalidCheckerOptionValue( - const CheckerBase *C, CheckerPartIdx Idx, StringRef OptionName, + const CheckerBase *C, const CheckerPart *Part, StringRef OptionName, StringRef ExpectedValueDesc) const { getDiagnostics().Report(diag::err_analyzer_checker_option_invalid_input) - << (llvm::Twine(C->getName(Idx)) + ":" + OptionName).str() + << (llvm::Twine(C->getName(*Part)) + ":" + OptionName).str() << ExpectedValueDesc; } ``` https://github.com/llvm/llvm-project/pull/132250 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits