Index: clang/include/clang/StaticAnalyzer/Core/Checker.h =================================================================== --- clang/include/clang/StaticAnalyzer/Core/Checker.h +++ clang/include/clang/StaticAnalyzer/Core/Checker.h @@ -494,6 +494,8 @@ friend class ::clang::ento::CheckerManager; public: + CheckerBase() = default; + CheckerBase(CheckerNameRef Name) : Name(Name) {} StringRef getTagDescription() const override; CheckerNameRef getCheckerName() const; @@ -502,6 +504,47 @@ const char *NL, const char *Sep) const { } }; + +template +class SuperCheckerBase : public CheckerBase { + static_assert(std::is_enum::value, + "SuperCheckers are required to provide an enum to keep track " + "of their subcheckers!"); + + using SubCheckerPair = std::pair; + using SubCheckerVector = typename llvm::SmallVector; + +public: + using SubCheckerTy = SubCheckerEnumTy; + +private: + SubCheckerVector Subcheckers; + + typename SubCheckerVector::const_iterator + getSubCheckerPos(SubCheckerEnumTy SubCheckerKind) const { + return llvm::find_if(Subcheckers, [SubCheckerKind](const auto &E) { + return E.first == SubCheckerKind; + }); + } + +public: + template + void addSubChecker(CheckerNameRef Name) { + assert(getSubCheckerPos(SubCheckerKind) == Subcheckers.end() && + "This subchecker was already added to the superchecker!"); + Subcheckers.emplace_back(SubCheckerKind, CheckerBase(Name)); + } + + template + const CheckerBase *getSubChecker() const { + typename SubCheckerVector::const_iterator Pos = + getSubCheckerPos(SubCheckerKind); + if (Pos == Subcheckers.end()) + return nullptr; + return &Pos->second; + } +}; + /// Dump checker name to stream. raw_ostream& operator<<(raw_ostream &Out, const CheckerBase &Checker); @@ -513,24 +556,35 @@ CheckerProgramPointTag(const CheckerBase *Checker, StringRef Msg); }; -template -class Checker : public CHECK1, public CHECKs..., public CheckerBase { +namespace checker_detail { +template +class CheckerImpl : public CHECK1, public CHECKs..., public BaseTy { public: template static void _register(CHECKER *checker, CheckerManager &mgr) { CHECK1::_register(checker, mgr); - Checker::_register(checker, mgr); + CheckerImpl::_register(checker, mgr); } }; -template -class Checker : public CHECK1, public CheckerBase { +template +class CheckerImpl : public CHECK1, public BaseTy { public: template static void _register(CHECKER *checker, CheckerManager &mgr) { CHECK1::_register(checker, mgr); } }; +} // end of namespace checker_detail + +template +using Checker = + typename checker_detail::CheckerImpl; + +template +using SuperChecker = + typename checker_detail::CheckerImpl, + CHECKs...>; template class EventDispatcher { @@ -575,8 +629,7 @@ DefaultBool &operator=(bool b) { val = b; return *this; } }; -} // end ento namespace - -} // end clang namespace +} // namespace ento +} // namespace clang #endif Index: clang/include/clang/StaticAnalyzer/Core/CheckerManager.h =================================================================== --- clang/include/clang/StaticAnalyzer/Core/CheckerManager.h +++ clang/include/clang/StaticAnalyzer/Core/CheckerManager.h @@ -180,6 +180,20 @@ return static_cast(CheckerTags[tag]); } + /// Used to register subcheckers. Subcheckers aren't traditional checkers in + /// the sense that they don't have checker callbacks, but there is checker + /// object associated with them, which is retrievable though the checker they + /// are possessed by. + /// + /// \returns a pointer to the super checker object. + template + SuperChecker *registerSubChecker() { + SuperChecker *Super = getChecker(); + Super->template addSubChecker(CurrentCheckerName); + return Super; + } + //===----------------------------------------------------------------------===// // Functions for running checkers for AST traversing. //===----------------------------------------------------------------------===// Index: clang/unittests/StaticAnalyzer/RegisterCustomCheckersTest.cpp =================================================================== --- clang/unittests/StaticAnalyzer/RegisterCustomCheckersTest.cpp +++ clang/unittests/StaticAnalyzer/RegisterCustomCheckersTest.cpp @@ -81,6 +81,127 @@ runCheckerOnCode("void f() { int *p; (*p)++; }")); } +//===----------------------------------------------------------------------===// +// Subchecker system. +//===----------------------------------------------------------------------===// + +enum CXX23ModelingDiagKind { IntPointer, NonLoad }; + +class CXX23Modeling + : public SuperChecker { +public: + void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr, + BugReporter &BR) const { + BR.EmitBasicReport(D, this, "Custom diagnostic", categories::LogicError, + "Sketchy C++23 code modeled", + PathDiagnosticLocation(D, Mgr.getSourceManager()), {}); + + if (const CheckerBase *IntPointerChecker = getSubChecker()) + BR.EmitBasicReport(D, IntPointerChecker, "Custom diagnostic", + categories::LogicError, "Sketchy C++23 int pointer", + PathDiagnosticLocation(D, Mgr.getSourceManager()), {}); + + if (const CheckerBase *NonLoadChecker = getSubChecker()) + BR.EmitBasicReport(D, NonLoadChecker, "Custom diagnostic", + categories::LogicError, + "Sketchy C++23 pointer non-loaded", + PathDiagnosticLocation(D, Mgr.getSourceManager()), {}); + } +}; + +void registerCXX23IntPointer(CheckerManager &Mgr) { + Mgr.registerSubChecker(); +} + +void registerCXX23NonLoad(CheckerManager &Mgr) { + Mgr.registerSubChecker(); +} + +void addButDontSpecifyCXX23Modeling(AnalysisASTConsumer &AnalysisConsumer, + AnalyzerOptions &AnOpts) { + AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) { + Registry.addChecker("test.CXX23Modeling", "Description", ""); + }); +} + +void addAndEnableCXX23Modeling(AnalysisASTConsumer &AnalysisConsumer, + AnalyzerOptions &AnOpts) { + AnOpts.CheckersAndPackages = {{"test.CXX23Modeling", true}}; + AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) { + Registry.addChecker("test.CXX23Modeling", "Description", ""); + }); +} + +void addButDisableCXX23Modeling(AnalysisASTConsumer &AnalysisConsumer, + AnalyzerOptions &AnOpts) { + AnOpts.CheckersAndPackages = {{"test.CXX23Modeling", false}}; + AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) { + Registry.addChecker("test.CXX23Modeling", "Description", ""); + }); +} + +void addCXX23IntPointer(AnalysisASTConsumer &AnalysisConsumer, + AnalyzerOptions &AnOpts) { + AnOpts.CheckersAndPackages.emplace_back("test.CXX23IntPointer", true); + AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) { + Registry.addChecker(registerCXX23IntPointer, CheckerRegistry::returnTrue, + "test.CXX23IntPointer", "Description", "", + /*IsHidden*/ false); + Registry.addDependency("test.CXX23IntPointer", "test.CXX23Modeling"); + }); +} + +void addCXX23NonLoad(AnalysisASTConsumer &AnalysisConsumer, + AnalyzerOptions &AnOpts) { + AnOpts.CheckersAndPackages.emplace_back("test.CXX23NonLoad", true); + AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) { + Registry.addChecker(registerCXX23NonLoad, CheckerRegistry::returnTrue, + "test.CXX23NonLoad", "Description", "", + /*IsHidden*/ false); + Registry.addDependency("test.CXX23NonLoad", "test.CXX23Modeling"); + }); +} + +TEST(RegisterCustomCheckers, SuperChecker) { + std::string Output; + EXPECT_TRUE(runCheckerOnCode( + "void foo(int *a) { *a; }", Output)); + EXPECT_EQ(Output, "test.CXX23Modeling:Sketchy C++23 code modeled\n"); + + Output.clear(); + bool ReturnValue = + runCheckerOnCode( + "void foo(int *a) { *a; }", Output); + EXPECT_TRUE(ReturnValue); + EXPECT_EQ(Output, "test.CXX23Modeling:Sketchy C++23 code modeled\n" + "test.CXX23IntPointer:Sketchy C++23 int pointer\n"); + + Output.clear(); + ReturnValue = + runCheckerOnCode("void foo(int *a) { *a; }", Output); + EXPECT_TRUE(ReturnValue); + EXPECT_EQ(Output, "test.CXX23Modeling:Sketchy C++23 code modeled\n" + "test.CXX23IntPointer:Sketchy C++23 int pointer\n" + "test.CXX23NonLoad:Sketchy C++23 pointer non-loaded\n"); + + Output.clear(); + ReturnValue = + runCheckerOnCode("void foo(int *a) { *a; }", Output); + EXPECT_TRUE(ReturnValue); + EXPECT_EQ(Output, "test.CXX23Modeling:Sketchy C++23 code modeled\n" + "test.CXX23IntPointer:Sketchy C++23 int pointer\n" + "test.CXX23NonLoad:Sketchy C++23 pointer non-loaded\n"); + + Output.clear(); + ReturnValue = + runCheckerOnCode("void foo(int *a) { *a; }", Output); + EXPECT_TRUE(ReturnValue); + EXPECT_EQ(Output, ""); +} + } // namespace } // namespace ento } // namespace clang