Skip to content

Commit 9b5c9c4

Browse files
author
Balazs Benics
committed
[analyzer] Dump checker name if multiple checkers evaluate the same call
Previously, if accidentally multiple checkers `eval::Call`-ed the same `CallEvent`, in debug builds the analyzer detected this and crashed with the message stating this. Unfortunately, the message did not state the offending checkers violating this invariant. This revision addresses this by printing a more descriptive message before aborting. Reviewed By: martong Differential Revision: https://reviews.llvm.org/D112889
1 parent 6a5e08c commit 9b5c9c4

File tree

4 files changed

+81
-6
lines changed

4 files changed

+81
-6
lines changed

clang/lib/StaticAnalyzer/Core/CallEvent.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,6 @@ void CallEvent::dump(raw_ostream &Out) const {
411411
ASTContext &Ctx = getState()->getStateManager().getContext();
412412
if (const Expr *E = getOriginExpr()) {
413413
E->printPretty(Out, nullptr, Ctx.getPrintingPolicy());
414-
Out << "\n";
415414
return;
416415
}
417416

clang/lib/StaticAnalyzer/Core/CheckerManager.cpp

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "llvm/ADT/SmallVector.h"
2727
#include "llvm/Support/Casting.h"
2828
#include "llvm/Support/ErrorHandling.h"
29+
#include "llvm/Support/FormatVariadic.h"
2930
#include <cassert>
3031
#include <vector>
3132

@@ -655,7 +656,7 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst,
655656
ExprEngine &Eng,
656657
const EvalCallOptions &CallOpts) {
657658
for (auto *const Pred : Src) {
658-
bool anyEvaluated = false;
659+
Optional<CheckerNameRef> evaluatorChecker;
659660

660661
ExplodedNodeSet checkDst;
661662
NodeBuilder B(Pred, checkDst, Eng.getBuilderContext());
@@ -674,10 +675,26 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst,
674675
CheckerContext C(B, Eng, Pred, L);
675676
evaluated = EvalCallChecker(Call, C);
676677
}
677-
assert(!(evaluated && anyEvaluated)
678-
&& "There are more than one checkers evaluating the call");
678+
#ifndef NDEBUG
679+
if (evaluated && evaluatorChecker) {
680+
const auto toString = [](const CallEvent &Call) -> std::string {
681+
std::string Buf;
682+
llvm::raw_string_ostream OS(Buf);
683+
Call.dump(OS);
684+
OS.flush();
685+
return Buf;
686+
};
687+
std::string AssertionMessage = llvm::formatv(
688+
"The '{0}' call has been already evaluated by the {1} checker, "
689+
"while the {2} checker also tried to evaluate the same call. At "
690+
"most one checker supposed to evaluate a call.",
691+
toString(Call), evaluatorChecker->getName(),
692+
EvalCallChecker.Checker->getCheckerName());
693+
llvm_unreachable(AssertionMessage.c_str());
694+
}
695+
#endif
679696
if (evaluated) {
680-
anyEvaluated = true;
697+
evaluatorChecker = EvalCallChecker.Checker->getCheckerName();
681698
Dst.insert(checkDst);
682699
#ifdef NDEBUG
683700
break; // on release don't check that no other checker also evals.
@@ -686,7 +703,7 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst,
686703
}
687704

688705
// If none of the checkers evaluated the call, ask ExprEngine to handle it.
689-
if (!anyEvaluated) {
706+
if (!evaluatorChecker) {
690707
NodeBuilder B(Pred, Dst, Eng.getBuilderContext());
691708
Eng.defaultEvalCall(B, Pred, Call, CallOpts);
692709
}

clang/unittests/StaticAnalyzer/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ add_clang_unittest(StaticAnalysisTests
88
BugReportInterestingnessTest.cpp
99
CallDescriptionTest.cpp
1010
CallEventTest.cpp
11+
ConflictingEvalCallsTest.cpp
1112
FalsePositiveRefutationBRVisitorTest.cpp
1213
NoStateChangeFuncVisitorTest.cpp
1314
ParamRegionTest.cpp
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "CheckerRegistration.h"
10+
#include "clang/StaticAnalyzer/Core/Checker.h"
11+
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
12+
#include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h"
13+
#include "gtest/gtest.h"
14+
15+
using namespace clang;
16+
using namespace ento;
17+
18+
namespace {
19+
class EvalCallBase : public Checker<eval::Call> {
20+
const CallDescription Foo = {"foo", 0};
21+
22+
public:
23+
bool evalCall(const CallEvent &Call, CheckerContext &C) const {
24+
return Call.isCalled(Foo);
25+
}
26+
};
27+
28+
class EvalCallFoo1 : public EvalCallBase {};
29+
class EvalCallFoo2 : public EvalCallBase {};
30+
void addEvalFooCheckers(AnalysisASTConsumer &AnalysisConsumer,
31+
AnalyzerOptions &AnOpts) {
32+
AnOpts.CheckersAndPackages = {{"test.EvalFoo1", true},
33+
{"test.EvalFoo2", true}};
34+
AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) {
35+
Registry.addChecker<EvalCallFoo1>("test.EvalFoo1", "EmptyDescription",
36+
"EmptyDocsUri");
37+
Registry.addChecker<EvalCallFoo2>("test.EvalFoo2", "EmptyDescription",
38+
"EmptyDocsUri");
39+
});
40+
}
41+
} // namespace
42+
43+
TEST(EvalCall, DetectConflictingEvalCalls) {
44+
#ifdef NDEBUG
45+
GTEST_SKIP() << "This test is only available for debug builds.";
46+
#endif
47+
const std::string Code = R"code(
48+
void foo();
49+
void top() {
50+
foo(); // crash
51+
}
52+
)code";
53+
constexpr auto Regex =
54+
"The 'foo\\(\\)' call has been already evaluated by the test\\.EvalFoo1 "
55+
"checker, while the test\\.EvalFoo2 checker also tried to evaluate the "
56+
"same call\\. At most one checker supposed to evaluate a call\\.";
57+
ASSERT_DEATH(runCheckerOnCode<addEvalFooCheckers>(Code), Regex);
58+
}

0 commit comments

Comments
 (0)