[clang] fe0a555 - [analyzer][NFC] Add unittest for FalsePositiveRefutationBRVisitor

Balazs Benics via cfe-commits cfe-commits at lists.llvm.org
Mon Jun 29 09:19:38 PDT 2020


Author: Balazs Benics
Date: 2020-06-29T18:18:43+02:00
New Revision: fe0a555aa3c6f37a5b5d79942215b07587893efa

URL: https://github.com/llvm/llvm-project/commit/fe0a555aa3c6f37a5b5d79942215b07587893efa
DIFF: https://github.com/llvm/llvm-project/commit/fe0a555aa3c6f37a5b5d79942215b07587893efa.diff

LOG: [analyzer][NFC] Add unittest for FalsePositiveRefutationBRVisitor

Adds the test infrastructure for testing the FalsePositiveRefutationBRVisitor.
It will be extended in the D78457 patch, which demonstrates and fixes a bug in
the visitor.

Differential Revision: https://reviews.llvm.org/D78704

Added: 
    clang/unittests/StaticAnalyzer/FalsePositiveRefutationBRVisitorTest.cpp

Modified: 
    clang/unittests/StaticAnalyzer/CMakeLists.txt
    clang/unittests/StaticAnalyzer/CheckerRegistration.h

Removed: 
    


################################################################################
diff  --git a/clang/unittests/StaticAnalyzer/CMakeLists.txt b/clang/unittests/StaticAnalyzer/CMakeLists.txt
index 564eba879f24..0e6d8763d96d 100644
--- a/clang/unittests/StaticAnalyzer/CMakeLists.txt
+++ b/clang/unittests/StaticAnalyzer/CMakeLists.txt
@@ -7,6 +7,7 @@ add_clang_unittest(StaticAnalysisTests
   AnalyzerOptionsTest.cpp
   CallDescriptionTest.cpp
   CallEventTest.cpp
+  FalsePositiveRefutationBRVisitorTest.cpp
   ParamRegionTest.cpp
   RangeSetTest.cpp
   RegisterCustomCheckersTest.cpp

diff  --git a/clang/unittests/StaticAnalyzer/CheckerRegistration.h b/clang/unittests/StaticAnalyzer/CheckerRegistration.h
index 0bbed9b7784f..e02b856e0e9c 100644
--- a/clang/unittests/StaticAnalyzer/CheckerRegistration.h
+++ b/clang/unittests/StaticAnalyzer/CheckerRegistration.h
@@ -14,6 +14,7 @@
 #include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h"
 #include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h"
 #include "clang/Tooling/Tooling.h"
+#include "gtest/gtest.h"
 
 namespace clang {
 namespace ento {
@@ -65,10 +66,21 @@ class TestAction : public ASTFrontendAction {
   }
 };
 
+inline SmallString<80> getCurrentTestNameAsFileName() {
+  const ::testing::TestInfo *Info =
+      ::testing::UnitTest::GetInstance()->current_test_info();
+
+  SmallString<80> FileName;
+  (Twine{Info->name()} + ".cc").toVector(FileName);
+  return FileName;
+}
+
 template <AddCheckerFn... Fns>
 bool runCheckerOnCode(const std::string &Code, std::string &Diags) {
+  const SmallVectorImpl<char> &FileName = getCurrentTestNameAsFileName();
   llvm::raw_string_ostream OS(Diags);
-  return tooling::runToolOnCode(std::make_unique<TestAction<Fns...>>(OS), Code);
+  return tooling::runToolOnCode(std::make_unique<TestAction<Fns...>>(OS), Code,
+                                FileName);
 }
 
 template <AddCheckerFn... Fns>
@@ -77,5 +89,22 @@ bool runCheckerOnCode(const std::string &Code) {
   return runCheckerOnCode<Fns...>(Code, Diags);
 }
 
+template <AddCheckerFn... Fns>
+bool runCheckerOnCodeWithArgs(const std::string &Code,
+                              const std::vector<std::string> &Args,
+                              std::string &Diags) {
+  const SmallVectorImpl<char> &FileName = getCurrentTestNameAsFileName();
+  llvm::raw_string_ostream OS(Diags);
+  return tooling::runToolOnCodeWithArgs(
+      std::make_unique<TestAction<Fns...>>(OS), Code, Args, FileName);
+}
+
+template <AddCheckerFn... Fns>
+bool runCheckerOnCodeWithArgs(const std::string &Code,
+                              const std::vector<std::string> &Args) {
+  std::string Diags;
+  return runCheckerOnCodeWithArgs<Fns...>(Code, Args, Diags);
+}
+
 } // namespace ento
 } // namespace clang

