1*5ffd83dbSDimitry Andric //==-- DebugContainerModeling.cpp ---------------------------------*- C++ -*--//
2*5ffd83dbSDimitry Andric //
3*5ffd83dbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*5ffd83dbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*5ffd83dbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*5ffd83dbSDimitry Andric //
7*5ffd83dbSDimitry Andric //===----------------------------------------------------------------------===//
8*5ffd83dbSDimitry Andric //
9*5ffd83dbSDimitry Andric // Defines a checker for debugging iterator modeling.
10*5ffd83dbSDimitry Andric //
11*5ffd83dbSDimitry Andric //===----------------------------------------------------------------------===//
12*5ffd83dbSDimitry Andric
13*5ffd83dbSDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
14*5ffd83dbSDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
15*5ffd83dbSDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
16*5ffd83dbSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
17*5ffd83dbSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
18*5ffd83dbSDimitry Andric
19*5ffd83dbSDimitry Andric #include "Iterator.h"
20*5ffd83dbSDimitry Andric
21*5ffd83dbSDimitry Andric using namespace clang;
22*5ffd83dbSDimitry Andric using namespace ento;
23*5ffd83dbSDimitry Andric using namespace iterator;
24*5ffd83dbSDimitry Andric
25*5ffd83dbSDimitry Andric namespace {
26*5ffd83dbSDimitry Andric
27*5ffd83dbSDimitry Andric class DebugContainerModeling
28*5ffd83dbSDimitry Andric : public Checker<eval::Call> {
29*5ffd83dbSDimitry Andric
30*5ffd83dbSDimitry Andric std::unique_ptr<BugType> DebugMsgBugType;
31*5ffd83dbSDimitry Andric
32*5ffd83dbSDimitry Andric template <typename Getter>
33*5ffd83dbSDimitry Andric void analyzerContainerDataField(const CallExpr *CE, CheckerContext &C,
34*5ffd83dbSDimitry Andric Getter get) const;
35*5ffd83dbSDimitry Andric void analyzerContainerBegin(const CallExpr *CE, CheckerContext &C) const;
36*5ffd83dbSDimitry Andric void analyzerContainerEnd(const CallExpr *CE, CheckerContext &C) const;
37*5ffd83dbSDimitry Andric ExplodedNode *reportDebugMsg(llvm::StringRef Msg, CheckerContext &C) const;
38*5ffd83dbSDimitry Andric
39*5ffd83dbSDimitry Andric typedef void (DebugContainerModeling::*FnCheck)(const CallExpr *,
40*5ffd83dbSDimitry Andric CheckerContext &) const;
41*5ffd83dbSDimitry Andric
42*5ffd83dbSDimitry Andric CallDescriptionMap<FnCheck> Callbacks = {
43*5ffd83dbSDimitry Andric {{0, "clang_analyzer_container_begin", 1},
44*5ffd83dbSDimitry Andric &DebugContainerModeling::analyzerContainerBegin},
45*5ffd83dbSDimitry Andric {{0, "clang_analyzer_container_end", 1},
46*5ffd83dbSDimitry Andric &DebugContainerModeling::analyzerContainerEnd},
47*5ffd83dbSDimitry Andric };
48*5ffd83dbSDimitry Andric
49*5ffd83dbSDimitry Andric public:
50*5ffd83dbSDimitry Andric DebugContainerModeling();
51*5ffd83dbSDimitry Andric
52*5ffd83dbSDimitry Andric bool evalCall(const CallEvent &Call, CheckerContext &C) const;
53*5ffd83dbSDimitry Andric };
54*5ffd83dbSDimitry Andric
55*5ffd83dbSDimitry Andric } //namespace
56*5ffd83dbSDimitry Andric
DebugContainerModeling()57*5ffd83dbSDimitry Andric DebugContainerModeling::DebugContainerModeling() {
58*5ffd83dbSDimitry Andric DebugMsgBugType.reset(
59*5ffd83dbSDimitry Andric new BugType(this, "Checking analyzer assumptions", "debug",
60*5ffd83dbSDimitry Andric /*SuppressOnSink=*/true));
61*5ffd83dbSDimitry Andric }
62*5ffd83dbSDimitry Andric
evalCall(const CallEvent & Call,CheckerContext & C) const63*5ffd83dbSDimitry Andric bool DebugContainerModeling::evalCall(const CallEvent &Call,
64*5ffd83dbSDimitry Andric CheckerContext &C) const {
65*5ffd83dbSDimitry Andric const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
66*5ffd83dbSDimitry Andric if (!CE)
67*5ffd83dbSDimitry Andric return false;
68*5ffd83dbSDimitry Andric
69*5ffd83dbSDimitry Andric const FnCheck *Handler = Callbacks.lookup(Call);
70*5ffd83dbSDimitry Andric if (!Handler)
71*5ffd83dbSDimitry Andric return false;
72*5ffd83dbSDimitry Andric
73*5ffd83dbSDimitry Andric (this->**Handler)(CE, C);
74*5ffd83dbSDimitry Andric return true;
75*5ffd83dbSDimitry Andric }
76*5ffd83dbSDimitry Andric
77*5ffd83dbSDimitry Andric template <typename Getter>
analyzerContainerDataField(const CallExpr * CE,CheckerContext & C,Getter get) const78*5ffd83dbSDimitry Andric void DebugContainerModeling::analyzerContainerDataField(const CallExpr *CE,
79*5ffd83dbSDimitry Andric CheckerContext &C,
80*5ffd83dbSDimitry Andric Getter get) const {
81*5ffd83dbSDimitry Andric if (CE->getNumArgs() == 0) {
82*5ffd83dbSDimitry Andric reportDebugMsg("Missing container argument", C);
83*5ffd83dbSDimitry Andric return;
84*5ffd83dbSDimitry Andric }
85*5ffd83dbSDimitry Andric
86*5ffd83dbSDimitry Andric auto State = C.getState();
87*5ffd83dbSDimitry Andric const MemRegion *Cont = C.getSVal(CE->getArg(0)).getAsRegion();
88*5ffd83dbSDimitry Andric if (Cont) {
89*5ffd83dbSDimitry Andric const auto *Data = getContainerData(State, Cont);
90*5ffd83dbSDimitry Andric if (Data) {
91*5ffd83dbSDimitry Andric SymbolRef Field = get(Data);
92*5ffd83dbSDimitry Andric if (Field) {
93*5ffd83dbSDimitry Andric State = State->BindExpr(CE, C.getLocationContext(),
94*5ffd83dbSDimitry Andric nonloc::SymbolVal(Field));
95*5ffd83dbSDimitry Andric
96*5ffd83dbSDimitry Andric // Progpagate interestingness from the container's data (marked
97*5ffd83dbSDimitry Andric // interesting by an `ExprInspection` debug call to the container
98*5ffd83dbSDimitry Andric // itself.
99*5ffd83dbSDimitry Andric const NoteTag *InterestingTag =
100*5ffd83dbSDimitry Andric C.getNoteTag(
101*5ffd83dbSDimitry Andric [Cont, Field](PathSensitiveBugReport &BR) -> std::string {
102*5ffd83dbSDimitry Andric if (BR.isInteresting(Field)) {
103*5ffd83dbSDimitry Andric BR.markInteresting(Cont);
104*5ffd83dbSDimitry Andric }
105*5ffd83dbSDimitry Andric return "";
106*5ffd83dbSDimitry Andric });
107*5ffd83dbSDimitry Andric C.addTransition(State, InterestingTag);
108*5ffd83dbSDimitry Andric return;
109*5ffd83dbSDimitry Andric }
110*5ffd83dbSDimitry Andric }
111*5ffd83dbSDimitry Andric }
112*5ffd83dbSDimitry Andric
113*5ffd83dbSDimitry Andric auto &BVF = C.getSValBuilder().getBasicValueFactory();
114*5ffd83dbSDimitry Andric State = State->BindExpr(CE, C.getLocationContext(),
115*5ffd83dbSDimitry Andric nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
116*5ffd83dbSDimitry Andric }
117*5ffd83dbSDimitry Andric
analyzerContainerBegin(const CallExpr * CE,CheckerContext & C) const118*5ffd83dbSDimitry Andric void DebugContainerModeling::analyzerContainerBegin(const CallExpr *CE,
119*5ffd83dbSDimitry Andric CheckerContext &C) const {
120*5ffd83dbSDimitry Andric analyzerContainerDataField(CE, C, [](const ContainerData *D) {
121*5ffd83dbSDimitry Andric return D->getBegin();
122*5ffd83dbSDimitry Andric });
123*5ffd83dbSDimitry Andric }
124*5ffd83dbSDimitry Andric
analyzerContainerEnd(const CallExpr * CE,CheckerContext & C) const125*5ffd83dbSDimitry Andric void DebugContainerModeling::analyzerContainerEnd(const CallExpr *CE,
126*5ffd83dbSDimitry Andric CheckerContext &C) const {
127*5ffd83dbSDimitry Andric analyzerContainerDataField(CE, C, [](const ContainerData *D) {
128*5ffd83dbSDimitry Andric return D->getEnd();
129*5ffd83dbSDimitry Andric });
130*5ffd83dbSDimitry Andric }
131*5ffd83dbSDimitry Andric
reportDebugMsg(llvm::StringRef Msg,CheckerContext & C) const132*5ffd83dbSDimitry Andric ExplodedNode *DebugContainerModeling::reportDebugMsg(llvm::StringRef Msg,
133*5ffd83dbSDimitry Andric CheckerContext &C) const {
134*5ffd83dbSDimitry Andric ExplodedNode *N = C.generateNonFatalErrorNode();
135*5ffd83dbSDimitry Andric if (!N)
136*5ffd83dbSDimitry Andric return nullptr;
137*5ffd83dbSDimitry Andric
138*5ffd83dbSDimitry Andric auto &BR = C.getBugReporter();
139*5ffd83dbSDimitry Andric BR.emitReport(std::make_unique<PathSensitiveBugReport>(*DebugMsgBugType,
140*5ffd83dbSDimitry Andric Msg, N));
141*5ffd83dbSDimitry Andric return N;
142*5ffd83dbSDimitry Andric }
143*5ffd83dbSDimitry Andric
registerDebugContainerModeling(CheckerManager & mgr)144*5ffd83dbSDimitry Andric void ento::registerDebugContainerModeling(CheckerManager &mgr) {
145*5ffd83dbSDimitry Andric mgr.registerChecker<DebugContainerModeling>();
146*5ffd83dbSDimitry Andric }
147*5ffd83dbSDimitry Andric
shouldRegisterDebugContainerModeling(const CheckerManager & mgr)148*5ffd83dbSDimitry Andric bool ento::shouldRegisterDebugContainerModeling(const CheckerManager &mgr) {
149*5ffd83dbSDimitry Andric return true;
150*5ffd83dbSDimitry Andric }
151