steakhal created this revision.
steakhal added reviewers: NoQ, xazax.hun, rnkovacs, Szelethus, 
baloghadamsoftware, mikhail.ramalho.
Herald added subscribers: cfe-commits, ASDenysPetrov, martong, Charusso, 
dkrupp, donat.nagy, a.sidorin, szepet, whisperity, mgorny.
Herald added a project: clang.
steakhal added a child revision: D78457: [analyzer][Z3-refutation] Fix 
refutation BugReporterVisitor bug and refactor.
steakhal updated this revision to Diff 259531.
steakhal added a comment.

Upload the right diff.


Adds the test infrastructure for testing the `FalsePositiveRefutationBRVisitor`.

It will be extended later, to demonstrate a bug in the visitor.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D78704

Files:
  clang/unittests/StaticAnalyzer/CMakeLists.txt
  clang/unittests/StaticAnalyzer/CheckerRegistration.h
  clang/unittests/StaticAnalyzer/FalsePositiveRefutationBRVisitorTest.cpp

Index: clang/unittests/StaticAnalyzer/FalsePositiveRefutationBRVisitorTest.cpp
===================================================================
--- /dev/null
+++ clang/unittests/StaticAnalyzer/FalsePositiveRefutationBRVisitorTest.cpp
@@ -0,0 +1,171 @@
+//===- 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"
+
+#define SKIP_WITHOUT_Z3                                                        \
+  do                                                                           \
+    if (!LLVM_WITH_Z3)                                                         \
+      return;                                                                  \
+  while (0)
+
+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 &,
+                                   ProgramStateRef State) const;
+  CallDescriptionMap<HandlerFn> Callbacks = {
+      {{"reachedWithContradiction", 0}, &Self::reachedWithContradiction},
+      {{"reachedWithNoContradiction", 0}, &Self::reachedWithNoContradiction},
+      {{"reportIfCanBeZero", 1}, &Self::reportIfCanBeZero},
+  };
+
+  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,
+                                  ProgramStateRef State) const {
+    return report(C, State, "REACHED_WITH_NO_CONTRADICTION");
+  }
+
+  bool reachedWithContradiction(const CallEvent &, CheckerContext &C,
+                                ProgramStateRef State) const {
+    return report(C, State, "REACHED_WITH_CONTRADICTION");
+  }
+
+  bool reportIfCanBeZero(const CallEvent &Call, CheckerContext &C,
+                         ProgramStateRef State) const {
+    ProgramStateRef ArgIsZero, ArgIsNotZero;
+    std::tie(ArgIsZero, ArgIsNotZero) = assumeArgEqZero(Call, C, State);
+
+    if (ArgIsZero)
+      return report(C, ArgIsZero, "ZERO_STATE_SHOULD_NOT_EXIST");
+    return false;
+  }
+
+  std::pair<ProgramStateRef, ProgramStateRef>
+  assumeArgEqZero(const CallEvent &Call, CheckerContext &C,
+                  ProgramStateRef State) const {
+    SValBuilder &SVB = C.getSValBuilder();
+    const QualType ArgType = Call.getArgExpr(0)->getType();
+    const DefinedOrUnknownSVal Zero = SVB.makeZeroVal(ArgType);
+    const SVal ArgEqZero = SVB.evalEQ(State, Call.getArgSVal(0), Zero);
+    return State->assume(ArgEqZero.castAs<DefinedOrUnknownSVal>());
+  }
+
+public:
+  bool evalCall(const CallEvent &Call, CheckerContext &C) const {
+    if (const HandlerFn *Callback = Callbacks.lookup(Call))
+      return (this->*(*Callback))(Call, C, C.getState());
+    return false;
+  }
+};
+
+void addFalsePositiveGenerator(AnalysisASTConsumer &AnalysisConsumer,
+                               AnalyzerOptions &AnOpts) {
+  AnOpts.CheckersAndPackages = {{"test.FalsePositiveGenerator", true},
+                                {"debug.ViewExplodedGraph", false},
+                                {"debug.ExprInspection", false}};
+  AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) {
+    Registry.addChecker<FalsePositiveGenerator>(
+        "test.FalsePositiveGenerator", "EmptyDescription", "EmptyDocsUri");
+  });
+}
+
+const std::vector<std::string> CrossCheckArgs{
+    "-Xclang", "-analyzer-config", "-Xclang", "crosscheck-with-z3=true"};
+
+bool runFalsePositiveGeneratorOnCode(
+    const std::string &Code, std::string &Diags,
+    const std::vector<std::string> &Args = {}) {
+  const ::testing::TestInfo *Info =
+      ::testing::UnitTest::GetInstance()->current_test_info();
+  const std::string FileName = (Twine{Info->name()} + ".cc").str();
+
+  llvm::raw_string_ostream OS(Diags);
+  return tooling::runToolOnCodeWithArgs(
+      std::make_unique<TestAction<addFalsePositiveGenerator>>(OS), Code, Args,
+      FileName);
+}
+
+TEST(FalsePositiveRefutationBRVisitor, UnSatInTheMiddleNoReport) {
+  SKIP_WITHOUT_Z3;
+  std::string Diags;
+  constexpr auto Code = R"(
+     void reachedWithContradiction();
+     void test(int x, int y) {
+       int z = x * y;
+       if (z == 0)
+         return;
+       if (x == 0)
+         reachedWithContradiction(); // contradiction
+       // z != 0  =>  x != 0 && y != 0  => contradict with x == 0
+     })";
+  EXPECT_TRUE(runFalsePositiveGeneratorOnCode(Code, Diags, CrossCheckArgs));
+  EXPECT_EQ(Diags, ""); // no-warning, the report was invalidated by the visitor
+
+  // Without enabling the crosscheck-with-z3 the BugPath is not invalidated.
+  std::string Diags2;
+  EXPECT_TRUE(runFalsePositiveGeneratorOnCode(Code, Diags2));
+  EXPECT_EQ(Diags2, "test.FalsePositiveGenerator:REACHED_WITH_CONTRADICTION\n");
+}
+
+TEST(FalsePositiveRefutationBRVisitor, UnSatAtErrorNodeNoReport) {
+  SKIP_WITHOUT_Z3;
+  std::string Diags;
+  constexpr auto Code = R"(
+    void reportIfCanBeZero(int);
+    void test(int x, int y) {
+      int z = x * y;
+      if (z == 0)
+       return;
+      reportIfCanBeZero(x); // contradiction
+      // The new assumption at the ErrorNode (x == 0) contradict with z != 0.
+    })";
+
+  // We have some constraints without contradiction.
+  // In the error node we tighten a constraint which now contradicts with the
+  // constraints on the BugPath. The visitor should use the stricter constraint
+  // on that symbol on crosschecking and invalidate the bugreport.
+  EXPECT_TRUE(runFalsePositiveGeneratorOnCode(Code, Diags, CrossCheckArgs));
+  EXPECT_EQ(Diags, ""); // no-warning, the report was invalidated by the visitor
+
+  // Without enabling the crosscheck-with-z3 the BugPath is not invalidated.
+  std::string Diags2;
+  EXPECT_TRUE(runFalsePositiveGeneratorOnCode(Code, Diags2));
+  EXPECT_EQ(Diags2,
+            "test.FalsePositiveGenerator:ZERO_STATE_SHOULD_NOT_EXIST\n");
+}
+
+} // namespace
+} // namespace ento
+} // namespace clang
Index: clang/unittests/StaticAnalyzer/CheckerRegistration.h
===================================================================
--- clang/unittests/StaticAnalyzer/CheckerRegistration.h
+++ clang/unittests/StaticAnalyzer/CheckerRegistration.h
@@ -77,5 +77,14 @@
   return runCheckerOnCode<Fns...>(Code, Diags);
 }
 
+template <AddCheckerFn... Fns>
+bool runCheckerOnCodeWithArgs(const std::string &Code, std::string &Diags,
+                              const std::vector<std::string> &Args,
+                              const Twine &FileName = "input.cc") {
+  llvm::raw_string_ostream OS(Diags);
+  return tooling::runToolOnCodeWithArgs(
+      std::make_unique<TestAction<Fns...>>(OS), Code, Args, FileName);
+}
+
 } // namespace ento
 } // namespace clang
Index: clang/unittests/StaticAnalyzer/CMakeLists.txt
===================================================================
--- clang/unittests/StaticAnalyzer/CMakeLists.txt
+++ clang/unittests/StaticAnalyzer/CMakeLists.txt
@@ -6,6 +6,7 @@
 add_clang_unittest(StaticAnalysisTests
   AnalyzerOptionsTest.cpp
   CallDescriptionTest.cpp
+  FalsePositiveRefutationBRVisitorTest.cpp
   StoreTest.cpp
   RegisterCustomCheckersTest.cpp
   SymbolReaperTest.cpp
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to