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
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits