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