xref: /llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugContainerModeling.cpp (revision 1a27d63a8891076ad9176f1a70f372003bc55c2f)
19a08a3faSAdam Balogh //==-- DebugContainerModeling.cpp ---------------------------------*- C++ -*--//
29a08a3faSAdam Balogh //
39a08a3faSAdam Balogh // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
49a08a3faSAdam Balogh // See https://llvm.org/LICENSE.txt for license information.
59a08a3faSAdam Balogh // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
69a08a3faSAdam Balogh //
79a08a3faSAdam Balogh //===----------------------------------------------------------------------===//
89a08a3faSAdam Balogh //
99a08a3faSAdam Balogh // Defines a checker for debugging iterator modeling.
109a08a3faSAdam Balogh //
119a08a3faSAdam Balogh //===----------------------------------------------------------------------===//
129a08a3faSAdam Balogh 
139a08a3faSAdam Balogh #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
149a08a3faSAdam Balogh #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
159a08a3faSAdam Balogh #include "clang/StaticAnalyzer/Core/Checker.h"
169a08a3faSAdam Balogh #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
179a08a3faSAdam Balogh #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
189a08a3faSAdam Balogh 
199a08a3faSAdam Balogh #include "Iterator.h"
209a08a3faSAdam Balogh 
219a08a3faSAdam Balogh using namespace clang;
229a08a3faSAdam Balogh using namespace ento;
239a08a3faSAdam Balogh using namespace iterator;
249a08a3faSAdam Balogh 
259a08a3faSAdam Balogh namespace {
269a08a3faSAdam Balogh 
279a08a3faSAdam Balogh class DebugContainerModeling
289a08a3faSAdam Balogh   : public Checker<eval::Call> {
299a08a3faSAdam Balogh 
309a08a3faSAdam Balogh   std::unique_ptr<BugType> DebugMsgBugType;
319a08a3faSAdam Balogh 
329a08a3faSAdam Balogh   template <typename Getter>
339a08a3faSAdam Balogh   void analyzerContainerDataField(const CallExpr *CE, CheckerContext &C,
349a08a3faSAdam Balogh                                   Getter get) const;
359a08a3faSAdam Balogh   void analyzerContainerBegin(const CallExpr *CE, CheckerContext &C) const;
369a08a3faSAdam Balogh   void analyzerContainerEnd(const CallExpr *CE, CheckerContext &C) const;
379a08a3faSAdam Balogh   ExplodedNode *reportDebugMsg(llvm::StringRef Msg, CheckerContext &C) const;
389a08a3faSAdam Balogh 
399a08a3faSAdam Balogh   typedef void (DebugContainerModeling::*FnCheck)(const CallExpr *,
409a08a3faSAdam Balogh                                                  CheckerContext &) const;
419a08a3faSAdam Balogh 
429a08a3faSAdam Balogh   CallDescriptionMap<FnCheck> Callbacks = {
439a08a3faSAdam Balogh     {{0, "clang_analyzer_container_begin", 1},
449a08a3faSAdam Balogh      &DebugContainerModeling::analyzerContainerBegin},
459a08a3faSAdam Balogh     {{0, "clang_analyzer_container_end", 1},
469a08a3faSAdam Balogh      &DebugContainerModeling::analyzerContainerEnd},
479a08a3faSAdam Balogh   };
489a08a3faSAdam Balogh 
499a08a3faSAdam Balogh public:
509a08a3faSAdam Balogh   DebugContainerModeling();
519a08a3faSAdam Balogh 
529a08a3faSAdam Balogh   bool evalCall(const CallEvent &Call, CheckerContext &C) const;
539a08a3faSAdam Balogh };
549a08a3faSAdam Balogh 
559a08a3faSAdam Balogh } //namespace
569a08a3faSAdam Balogh 
579a08a3faSAdam Balogh DebugContainerModeling::DebugContainerModeling() {
589a08a3faSAdam Balogh   DebugMsgBugType.reset(
599a08a3faSAdam Balogh       new BugType(this, "Checking analyzer assumptions", "debug",
609a08a3faSAdam Balogh                   /*SuppressOnSink=*/true));
619a08a3faSAdam Balogh }
629a08a3faSAdam Balogh 
639a08a3faSAdam Balogh bool DebugContainerModeling::evalCall(const CallEvent &Call,
649a08a3faSAdam Balogh                                       CheckerContext &C) const {
659a08a3faSAdam Balogh   const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
669a08a3faSAdam Balogh   if (!CE)
679a08a3faSAdam Balogh     return false;
689a08a3faSAdam Balogh 
699a08a3faSAdam Balogh   const FnCheck *Handler = Callbacks.lookup(Call);
709a08a3faSAdam Balogh   if (!Handler)
719a08a3faSAdam Balogh     return false;
729a08a3faSAdam Balogh 
739a08a3faSAdam Balogh   (this->**Handler)(CE, C);
749a08a3faSAdam Balogh   return true;
759a08a3faSAdam Balogh }
769a08a3faSAdam Balogh 
779a08a3faSAdam Balogh template <typename Getter>
789a08a3faSAdam Balogh void DebugContainerModeling::analyzerContainerDataField(const CallExpr *CE,
799a08a3faSAdam Balogh                                                         CheckerContext &C,
809a08a3faSAdam Balogh                                                         Getter get) const {
819a08a3faSAdam Balogh   if (CE->getNumArgs() == 0) {
829a08a3faSAdam Balogh     reportDebugMsg("Missing container argument", C);
839a08a3faSAdam Balogh     return;
849a08a3faSAdam Balogh   }
859a08a3faSAdam Balogh 
869a08a3faSAdam Balogh   auto State = C.getState();
879a08a3faSAdam Balogh   const MemRegion *Cont = C.getSVal(CE->getArg(0)).getAsRegion();
889a08a3faSAdam Balogh   if (Cont) {
899a08a3faSAdam Balogh     const auto *Data = getContainerData(State, Cont);
909a08a3faSAdam Balogh     if (Data) {
919a08a3faSAdam Balogh       SymbolRef Field = get(Data);
929a08a3faSAdam Balogh       if (Field) {
939a08a3faSAdam Balogh         State = State->BindExpr(CE, C.getLocationContext(),
949a08a3faSAdam Balogh                                 nonloc::SymbolVal(Field));
95*1a27d63aSAdam Balogh 
96*1a27d63aSAdam Balogh         // Progpagate interestingness from the container's data (marked
97*1a27d63aSAdam Balogh         // interesting by an `ExprInspection` debug call to the container
98*1a27d63aSAdam Balogh         // itself.
99*1a27d63aSAdam Balogh         const NoteTag *InterestingTag =
100*1a27d63aSAdam Balogh           C.getNoteTag(
101*1a27d63aSAdam Balogh               [Cont, Field](PathSensitiveBugReport &BR) -> std::string {
102*1a27d63aSAdam Balogh                 if (BR.isInteresting(Field)) {
103*1a27d63aSAdam Balogh                   BR.markInteresting(Cont);
104*1a27d63aSAdam Balogh                 }
105*1a27d63aSAdam Balogh                 return "";
106*1a27d63aSAdam Balogh               });
107*1a27d63aSAdam Balogh         C.addTransition(State, InterestingTag);
1089a08a3faSAdam Balogh         return;
1099a08a3faSAdam Balogh       }
1109a08a3faSAdam Balogh     }
1119a08a3faSAdam Balogh   }
1129a08a3faSAdam Balogh 
1139a08a3faSAdam Balogh   auto &BVF = C.getSValBuilder().getBasicValueFactory();
1149a08a3faSAdam Balogh   State = State->BindExpr(CE, C.getLocationContext(),
1159a08a3faSAdam Balogh                    nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
1169a08a3faSAdam Balogh }
1179a08a3faSAdam Balogh 
1189a08a3faSAdam Balogh void DebugContainerModeling::analyzerContainerBegin(const CallExpr *CE,
1199a08a3faSAdam Balogh                                                     CheckerContext &C) const {
1209a08a3faSAdam Balogh   analyzerContainerDataField(CE, C, [](const ContainerData *D) {
1219a08a3faSAdam Balogh       return D->getBegin();
1229a08a3faSAdam Balogh     });
1239a08a3faSAdam Balogh }
1249a08a3faSAdam Balogh 
1259a08a3faSAdam Balogh void DebugContainerModeling::analyzerContainerEnd(const CallExpr *CE,
1269a08a3faSAdam Balogh                                                   CheckerContext &C) const {
1279a08a3faSAdam Balogh   analyzerContainerDataField(CE, C, [](const ContainerData *D) {
1289a08a3faSAdam Balogh       return D->getEnd();
1299a08a3faSAdam Balogh     });
1309a08a3faSAdam Balogh }
1319a08a3faSAdam Balogh 
1329a08a3faSAdam Balogh ExplodedNode *DebugContainerModeling::reportDebugMsg(llvm::StringRef Msg,
1339a08a3faSAdam Balogh                                                      CheckerContext &C) const {
1349a08a3faSAdam Balogh   ExplodedNode *N = C.generateNonFatalErrorNode();
1359a08a3faSAdam Balogh   if (!N)
1369a08a3faSAdam Balogh     return nullptr;
1379a08a3faSAdam Balogh 
1389a08a3faSAdam Balogh   auto &BR = C.getBugReporter();
1399a08a3faSAdam Balogh   BR.emitReport(std::make_unique<PathSensitiveBugReport>(*DebugMsgBugType,
1409a08a3faSAdam Balogh                                                          Msg, N));
1419a08a3faSAdam Balogh   return N;
1429a08a3faSAdam Balogh }
1439a08a3faSAdam Balogh 
1449a08a3faSAdam Balogh void ento::registerDebugContainerModeling(CheckerManager &mgr) {
1459a08a3faSAdam Balogh   mgr.registerChecker<DebugContainerModeling>();
1469a08a3faSAdam Balogh }
1479a08a3faSAdam Balogh 
1489a08a3faSAdam Balogh bool ento::shouldRegisterDebugContainerModeling(const LangOptions &LO) {
1499a08a3faSAdam Balogh   return true;
1509a08a3faSAdam Balogh }
151