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