#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallVector.h"
-#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Casting.h"
// Implementation of FalsePositiveRefutationBRVisitor.
//===----------------------------------------------------------------------===//
-#define DEBUG_TYPE "FalsePositiveRefutationBRVisitor"
-STATISTIC(CrosscheckedBugReports,
- "The # of bug reports which were checked for infeasible constraints");
-STATISTIC(CrosscheckInvalidatedBugReports,
- "The # of bug reports invalidated due to infeasible constraints");
-
FalsePositiveRefutationBRVisitor::FalsePositiveRefutationBRVisitor()
: Constraints(ConstraintRangeTy::Factory().getEmptyMap()) {}
void FalsePositiveRefutationBRVisitor::finalizeVisitor(
BugReporterContext &BRC, const ExplodedNode *EndPathNode,
PathSensitiveBugReport &BR) {
- ++CrosscheckedBugReports;
// Collect new constraints
VisitNode(EndPathNode, BRC, BR);
if (!isSat.hasValue())
return;
- if (!isSat.getValue()) {
- ++CrosscheckInvalidatedBugReports;
+ if (!isSat.getValue())
BR.markInvalid("Infeasible constraints", EndPathNode->getLocationContext());
- }
}
PathDiagnosticPieceRef FalsePositiveRefutationBRVisitor::VisitNode(
#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 {
}
};
-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,
- FileName);
+ return tooling::runToolOnCode(std::make_unique<TestAction<Fns...>>(OS), Code);
}
template <AddCheckerFn... Fns>
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
+++ /dev/null
-//===- 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
-#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 &) 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