xref: /freebsd-src/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugContainerModeling.cpp (revision 5ffd83dbcc34f10e07f6d3e968ae6365869615f4)
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 
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 
63*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>
78*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 
118*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 
125*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 
132*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 
144*5ffd83dbSDimitry Andric void ento::registerDebugContainerModeling(CheckerManager &mgr) {
145*5ffd83dbSDimitry Andric   mgr.registerChecker<DebugContainerModeling>();
146*5ffd83dbSDimitry Andric }
147*5ffd83dbSDimitry Andric 
148*5ffd83dbSDimitry Andric bool ento::shouldRegisterDebugContainerModeling(const CheckerManager &mgr) {
149*5ffd83dbSDimitry Andric   return true;
150*5ffd83dbSDimitry Andric }
151