
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
151 lines
4.9 KiB
C++
151 lines
4.9 KiB
C++
//==-- DebugContainerModeling.cpp ---------------------------------*- C++ -*--//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Defines a checker for debugging iterator modeling.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.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 "Iterator.h"
|
|
|
|
using namespace clang;
|
|
using namespace ento;
|
|
using namespace iterator;
|
|
|
|
namespace {
|
|
|
|
class DebugContainerModeling
|
|
: public Checker<eval::Call> {
|
|
|
|
std::unique_ptr<BugType> DebugMsgBugType;
|
|
|
|
template <typename Getter>
|
|
void analyzerContainerDataField(const CallExpr *CE, CheckerContext &C,
|
|
Getter get) const;
|
|
void analyzerContainerBegin(const CallExpr *CE, CheckerContext &C) const;
|
|
void analyzerContainerEnd(const CallExpr *CE, CheckerContext &C) const;
|
|
ExplodedNode *reportDebugMsg(llvm::StringRef Msg, CheckerContext &C) const;
|
|
|
|
typedef void (DebugContainerModeling::*FnCheck)(const CallExpr *,
|
|
CheckerContext &) const;
|
|
|
|
CallDescriptionMap<FnCheck> Callbacks = {
|
|
{{0, "clang_analyzer_container_begin", 1},
|
|
&DebugContainerModeling::analyzerContainerBegin},
|
|
{{0, "clang_analyzer_container_end", 1},
|
|
&DebugContainerModeling::analyzerContainerEnd},
|
|
};
|
|
|
|
public:
|
|
DebugContainerModeling();
|
|
|
|
bool evalCall(const CallEvent &Call, CheckerContext &C) const;
|
|
};
|
|
|
|
} //namespace
|
|
|
|
DebugContainerModeling::DebugContainerModeling() {
|
|
DebugMsgBugType.reset(
|
|
new BugType(this, "Checking analyzer assumptions", "debug",
|
|
/*SuppressOnSink=*/true));
|
|
}
|
|
|
|
bool DebugContainerModeling::evalCall(const CallEvent &Call,
|
|
CheckerContext &C) const {
|
|
const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
|
|
if (!CE)
|
|
return false;
|
|
|
|
const FnCheck *Handler = Callbacks.lookup(Call);
|
|
if (!Handler)
|
|
return false;
|
|
|
|
(this->**Handler)(CE, C);
|
|
return true;
|
|
}
|
|
|
|
template <typename Getter>
|
|
void DebugContainerModeling::analyzerContainerDataField(const CallExpr *CE,
|
|
CheckerContext &C,
|
|
Getter get) const {
|
|
if (CE->getNumArgs() == 0) {
|
|
reportDebugMsg("Missing container argument", C);
|
|
return;
|
|
}
|
|
|
|
auto State = C.getState();
|
|
const MemRegion *Cont = C.getSVal(CE->getArg(0)).getAsRegion();
|
|
if (Cont) {
|
|
const auto *Data = getContainerData(State, Cont);
|
|
if (Data) {
|
|
SymbolRef Field = get(Data);
|
|
if (Field) {
|
|
State = State->BindExpr(CE, C.getLocationContext(),
|
|
nonloc::SymbolVal(Field));
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
}
|
|
|
|
auto &BVF = C.getSValBuilder().getBasicValueFactory();
|
|
State = State->BindExpr(CE, C.getLocationContext(),
|
|
nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
|
|
}
|
|
|
|
void DebugContainerModeling::analyzerContainerBegin(const CallExpr *CE,
|
|
CheckerContext &C) const {
|
|
analyzerContainerDataField(CE, C, [](const ContainerData *D) {
|
|
return D->getBegin();
|
|
});
|
|
}
|
|
|
|
void DebugContainerModeling::analyzerContainerEnd(const CallExpr *CE,
|
|
CheckerContext &C) const {
|
|
analyzerContainerDataField(CE, C, [](const ContainerData *D) {
|
|
return D->getEnd();
|
|
});
|
|
}
|
|
|
|
ExplodedNode *DebugContainerModeling::reportDebugMsg(llvm::StringRef Msg,
|
|
CheckerContext &C) const {
|
|
ExplodedNode *N = C.generateNonFatalErrorNode();
|
|
if (!N)
|
|
return nullptr;
|
|
|
|
auto &BR = C.getBugReporter();
|
|
BR.emitReport(std::make_unique<PathSensitiveBugReport>(*DebugMsgBugType,
|
|
Msg, N));
|
|
return N;
|
|
}
|
|
|
|
void ento::registerDebugContainerModeling(CheckerManager &mgr) {
|
|
mgr.registerChecker<DebugContainerModeling>();
|
|
}
|
|
|
|
bool ento::shouldRegisterDebugContainerModeling(const LangOptions &LO) {
|
|
return true;
|
|
}
|