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