llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugIteratorModeling.cpp
NagyDonat 06eedffe0d
[analyzer] Use explicit call description mode in iterator checkers (#88913)
This commit explicitly specifies the matching mode (C library function,
any non-method function, or C++ method) for the `CallDescription`s
constructed in the iterator/container checkers.

This change won't cause major functional changes, but isn't NFC because
it ensures that e.g. call descriptions for a non-method function won't
accidentally match a method that has the same name.

Separate commits will perform (or have already performed) this change in
other checkers. My goal is to ensure that the call description mode is
always explicitly specified and eliminate (or strongly restrict) the
vague "may be either a method or a simple function" mode that's the
current default.

I'm handling the iterator checkers in this separate commit because
they're infamously complex; but I don't expect any trouble because this
transition doesn't interact with the "central" logic of iterator
handling.
2024-04-17 13:26:51 +02:00

139 lines
5.1 KiB
C++

//===-- DebugIteratorModeling.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/CallDescription.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 DebugIteratorModeling
: public Checker<eval::Call> {
const BugType DebugMsgBugType{this, "Checking analyzer assumptions", "debug",
/*SuppressOnSink=*/true};
template <typename Getter>
void analyzerIteratorDataField(const CallExpr *CE, CheckerContext &C,
Getter get, SVal Default) const;
void analyzerIteratorPosition(const CallExpr *CE, CheckerContext &C) const;
void analyzerIteratorContainer(const CallExpr *CE, CheckerContext &C) const;
void analyzerIteratorValidity(const CallExpr *CE, CheckerContext &C) const;
ExplodedNode *reportDebugMsg(llvm::StringRef Msg, CheckerContext &C) const;
typedef void (DebugIteratorModeling::*FnCheck)(const CallExpr *,
CheckerContext &) const;
CallDescriptionMap<FnCheck> Callbacks = {
{{CDM::SimpleFunc, {"clang_analyzer_iterator_position"}, 1},
&DebugIteratorModeling::analyzerIteratorPosition},
{{CDM::SimpleFunc, {"clang_analyzer_iterator_container"}, 1},
&DebugIteratorModeling::analyzerIteratorContainer},
{{CDM::SimpleFunc, {"clang_analyzer_iterator_validity"}, 1},
&DebugIteratorModeling::analyzerIteratorValidity},
};
public:
bool evalCall(const CallEvent &Call, CheckerContext &C) const;
};
} // namespace
bool DebugIteratorModeling::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 DebugIteratorModeling::analyzerIteratorDataField(const CallExpr *CE,
CheckerContext &C,
Getter get,
SVal Default) const {
if (CE->getNumArgs() == 0) {
reportDebugMsg("Missing iterator argument", C);
return;
}
auto State = C.getState();
SVal V = C.getSVal(CE->getArg(0));
const auto *Pos = getIteratorPosition(State, V);
if (Pos) {
State = State->BindExpr(CE, C.getLocationContext(), get(Pos));
} else {
State = State->BindExpr(CE, C.getLocationContext(), Default);
}
C.addTransition(State);
}
void DebugIteratorModeling::analyzerIteratorPosition(const CallExpr *CE,
CheckerContext &C) const {
auto &BVF = C.getSValBuilder().getBasicValueFactory();
analyzerIteratorDataField(CE, C, [](const IteratorPosition *P) {
return nonloc::SymbolVal(P->getOffset());
}, nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
}
void DebugIteratorModeling::analyzerIteratorContainer(const CallExpr *CE,
CheckerContext &C) const {
auto &BVF = C.getSValBuilder().getBasicValueFactory();
analyzerIteratorDataField(CE, C, [](const IteratorPosition *P) {
return loc::MemRegionVal(P->getContainer());
}, loc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
}
void DebugIteratorModeling::analyzerIteratorValidity(const CallExpr *CE,
CheckerContext &C) const {
auto &BVF = C.getSValBuilder().getBasicValueFactory();
analyzerIteratorDataField(CE, C, [&BVF](const IteratorPosition *P) {
return
nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get((P->isValid()))));
}, nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
}
ExplodedNode *DebugIteratorModeling::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::registerDebugIteratorModeling(CheckerManager &mgr) {
mgr.registerChecker<DebugIteratorModeling>();
}
bool ento::shouldRegisterDebugIteratorModeling(const CheckerManager &mgr) {
return true;
}