[clang] [NFC][analyzer] Use `CheckerBase::getName` in checker option handling (PR #131612)

via cfe-commits cfe-commits at lists.llvm.org
Mon Mar 17 06:24:26 PDT 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang-static-analyzer-1

Author: DonĂ¡t Nagy (NagyDonat)

<details>
<summary>Changes</summary>

The virtual method `ProgramPointTag::getTagDescription` had two very distinct use cases:
- It is printed in the DOT graph visualization of the exploded graph (that is, a debug printout).
- The checker option handling code used it to query the name of a checker, which relied on the coincidence that in `CheckerBase` this method is defined to be equivalent with `getName()`.

This commit switches to using `getName` in the second use case, because this way we will be able to properly support checkers that have multiple (separately named) parts.

The method `reportInvalidCheckerOptionName` is extended with an additional overload that allows specifying the `CheckerPartIdx`. The methods `getChecker*Option` could be extended analogously in the future, but they are just convenience wrappers around the variants that directly take `StringRef CheckerName`, so I'll only do this extension if it's needed.

---
Full diff: https://github.com/llvm/llvm-project/pull/131612.diff


6 Files Affected:

- (modified) clang/include/clang/Analysis/ProgramPoint.h (+3) 
- (modified) clang/include/clang/StaticAnalyzer/Core/Checker.h (+5-12) 
- (modified) clang/include/clang/StaticAnalyzer/Core/CheckerManager.h (+7) 
- (modified) clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp (+3-6) 
- (modified) clang/lib/StaticAnalyzer/Core/CheckerManager.cpp (+2-2) 
- (modified) clang/unittests/StaticAnalyzer/AnalyzerOptionsTest.cpp (+6-31) 


``````````diff
diff --git a/clang/include/clang/Analysis/ProgramPoint.h b/clang/include/clang/Analysis/ProgramPoint.h
index 1df1f1cb892e4..c40aa3d8ffb72 100644
--- a/clang/include/clang/Analysis/ProgramPoint.h
+++ b/clang/include/clang/Analysis/ProgramPoint.h
@@ -39,6 +39,9 @@ class ProgramPointTag {
 public:
   ProgramPointTag(void *tagKind = nullptr) : TagKind(tagKind) {}
   virtual ~ProgramPointTag();
+
+  /// The description of this program point which will be displayed when the
+  /// ExplodedGraph is dumped in DOT format for debugging.
   virtual StringRef getTagDescription() const = 0;
 
   /// Used to implement 'isKind' in subclasses.
diff --git a/clang/include/clang/StaticAnalyzer/Core/Checker.h b/clang/include/clang/StaticAnalyzer/Core/Checker.h
index df29be8ba3f62..a54c5bee612f6 100644
--- a/clang/include/clang/StaticAnalyzer/Core/Checker.h
+++ b/clang/include/clang/StaticAnalyzer/Core/Checker.h
@@ -516,18 +516,11 @@ class CheckerBase : public ProgramPointTag {
   }
 
   StringRef getTagDescription() const override {
-    // This method inherited from `ProgramPointTag` has two unrelated roles:
-    // (1) The analyzer option handling logic uses this method to query the
-    //     name of a checker.
-    // (2) When the `ExplodedGraph` is dumped in DOT format for debugging,
-    //     this is called to attach a description to the nodes. (This happens
-    //     for all subclasses of `ProgramPointTag`, not just checkers.)
-    // FIXME: Application (1) should be aware of multiple parts within the same
-    // checker class instance, so it should directly use `getName` instead of
-    // this inherited interface which cannot support a `CheckerPartIdx`.
-    // FIXME: Ideally application (2) should return a string that describes the
-    // whole checker class, not just one of it parts. However, this is only for
-    // debugging, so returning the name of one part is probably good enough.
+    // When the ExplodedGraph is dumped for debugging (in DOT format), this
+    // method is called to attach a description to nodes created by this
+    // 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;
diff --git a/clang/include/clang/StaticAnalyzer/Core/CheckerManager.h b/clang/include/clang/StaticAnalyzer/Core/CheckerManager.h
index 44ae3ebe3d09d..03ffadd346d0b 100644
--- a/clang/include/clang/StaticAnalyzer/Core/CheckerManager.h
+++ b/clang/include/clang/StaticAnalyzer/Core/CheckerManager.h
@@ -194,6 +194,13 @@ class CheckerManager {
   /// Emits an error through a DiagnosticsEngine about an invalid user supplied
   /// checker option value.
   void reportInvalidCheckerOptionValue(const CheckerBase *C,
+                                       StringRef OptionName,
+                                       StringRef ExpectedValueDesc) const {
+    reportInvalidCheckerOptionValue(C, DefaultPart, OptionName,
+                                    ExpectedValueDesc);
+  }
+
+  void reportInvalidCheckerOptionValue(const CheckerBase *C, CheckerPartIdx Idx,
                                        StringRef OptionName,
                                        StringRef ExpectedValueDesc) const;
 
diff --git a/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp b/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
index 86ef4a5686650..3e0414d00f551 100644
--- a/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
+++ b/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
@@ -154,8 +154,7 @@ StringRef AnalyzerOptions::getCheckerStringOption(StringRef CheckerName,
 StringRef AnalyzerOptions::getCheckerStringOption(const ento::CheckerBase *C,
                                                   StringRef OptionName,
                                                   bool SearchInParents) const {
-  return getCheckerStringOption(
-                           C->getTagDescription(), OptionName, SearchInParents);
+  return getCheckerStringOption(C->getName(), OptionName, SearchInParents);
 }
 
 bool AnalyzerOptions::getCheckerBooleanOption(StringRef CheckerName,
@@ -178,8 +177,7 @@ bool AnalyzerOptions::getCheckerBooleanOption(StringRef CheckerName,
 bool AnalyzerOptions::getCheckerBooleanOption(const ento::CheckerBase *C,
                                               StringRef OptionName,
                                               bool SearchInParents) const {
-  return getCheckerBooleanOption(
-             C->getTagDescription(), OptionName, SearchInParents);
+  return getCheckerBooleanOption(C->getName(), OptionName, SearchInParents);
 }
 
 int AnalyzerOptions::getCheckerIntegerOption(StringRef CheckerName,
@@ -199,6 +197,5 @@ int AnalyzerOptions::getCheckerIntegerOption(StringRef CheckerName,
 int AnalyzerOptions::getCheckerIntegerOption(const ento::CheckerBase *C,
                                              StringRef OptionName,
                                              bool SearchInParents) const {
-  return getCheckerIntegerOption(
-                           C->getTagDescription(), OptionName, SearchInParents);
+  return getCheckerIntegerOption(C->getName(), OptionName, SearchInParents);
 }
diff --git a/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp b/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp
index 56b1e44acc1fd..7ae86f133904b 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, StringRef OptionName,
+    const CheckerBase *C, CheckerPartIdx Idx, StringRef OptionName,
     StringRef ExpectedValueDesc) const {
 
   getDiagnostics().Report(diag::err_analyzer_checker_option_invalid_input)
-      << (llvm::Twine() + C->getTagDescription() + ":" + OptionName).str()
+      << (llvm::Twine(C->getName(Idx)) + ":" + OptionName).str()
       << ExpectedValueDesc;
 }
 
diff --git a/clang/unittests/StaticAnalyzer/AnalyzerOptionsTest.cpp b/clang/unittests/StaticAnalyzer/AnalyzerOptionsTest.cpp
index aace4b991b5ec..f4871b8560522 100644
--- a/clang/unittests/StaticAnalyzer/AnalyzerOptionsTest.cpp
+++ b/clang/unittests/StaticAnalyzer/AnalyzerOptionsTest.cpp
@@ -37,26 +37,17 @@ TEST(StaticAnalyzerOptions, SearchInParentPackageTests) {
   Opts.Config["Outer.Inner:Option2"] = "true";
   Opts.Config["Outer:Option2"] = "false";
 
-  struct CheckerOneMock : CheckerBase {
-    StringRef getTagDescription() const override {
-      return "Outer.Inner.CheckerOne";
-    }
-  };
-  struct CheckerTwoMock : CheckerBase {
-    StringRef getTagDescription() const override {
-      return "Outer.Inner.CheckerTwo";
-    }
-  };
+  StringRef CheckerOneName = "Outer.Inner.CheckerOne";
+  StringRef CheckerTwoName = "Outer.Inner.CheckerTwo";
 
   // CheckerTwo one has Option specified as true. It should read true regardless
   // of search mode.
-  CheckerOneMock CheckerOne;
-  EXPECT_TRUE(Opts.getCheckerBooleanOption(&CheckerOne, "Option"));
+  EXPECT_TRUE(Opts.getCheckerBooleanOption(CheckerOneName, "Option"));
   // The package option is overridden with a checker option.
-  EXPECT_TRUE(Opts.getCheckerBooleanOption(&CheckerOne, "Option", true));
+  EXPECT_TRUE(Opts.getCheckerBooleanOption(CheckerOneName, "Option", true));
   // The Outer package option is overridden by the Inner package option. No
   // package option is specified.
-  EXPECT_TRUE(Opts.getCheckerBooleanOption(&CheckerOne, "Option2", true));
+  EXPECT_TRUE(Opts.getCheckerBooleanOption(CheckerOneName, "Option2", true));
   // No package option is specified and search in packages is turned off. We
   // should assert here, but we can't test that.
   //Opts.getCheckerBooleanOption(&CheckerOne, "Option2");
@@ -64,8 +55,7 @@ TEST(StaticAnalyzerOptions, SearchInParentPackageTests) {
 
   // Checker true has no option specified. It should get false when search in
   // parents turned on.
-  CheckerTwoMock CheckerTwo;
-  EXPECT_FALSE(Opts.getCheckerBooleanOption(&CheckerTwo, "Option", true));
+  EXPECT_FALSE(Opts.getCheckerBooleanOption(CheckerTwoName, "Option", true));
   // In any other case, we should assert, that we cannot test unfortunately.
   //Opts.getCheckerBooleanOption(&CheckerTwo, "Option");
   //Opts.getCheckerBooleanOption(&CheckerTwo, "Option");
@@ -74,21 +64,6 @@ TEST(StaticAnalyzerOptions, SearchInParentPackageTests) {
 TEST(StaticAnalyzerOptions, StringOptions) {
   AnalyzerOptions Opts;
   Opts.Config["Outer.Inner.CheckerOne:Option"] = "StringValue";
-
-  struct CheckerOneMock : CheckerBase {
-    StringRef getTagDescription() const override {
-      return "Outer.Inner.CheckerOne";
-    }
-  };
-
-  CheckerOneMock CheckerOne;
-  EXPECT_TRUE("StringValue" ==
-            Opts.getCheckerStringOption(&CheckerOne, "Option"));
-}
-
-TEST(StaticAnalyzerOptions, SubCheckerOptions) {
-  AnalyzerOptions Opts;
-  Opts.Config["Outer.Inner.CheckerOne:Option"] = "StringValue";
   EXPECT_TRUE("StringValue" == Opts.getCheckerStringOption(
         "Outer.Inner.CheckerOne", "Option"));
 }

``````````

</details>


https://github.com/llvm/llvm-project/pull/131612


More information about the cfe-commits mailing list