Szelethus created this revision.
Szelethus added reviewers: NoQ, xazax.hun, rnkovacs, dcoughlin, Charusso, 
baloghadamsoftware, dkrupp.
Szelethus added a project: clang.
Herald added subscribers: cfe-commits, gamesh411, donat.nagy, mikhail.ramalho, 
a.sidorin, szepet, whisperity.

Please don't take a shot for each time I write checker, it'd end bad.

The term "super checker" or "modeling checker", and the term "subchecker" 
always existed conceptually, but they weren't ever formalized. In the last half 
a year or so, we referred to them as

- Super/modeling checker: A checker that models some C++ code, but doesn't (or, 
as of now, just //shouldn't//) emit any diagnostics, at least not under its own 
name.
- Subcheckers: Checkers that are a part of super/modeling checkers, 
enabling/disabling them (ideally) only toggles whether a diagnostic is emitted 
from the checker it is a part of. They don't possess a checker object on their 
own, and are basically glorified checker options.

While checker dependencies were in similar shoes (existed conceptually but not 
formalized), this change isn't //as// critical, it just removes boilerplate 
code. When you look at `IteratorChecker`, `SecuritySyntaxChecker` or 
`RetainCountBase` they all use a similar, some cases faulty implementation to 
keep track of which subcheckers are enabled and what their name is, so its 
about time we combined them.

I envision this interface to be used to enforce our currently non-existent 
specification on the checker system.

In detail:

- Introduce `SuperChecker`:
  - It is essentially the same as `Checker`, but requires an enum template 
argument to keep track of which subcheckers are enabled.
  - While previously we defined subcheckers as checkers that don't have a 
checker object on their own, `SuperChecker` does create a `CheckerBase` object 
for them, but they still can't have checker callbacks.
- Add `CheckerManager::registerSubChecker` adds a new checker to a 
`SuperChecker`. It is ensured runtime that the `SuperChecker` was previously 
registered, and that a subchecker isn't registered multiple times.
- Add thorough test cases for the new interface.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D67336

Files:
  clang/include/clang/StaticAnalyzer/Core/Checker.h
  clang/include/clang/StaticAnalyzer/Core/CheckerManager.h
  clang/unittests/StaticAnalyzer/RegisterCustomCheckersTest.cpp

Index: clang/unittests/StaticAnalyzer/RegisterCustomCheckersTest.cpp
===================================================================
--- clang/unittests/StaticAnalyzer/RegisterCustomCheckersTest.cpp
+++ clang/unittests/StaticAnalyzer/RegisterCustomCheckersTest.cpp
@@ -81,6 +81,127 @@
       runCheckerOnCode<addLocIncDecChecker>("void f() { int *p; (*p)++; }"));
 }
 
