1//===-- DebugIteratorModeling.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 DebugIteratorModeling
29  : public Checker<eval::Call> {
30
31  std::unique_ptr<BugType> DebugMsgBugType;
32
33  template <typename Getter>
34  void analyzerIteratorDataField(const CallExpr *CE, CheckerContext &C,
35                                 Getter get, SVal Default) const;
36  void analyzerIteratorPosition(const CallExpr *CE, CheckerContext &C) const;
37  void analyzerIteratorContainer(const CallExpr *CE, CheckerContext &C) const;
38  void analyzerIteratorValidity(const CallExpr *CE, CheckerContext &C) const;
39  ExplodedNode *reportDebugMsg(llvm::StringRef Msg, CheckerContext &C) const;
40
41  typedef void (DebugIteratorModeling::*FnCheck)(const CallExpr *,
42                                                 CheckerContext &) const;
43
44  CallDescriptionMap<FnCheck> Callbacks = {
45      {{{"clang_analyzer_iterator_position"}, 1},
46       &DebugIteratorModeling::analyzerIteratorPosition},
47      {{{"clang_analyzer_iterator_container"}, 1},
48       &DebugIteratorModeling::analyzerIteratorContainer},
49      {{{"clang_analyzer_iterator_validity"}, 1},
50       &DebugIteratorModeling::analyzerIteratorValidity},
51  };
52
53public:
54  DebugIteratorModeling();
55
56  bool evalCall(const CallEvent &Call, CheckerContext &C) const;
57};
58
59} //namespace
60
61DebugIteratorModeling::DebugIteratorModeling() {
62  DebugMsgBugType.reset(
63      new BugType(this, "Checking analyzer assumptions", "debug",
64                  /*SuppressOnSink=*/true));
65}
66
67bool DebugIteratorModeling::evalCall(const CallEvent &Call,
68                                     CheckerContext &C) const {
69  const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
70  if (!CE)
71    return false;
72
73  const FnCheck *Handler = Callbacks.lookup(Call);
74  if (!Handler)
75    return false;
76
77  (this->**Handler)(CE, C);
78  return true;
79}
80
81template <typename Getter>
82void DebugIteratorModeling::analyzerIteratorDataField(const CallExpr *CE,
83                                                      CheckerContext &C,
84                                                      Getter get,
85                                                      SVal Default) const {
86  if (CE->getNumArgs() == 0) {
87    reportDebugMsg("Missing iterator argument", C);
88    return;
89  }
90
91  auto State = C.getState();
92  SVal V = C.getSVal(CE->getArg(0));
93  const auto *Pos = getIteratorPosition(State, V);
94  if (Pos) {
95    State = State->BindExpr(CE, C.getLocationContext(), get(Pos));
96  } else {
97    State = State->BindExpr(CE, C.getLocationContext(), Default);
98  }
99  C.addTransition(State);
100}
101
102void DebugIteratorModeling::analyzerIteratorPosition(const CallExpr *CE,
103                                                     CheckerContext &C) const {
104  auto &BVF = C.getSValBuilder().getBasicValueFactory();
105  analyzerIteratorDataField(CE, C, [](const IteratorPosition *P) {
106      return nonloc::SymbolVal(P->getOffset());
107    }, nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
108}
109
110void DebugIteratorModeling::analyzerIteratorContainer(const CallExpr *CE,
111                                                      CheckerContext &C) const {
112  auto &BVF = C.getSValBuilder().getBasicValueFactory();
113  analyzerIteratorDataField(CE, C, [](const IteratorPosition *P) {
114      return loc::MemRegionVal(P->getContainer());
115    }, loc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
116}
117
118void DebugIteratorModeling::analyzerIteratorValidity(const CallExpr *CE,
119                                                     CheckerContext &C) const {
120  auto &BVF = C.getSValBuilder().getBasicValueFactory();
121  analyzerIteratorDataField(CE, C, [&BVF](const IteratorPosition *P) {
122      return
123        nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get((P->isValid()))));
124    }, nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
125}
126
127ExplodedNode *DebugIteratorModeling::reportDebugMsg(llvm::StringRef Msg,
128                                                    CheckerContext &C) const {
129  ExplodedNode *N = C.generateNonFatalErrorNode();
130  if (!N)
131    return nullptr;
132
133  auto &BR = C.getBugReporter();
134  BR.emitReport(std::make_unique<PathSensitiveBugReport>(*DebugMsgBugType,
135                                                         Msg, N));
136  return N;
137}
138
139void ento::registerDebugIteratorModeling(CheckerManager &mgr) {
140  mgr.registerChecker<DebugIteratorModeling>();
141}
142
143bool ento::shouldRegisterDebugIteratorModeling(const CheckerManager &mgr) {
144  return true;
145}
146