[Analyzer] Only add container note tags to the operations of the affected container

If an error happens which is related to a container the Container
Modeling checker adds note tags to all the container operations along
the bug path. This may be disturbing if there are other containers
beside the one which is affected by the bug. This patch restricts the
note tags to only the affected container and adjust the debug checkers
to be able to test this change.

Differential Revision: https://reviews.llvm.org/D75514
This commit is contained in:
Adam Balogh 2020-01-30 13:54:52 +01:00
parent 27c201aa1d
commit 1a27d63a88
4 changed files with 41 additions and 19 deletions

View File

@ -721,6 +721,9 @@ const NoteTag *ContainerModeling::getChangeTag(CheckerContext &C,
return C.getNoteTag(
[Text, Name, ContReg](PathSensitiveBugReport &BR) -> std::string {
if (!BR.isInteresting(ContReg))
return "";
SmallString<256> Msg;
llvm::raw_svector_ostream Out(Msg);
Out << "Container " << (!Name.empty() ? ("'" + Name.str() + "' ") : "" )

View File

@ -92,7 +92,19 @@ void DebugContainerModeling::analyzerContainerDataField(const CallExpr *CE,
if (Field) {
State = State->BindExpr(CE, C.getLocationContext(),
nonloc::SymbolVal(Field));
C.addTransition(State);
// Progpagate interestingness from the container's data (marked
// interesting by an `ExprInspection` debug call to the container
// itself.
const NoteTag *InterestingTag =
C.getNoteTag(
[Cont, Field](PathSensitiveBugReport &BR) -> std::string {
if (BR.isInteresting(Field)) {
BR.markInteresting(Cont);
}
return "";
});
C.addTransition(State, InterestingTag);
return;
}
}

View File

@ -52,9 +52,12 @@ class ExprInspectionChecker : public Checker<eval::Call, check::DeadSymbols,
typedef void (ExprInspectionChecker::*FnCheck)(const CallExpr *,
CheckerContext &C) const;
ExplodedNode *reportBug(llvm::StringRef Msg, CheckerContext &C) const;
// Optional parameter `ExprVal` for expression value to be marked interesting.
ExplodedNode *reportBug(llvm::StringRef Msg, CheckerContext &C,
Optional<SVal> ExprVal = None) const;
ExplodedNode *reportBug(llvm::StringRef Msg, BugReporter &BR,
ExplodedNode *N) const;
ExplodedNode *N,
Optional<SVal> ExprVal = None) const;
public:
bool evalCall(const CallEvent &Call, CheckerContext &C) const;
@ -144,22 +147,28 @@ static const char *getArgumentValueString(const CallExpr *CE,
}
ExplodedNode *ExprInspectionChecker::reportBug(llvm::StringRef Msg,
CheckerContext &C) const {
CheckerContext &C,
Optional<SVal> ExprVal) const {
ExplodedNode *N = C.generateNonFatalErrorNode();
reportBug(Msg, C.getBugReporter(), N);
reportBug(Msg, C.getBugReporter(), N, ExprVal);
return N;
}
ExplodedNode *ExprInspectionChecker::reportBug(llvm::StringRef Msg,
BugReporter &BR,
ExplodedNode *N) const {
ExplodedNode *N,
Optional<SVal> ExprVal) const {
if (!N)
return nullptr;
if (!BT)
BT.reset(new BugType(this, "Checking analyzer assumptions", "debug"));
BR.emitReport(std::make_unique<PathSensitiveBugReport>(*BT, Msg, N));
auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N);
if (ExprVal) {
R->markInteresting(*ExprVal);
}
BR.emitReport(std::move(R));
return N;
}
@ -406,7 +415,8 @@ void ExprInspectionChecker::analyzerExpress(const CallExpr *CE,
return;
}
SymbolRef Sym = C.getSVal(CE->getArg(0)).getAsSymbol();
SVal ArgVal = C.getSVal(CE->getArg(0));
SymbolRef Sym = ArgVal.getAsSymbol();
if (!Sym) {
reportBug("Not a symbol", C);
return;
@ -419,7 +429,7 @@ void ExprInspectionChecker::analyzerExpress(const CallExpr *CE,
return;
}
reportBug(*Str, C);
reportBug(*Str, C, ArgVal);
}
void ExprInspectionChecker::analyzerIsTainted(const CallExpr *CE,

View File

@ -76,8 +76,7 @@ void push_back(std::vector<int> &V, int n) {
clang_analyzer_denote(clang_analyzer_container_begin(V), "$V.begin()");
clang_analyzer_denote(clang_analyzer_container_end(V), "$V.end()");
V.push_back(n); // expected-note{{Container 'V' extended to the back by 1 position}}
// expected-note@-1{{Container 'V' extended to the back by 1 position}}
V.push_back(n); // expected-note 2{{Container 'V' extended to the back by 1 position}}
clang_analyzer_express(clang_analyzer_container_begin(V)); // expected-warning{{$V.begin()}}
// expected-note@-1{{$V.begin()}}
@ -99,7 +98,6 @@ void emplace_back(std::vector<int> &V, int n) {
V.emplace_back(n); // expected-note 2{{Container 'V' extended to the back by 1 position}}
clang_analyzer_express(clang_analyzer_container_begin(V)); // expected-warning{{$V.begin()}}
// expected-note@-1{{$V.begin()}}
clang_analyzer_express(clang_analyzer_container_end(V)); // expected-warning{{$V.end() + 1}}
@ -217,10 +215,10 @@ void push_back1(std::vector<int> &V1, std::vector<int> &V2, int n) {
clang_analyzer_denote(clang_analyzer_container_begin(V1), "$V1.begin()");
V2.push_back(n); // expected-note{{Container 'V2' extended to the back by 1 position}} FIXME: This note should not appear since `V2` is not affected in the "bug"
V2.push_back(n); // no-note
clang_analyzer_express(clang_analyzer_container_begin(V1)); // expected-warning{{$V1.begin()}}
// expected-note@-1{{$V1.begin()}}
// expected-note@-1{{$V1.begin()}}
}
void push_back2(std::vector<int> &V1, std::vector<int> &V2, int n) {
@ -232,15 +230,14 @@ void push_back2(std::vector<int> &V1, std::vector<int> &V2, int n) {
clang_analyzer_denote(clang_analyzer_container_begin(V1), "$V1.begin()");
clang_analyzer_denote(clang_analyzer_container_begin(V2), "$V2.begin()");
V1.push_back(n); // expected-note 2{{Container 'V1' extended to the back by 1 position}}
// FIXME: This should appear only once since there is only
// one "bug" where `V1` is affected
V1.push_back(n); // expected-note{{Container 'V1' extended to the back by 1 position}}
// Only once!
clang_analyzer_express(clang_analyzer_container_begin(V1)); // expected-warning{{$V1.begin()}}
// expected-note@-1{{$V1.begin()}}
// expected-note@-1{{$V1.begin()}}
clang_analyzer_express(clang_analyzer_container_begin(V2)); // expected-warning{{$V2.begin()}}
// expected-note@-1{{$V2.begin()}}
// expected-note@-1{{$V2.begin()}}
}
/// Print Container Data as Part of the Program State