+//===----------------------------------------------------------------------===//
+// Subchecker system.
+//===----------------------------------------------------------------------===//
+
+enum CXX23ModelingDiagKind { IntPointer, NonLoad };
+
+class CXX23Modeling
+    : public SuperChecker<CXX23ModelingDiagKind, check::ASTCodeBody> {
+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<IntPointer>())
+      BR.EmitBasicReport(D, IntPointerChecker, "Custom diagnostic",
+                         categories::LogicError, "Sketchy C++23 int pointer",
+                         PathDiagnosticLocation(D, Mgr.getSourceManager()), {});
+
+    if (const CheckerBase *NonLoadChecker = getSubChecker<NonLoad>())
+      BR.EmitBasicReport(D, NonLoadChecker, "Custom diagnostic",
+                         categories::LogicError,
+                         "Sketchy C++23 pointer non-loaded",
+                         PathDiagnosticLocation(D, Mgr.getSourceManager()), {});
+  }
+};
+
+void registerCXX23IntPointer(CheckerManager &Mgr) {
+  Mgr.registerSubChecker<CXX23Modeling, CXX23ModelingDiagKind::IntPointer>();
+}
+
+void registerCXX23NonLoad(CheckerManager &Mgr) {
+  Mgr.registerSubChecker<CXX23Modeling, CXX23ModelingDiagKind::NonLoad>();
+}
+
+void addButDontSpecifyCXX23Modeling(AnalysisASTConsumer &AnalysisConsumer,
+                      AnalyzerOptions &AnOpts) {
+  AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) {
+    Registry.addChecker<CXX23Modeling>("test.CXX23Modeling", "Description", "");
+  });
+}
+
+void addAndEnableCXX23Modeling(AnalysisASTConsumer &AnalysisConsumer,
+                      AnalyzerOptions &AnOpts) {
+  AnOpts.CheckersAndPackages = {{"test.CXX23Modeling", true}};
+  AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) {
+    Registry.addChecker<CXX23Modeling>("test.CXX23Modeling", "Description", "");
+  });
+}
+
+void addButDisableCXX23Modeling(AnalysisASTConsumer &AnalysisConsumer,
+                      AnalyzerOptions &AnOpts) {
+  AnOpts.CheckersAndPackages = {{"test.CXX23Modeling", false}};
+  AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) {
+    Registry.addChecker<CXX23Modeling>("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<addAndEnableCXX23Modeling>(
+      "void foo(int *a) { *a; }", Output));
+  EXPECT_EQ(Output, "test.CXX23Modeling:Sketchy C++23 code modeled\n");
+
+  Output.clear();
+  bool ReturnValue =
+      runCheckerOnCode<addAndEnableCXX23Modeling, addCXX23IntPointer>(
+          "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<addAndEnableCXX23Modeling, addCXX23IntPointer,
+                       addCXX23NonLoad>("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<addButDontSpecifyCXX23Modeling, addCXX23IntPointer,
+                       addCXX23NonLoad>("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<addButDisableCXX23Modeling, addCXX23IntPointer,
+                       addCXX23NonLoad>("void foo(int *a) { *a; }", Output);
+  EXPECT_TRUE(ReturnValue);
+  EXPECT_EQ(Output, "");
+}
+
 } // namespace
 } // namespace ento
 } // namespace clang
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<CHECKER *>(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 <typename SuperChecker,
+            typename SuperChecker::SubCheckerTy SubCheckerKind>
+  SuperChecker *registerSubChecker() {
+    SuperChecker *Super = getChecker<SuperChecker>();
+    Super->template addSubChecker<SubCheckerKind>(CurrentCheckerName);
+    return Super;
+  }
+
 //===----------------------------------------------------------------------===//
 // Functions for running checkers for AST traversing.
 //===----------------------------------------------------------------------===//
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 <typename SubCheckerEnumTy>
+class SuperCheckerBase : public CheckerBase {
+  static_assert(std::is_enum<SubCheckerEnumTy>::value,
+                "SuperCheckers are required to provide an enum to keep track "
+                "of their subcheckers!");
+
+  using SubCheckerPair = std::pair<SubCheckerEnumTy, const CheckerBase>;
+  using SubCheckerVector = typename llvm::SmallVector<SubCheckerPair, 4>;
+
+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 <SubCheckerEnumTy SubCheckerKind>
+  void addSubChecker(CheckerNameRef Name) {
+    assert(getSubCheckerPos(SubCheckerKind) == Subcheckers.end() &&
+           "This subchecker was already added to the superchecker!");
+    Subcheckers.emplace_back(SubCheckerKind, CheckerBase(Name));
+  }
+
+  template <SubCheckerEnumTy SubCheckerKind>
+  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 <typename CHECK1, typename... CHECKs>
-class Checker : public CHECK1, public CHECKs..., public CheckerBase {
+namespace checker_detail {
+template <typename BaseTy, typename CHECK1, typename... CHECKs>
+class CheckerImpl : public CHECK1, public CHECKs..., public BaseTy {
 public:
   template <typename CHECKER>
   static void _register(CHECKER *checker, CheckerManager &mgr) {
     CHECK1::_register(checker, mgr);
-    Checker<CHECKs...>::_register(checker, mgr);
+    CheckerImpl<BaseTy, CHECKs...>::_register(checker, mgr);
   }
 };
 
-template <typename CHECK1>
-class Checker<CHECK1> : public CHECK1, public CheckerBase {
+template <typename BaseTy, typename CHECK1>
+class CheckerImpl<BaseTy, CHECK1> : public CHECK1, public BaseTy {
 public:
   template <typename CHECKER>
   static void _register(CHECKER *checker, CheckerManager &mgr) {
     CHECK1::_register(checker, mgr);
   }
 };
+} // end of namespace checker_detail
+
+template <typename... CHECKs>
+using Checker =
+    typename checker_detail::CheckerImpl<CheckerBase, CHECKs...>;
+
+template <typename SubCheckerEnumTy, typename... CHECKs>
+using SuperChecker =
+    typename checker_detail::CheckerImpl<SuperCheckerBase<SubCheckerEnumTy>,
+                                         CHECKs...>;
 
 template <typename EVENT>
 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
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to