diff  --git a/clang/unittests/StaticAnalyzer/FalsePositiveRefutationBRVisitorTest.cpp b/clang/unittests/StaticAnalyzer/FalsePositiveRefutationBRVisitorTest.cpp
new file mode 100644
index 000000000000..ad202f844597
--- /dev/null
+++ b/clang/unittests/StaticAnalyzer/FalsePositiveRefutationBRVisitorTest.cpp
@@ -0,0 +1,175 @@
+//===- unittests/StaticAnalyzer/FalsePositiveRefutationBRVisitorTest.cpp --===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CheckerRegistration.h"
+#include "Reusables.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h"
+#include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h"
+#include "llvm/Config/config.h"
+#include "gtest/gtest.h"
+
+// FIXME: Use GTEST_SKIP() instead if GTest is updated to version 1.10.0
+#ifdef LLVM_WITH_Z3
+#define SKIP_WITHOUT_Z3
+#else
+#define SKIP_WITHOUT_Z3 return
+#endif
+
+namespace clang {
+namespace ento {
+namespace {
+
+class FalsePositiveGenerator : public Checker<eval::Call> {
+  using Self = FalsePositiveGenerator;
+  const BuiltinBug FalsePositiveGeneratorBug{this, "FalsePositiveGenerator"};
+  using HandlerFn = bool (Self::*)(const CallEvent &Call,
+                                   CheckerContext &) const;
+  CallDescriptionMap<HandlerFn> Callbacks = {
+      {{"reachedWithContradiction", 0}, &Self::reachedWithContradiction},
+      {{"reachedWithNoContradiction", 0}, &Self::reachedWithNoContradiction},
+      {{"reportIfCanBeTrue", 1}, &Self::reportIfCanBeTrue},
+  };
+
+  bool report(CheckerContext &C, ProgramStateRef State,
+              StringRef Description) const {
+    ExplodedNode *Node = C.generateNonFatalErrorNode(State);
+    if (!Node)
+      return false;
+
+    auto Report = std::make_unique<PathSensitiveBugReport>(
+        FalsePositiveGeneratorBug, Description, Node);
+    C.emitReport(std::move(Report));
+    return true;
+  }
+
+  bool reachedWithNoContradiction(const CallEvent &, CheckerContext &C) const {
+    return report(C, C.getState(), "REACHED_WITH_NO_CONTRADICTION");
+  }
+
+  bool reachedWithContradiction(const CallEvent &, CheckerContext &C) const {
+    return report(C, C.getState(), "REACHED_WITH_CONTRADICTION");
+  }
+
+  // Similar to ExprInspectionChecker::analyzerEval except it emits warning only
+  // if the argument can be true. The report emits the report in the state where
+  // the assertion true.
+  bool reportIfCanBeTrue(const CallEvent &Call, CheckerContext &C) const {
+    // A specific instantiation of an inlined function may have more constrained
+    // values than can generally be assumed. Skip the check.
+    if (C.getPredecessor()->getLocationContext()->getStackFrame()->getParent())
+      return false;
+
+    SVal AssertionVal = Call.getArgSVal(0);
+    if (AssertionVal.isUndef())
+      return false;
+
+    ProgramStateRef State = C.getPredecessor()->getState();
+    ProgramStateRef StTrue;
+    std::tie(StTrue, std::ignore) =
+        State->assume(AssertionVal.castAs<DefinedOrUnknownSVal>());
+    if (StTrue)
+      return report(C, StTrue, "CAN_BE_TRUE");
+    return false;
+  }
+
+public:
+  bool evalCall(const CallEvent &Call, CheckerContext &C) const {
+    if (const HandlerFn *Callback = Callbacks.lookup(Call))
+      return (this->*(*Callback))(Call, C);
+    return false;
+  }
+};
+
+void addFalsePositiveGenerator(AnalysisASTConsumer &AnalysisConsumer,
+                               AnalyzerOptions &AnOpts) {
+  AnOpts.CheckersAndPackages = {{"test.FalsePositiveGenerator", true},
+                                {"debug.ViewExplodedGraph", false}};
+  AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) {
+    Registry.addChecker<FalsePositiveGenerator>(
+        "test.FalsePositiveGenerator", "EmptyDescription", "EmptyDocsUri");
+  });
+}
+
+// C++20 use constexpr below.
+const std::vector<std::string> LazyAssumeArgs{
+    "-Xclang", "-analyzer-config", "-Xclang", "eagerly-assume=false"};
+const std::vector<std::string> LazyAssumeAndCrossCheckArgs{
+    "-Xclang", "-analyzer-config", "-Xclang", "eagerly-assume=false",
+    "-Xclang", "-analyzer-config", "-Xclang", "crosscheck-with-z3=true"};
+
+TEST(FalsePositiveRefutationBRVisitor, UnSatInTheMiddleNoReport) {
+  SKIP_WITHOUT_Z3;
+  constexpr auto Code = R"(
+     void reachedWithContradiction();
+     void reachedWithNoContradiction();
+     void test(int x, int y) {
+       if (x * y == 0)
+         return;
+       reachedWithNoContradiction();
+       if (x == 0) {
+         reachedWithContradiction();
+         // x * y != 0  =>  x != 0 && y != 0  => contradict with x == 0
+       }
+     })";
+
+  std::string Diags;
+  EXPECT_TRUE(runCheckerOnCodeWithArgs<addFalsePositiveGenerator>(
+      Code, LazyAssumeAndCrossCheckArgs, Diags));
+  EXPECT_EQ(Diags,
+            "test.FalsePositiveGenerator:REACHED_WITH_NO_CONTRADICTION\n");
+  // Single warning. The second report was invalidated by the visitor.
+
+  // Without enabling the crosscheck-with-z3 both reports are displayed.
+  std::string Diags2;
+  EXPECT_TRUE(runCheckerOnCodeWithArgs<addFalsePositiveGenerator>(
+      Code, LazyAssumeArgs, Diags2));
+  EXPECT_EQ(Diags2,
+            "test.FalsePositiveGenerator:REACHED_WITH_NO_CONTRADICTION\n"
+            "test.FalsePositiveGenerator:REACHED_WITH_CONTRADICTION\n");
+}
+
+TEST(FalsePositiveRefutationBRVisitor, UnSatAtErrorNodeWithNewSymbolNoReport) {
+  SKIP_WITHOUT_Z3;
+  constexpr auto Code = R"(
+    void reportIfCanBeTrue(bool);
+    void reachedWithNoContradiction();
+    void test(int x, int y) {
+      if (x * y == 0)
+       return;
+      // We know that 'x * y': {[MIN,-1], [1,MAX]}
+      reachedWithNoContradiction();
+      reportIfCanBeTrue(x == 0); // contradiction
+      // The function introduces the 'x == 0' constraint in the ErrorNode which
+      // leads to contradiction with the constraint of 'x * y'.
+      // Note that the new constraint was bound to a new symbol 'x'.
+    })";
+  std::string Diags;
+  EXPECT_TRUE(runCheckerOnCodeWithArgs<addFalsePositiveGenerator>(
+      Code, LazyAssumeAndCrossCheckArgs, Diags));
+  EXPECT_EQ(Diags,
+            "test.FalsePositiveGenerator:REACHED_WITH_NO_CONTRADICTION\n");
+  // Single warning. The second report was invalidated by the visitor.
+
+  // Without enabling the crosscheck-with-z3 both reports are displayed.
+  std::string Diags2;
+  EXPECT_TRUE(runCheckerOnCodeWithArgs<addFalsePositiveGenerator>(
+      Code, LazyAssumeArgs, Diags2));
+  EXPECT_EQ(Diags2,
+            "test.FalsePositiveGenerator:REACHED_WITH_NO_CONTRADICTION\n"
+            "test.FalsePositiveGenerator:CAN_BE_TRUE\n");
+}
+
+} // namespace
+} // namespace ento
+} // namespace clang


        


More information about the cfe-commits mailing list