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  std::unique_ptr<BugType> DebugMsgBugType;
32
33  template <typename Getter>
34  void analyzerContainerDataField(const CallExpr *CE, CheckerContext &C,
35                                  Getter get) const;
36  void analyzerContainerBegin(const CallExpr *CE, CheckerContext &C) const;
37  void analyzerContainerEnd(const CallExpr *CE, CheckerContext &C) const;
38  ExplodedNode *reportDebugMsg(llvm::StringRef Msg, CheckerContext &C) const;
39
40  typedef void (DebugContainerModeling::*FnCheck)(const CallExpr *,
41                                                 CheckerContext &) const;
42
43  CallDescriptionMap<FnCheck> Callbacks = {
44      {{{"clang_analyzer_container_begin"}, 1},
45       &DebugContainerModeling::analyzerContainerBegin},
46      {{{"clang_analyzer_container_end"}, 1},
47       &DebugContainerModeling::analyzerContainerEnd},
48  };
49
50public:
51  DebugContainerModeling();
52
53  bool evalCall(const CallEvent &Call, CheckerContext &C) const;
54};
55
56} //namespace
57
58DebugContainerModeling::DebugContainerModeling() {
59  DebugMsgBugType.reset(
60      new BugType(this, "Checking analyzer assumptions", "debug",
61                  /*SuppressOnSink=*/true));
62}
63
64bool DebugContainerModeling::evalCall(const CallEvent &Call,
65                                      CheckerContext &C) const {
66  const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
67  if (!CE)
68    return false;
69
70  const FnCheck *Handler = Callbacks.lookup(Call);
71  if (!Handler)
72    return false;
73
74  (this->**Handler)(CE, C);
75  return true;
76}
77
78template <typename Getter>
79void DebugContainerModeling::analyzerContainerDataField(const CallExpr *CE,
80                                                        CheckerContext &C,
81                                                        Getter get) const {
82  if (CE->getNumArgs() == 0) {
83    reportDebugMsg("Missing container argument", C);
84    return;
85  }
86
87  auto State = C.getState();
88  const MemRegion *Cont = C.getSVal(CE->getArg(0)).getAsRegion();
89  if (Cont) {
90    const auto *Data = getContainerData(State, Cont);
91    if (Data) {
92      SymbolRef Field = get(Data);
93      if (Field) {
94        State = State->BindExpr(CE, C.getLocationContext(),
95                                nonloc::SymbolVal(Field));
96
97        // Progpagate interestingness from the container's data (marked
98        // interesting by an `ExprInspection` debug call to the container
99        // itself.
100        const NoteTag *InterestingTag =
101          C.getNoteTag(
102              [Cont, Field](PathSensitiveBugReport &BR) -> std::string {
103                if (BR.isInteresting(Field)) {
104                  BR.markInteresting(Cont);
105                }
106                return "";
107              });
108        C.addTransition(State, InterestingTag);
109        return;
110      }
111    }
112  }
113
114  auto &BVF = C.getSValBuilder().getBasicValueFactory();
115  State = State->BindExpr(CE, C.getLocationContext(),
116                   nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
117}
118
119void DebugContainerModeling::analyzerContainerBegin(const CallExpr *CE,
120                                                    CheckerContext &C) const {
121  analyzerContainerDataField(CE, C, [](const ContainerData *D) {
122      return D->getBegin();
123    });
124}
125
126void DebugContainerModeling::analyzerContainerEnd(const CallExpr *CE,
127                                                  CheckerContext &C) const {
128  analyzerContainerDataField(CE, C, [](const ContainerData *D) {
129      return D->getEnd();
130    });
131}
132
133ExplodedNode *DebugContainerModeling::reportDebugMsg(llvm::StringRef Msg,
134                                                     CheckerContext &C) const {
135  ExplodedNode *N = C.generateNonFatalErrorNode();
136  if (!N)
137    return nullptr;
138
139  auto &BR = C.getBugReporter();
140  BR.emitReport(std::make_unique<PathSensitiveBugReport>(*DebugMsgBugType,
141                                                         Msg, N));
142  return N;
143}
144
145void ento::registerDebugContainerModeling(CheckerManager &mgr) {
146  mgr.registerChecker<DebugContainerModeling>();
147}
148
149bool ento::shouldRegisterDebugContainerModeling(const CheckerManager &mgr) {
150  return true;
151}
152