Commit 9a08a3fa authored by Adam Balogh's avatar Adam Balogh
Browse files

[Analyzer] Split container modeling from iterator modeling

Iterator modeling depends on container modeling,
but not vice versa. This enables the possibility
to arrange these two modeling checkers into
separate layers.

There are several advantages for doing this: the
first one is that this way we can keep the
respective modeling checkers moderately simple
and small. Furthermore, this enables creation of
checkers on container operations which only
depend on the container modeling. Thus iterator
modeling can be disabled together with the
iterator checkers if they are not needed.

Since many container operations also affect
iterators, container modeling also uses the
iterator library: it creates iterator positions
upon calling the `begin()` or `end()` method of
a containter (but propagation of the abstract
position is left to the iterator modeling),
shifts or invalidates iterators according to the
rules upon calling a container modifier and
rebinds the iterator to a new container upon
`std::move()`.

Iterator modeling propagates the abstract
iterator position, handles the relations between
iterator positions and models iterator
operations such as increments and decrements.

Differential Revision: https://reviews.llvm.org/D73547
parent da58e68f
Loading
Loading
Loading
Loading
+12 −1
Original line number Diff line number Diff line
@@ -602,6 +602,11 @@ def VirtualCallChecker : Checker<"VirtualCall">,

let ParentPackage = CplusplusAlpha in {

def ContainerModeling : Checker<"ContainerModeling">,
  HelpText<"Models C++ containers">,
  Documentation<NotDocumented>,
  Hidden;

def DeleteWithNonVirtualDtorChecker : Checker<"DeleteWithNonVirtualDtor">,
  HelpText<"Reports destructions of polymorphic objects with a non-virtual "
           "destructor in their base class">,
@@ -613,6 +618,7 @@ def EnumCastOutOfRangeChecker : Checker<"EnumCastOutOfRange">,

def IteratorModeling : Checker<"IteratorModeling">,
  HelpText<"Models iterators of C++ containers">,
  Dependencies<[ContainerModeling]>,
  Documentation<NotDocumented>,
  Hidden;

@@ -1373,9 +1379,14 @@ def ReportStmts : Checker<"ReportStmts">,
  HelpText<"Emits a warning for every statement.">,
  Documentation<NotDocumented>;

def DebugContainerModeling : Checker<"DebugContainerModeling">,
  HelpText<"Check the analyzer's understanding of C++ containers">,
  Dependencies<[ContainerModeling]>,
  Documentation<NotDocumented>;

def DebugIteratorModeling : Checker<"DebugIteratorModeling">,
  HelpText<"Check the analyzer's understanding of C++ iterators">,
  Dependencies<[IteratorModeling]>,
  Dependencies<[DebugContainerModeling, IteratorModeling]>,
  Documentation<NotDocumented>;

} // end "debug"
+2 −0
Original line number Diff line number Diff line
@@ -25,10 +25,12 @@ add_clang_library(clangStaticAnalyzerCheckers
  CheckerDocumentation.cpp
  ChrootChecker.cpp
  CloneChecker.cpp
  ContainerModeling.cpp
  ConversionChecker.cpp
  CXXSelfAssignmentChecker.cpp
  DeadStoresChecker.cpp
  DebugCheckers.cpp
  DebugContainerModeling.cpp
  DebugIteratorModeling.cpp
  DeleteWithNonVirtualDtorChecker.cpp
  DereferenceChecker.cpp
+1040 −0

File added.

Preview size limit exceeded, changes collapsed.

+138 −0
Original line number Diff line number Diff line
//==-- DebugContainerModeling.cpp ---------------------------------*- C++ -*--//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Defines a checker for debugging iterator modeling.
//
//===----------------------------------------------------------------------===//

#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"

#include "Iterator.h"

using namespace clang;
using namespace ento;
using namespace iterator;

namespace {

class DebugContainerModeling
  : public Checker<eval::Call> {

  std::unique_ptr<BugType> DebugMsgBugType;

  template <typename Getter>
  void analyzerContainerDataField(const CallExpr *CE, CheckerContext &C,
                                  Getter get) const;
  void analyzerContainerBegin(const CallExpr *CE, CheckerContext &C) const;
  void analyzerContainerEnd(const CallExpr *CE, CheckerContext &C) const;
  ExplodedNode *reportDebugMsg(llvm::StringRef Msg, CheckerContext &C) const;

  typedef void (DebugContainerModeling::*FnCheck)(const CallExpr *,
                                                 CheckerContext &) const;

  CallDescriptionMap<FnCheck> Callbacks = {
    {{0, "clang_analyzer_container_begin", 1},
     &DebugContainerModeling::analyzerContainerBegin},
    {{0, "clang_analyzer_container_end", 1},
     &DebugContainerModeling::analyzerContainerEnd},
  };

public:
  DebugContainerModeling();

  bool evalCall(const CallEvent &Call, CheckerContext &C) const;
};

} //namespace

DebugContainerModeling::DebugContainerModeling() {
  DebugMsgBugType.reset(
      new BugType(this, "Checking analyzer assumptions", "debug",
                  /*SuppressOnSink=*/true));
}

bool DebugContainerModeling::evalCall(const CallEvent &Call,
                                      CheckerContext &C) const {
  const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
  if (!CE)
    return false;

  const FnCheck *Handler = Callbacks.lookup(Call);
  if (!Handler)
    return false;

  (this->**Handler)(CE, C);
  return true;
}

template <typename Getter>
void DebugContainerModeling::analyzerContainerDataField(const CallExpr *CE,
                                                        CheckerContext &C,
                                                        Getter get) const {
  if (CE->getNumArgs() == 0) {
    reportDebugMsg("Missing container argument", C);
    return;
  }

  auto State = C.getState();
  const MemRegion *Cont = C.getSVal(CE->getArg(0)).getAsRegion();
  if (Cont) {
    const auto *Data = getContainerData(State, Cont);
    if (Data) {
      SymbolRef Field = get(Data);
      if (Field) {
        State = State->BindExpr(CE, C.getLocationContext(),
                                nonloc::SymbolVal(Field));
        C.addTransition(State);
        return;
      }
    }
  }

  auto &BVF = C.getSValBuilder().getBasicValueFactory();
  State = State->BindExpr(CE, C.getLocationContext(),
                   nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
}

void DebugContainerModeling::analyzerContainerBegin(const CallExpr *CE,
                                                    CheckerContext &C) const {
  analyzerContainerDataField(CE, C, [](const ContainerData *D) {
      return D->getBegin();
    });
}

void DebugContainerModeling::analyzerContainerEnd(const CallExpr *CE,
                                                  CheckerContext &C) const {
  analyzerContainerDataField(CE, C, [](const ContainerData *D) {
      return D->getEnd();
    });
}

ExplodedNode *DebugContainerModeling::reportDebugMsg(llvm::StringRef Msg,
                                                     CheckerContext &C) const {
  ExplodedNode *N = C.generateNonFatalErrorNode();
  if (!N)
    return nullptr;

  auto &BR = C.getBugReporter();
  BR.emitReport(std::make_unique<PathSensitiveBugReport>(*DebugMsgBugType,
                                                         Msg, N));
  return N;
}

void ento::registerDebugContainerModeling(CheckerManager &mgr) {
  mgr.registerChecker<DebugContainerModeling>();
}

bool ento::shouldRegisterDebugContainerModeling(const LangOptions &LO) {
  return true;
}
+0 −52
Original line number Diff line number Diff line
@@ -29,11 +29,6 @@ class DebugIteratorModeling

  std::unique_ptr<BugType> DebugMsgBugType;

  template <typename Getter>
  void analyzerContainerDataField(const CallExpr *CE, CheckerContext &C,
                                  Getter get) const;
  void analyzerContainerBegin(const CallExpr *CE, CheckerContext &C) const;
  void analyzerContainerEnd(const CallExpr *CE, CheckerContext &C) const;
  template <typename Getter>
  void analyzerIteratorDataField(const CallExpr *CE, CheckerContext &C,
                                 Getter get, SVal Default) const;
@@ -46,10 +41,6 @@ class DebugIteratorModeling
                                                 CheckerContext &) const;

  CallDescriptionMap<FnCheck> Callbacks = {
    {{0, "clang_analyzer_container_begin", 1},
     &DebugIteratorModeling::analyzerContainerBegin},
    {{0, "clang_analyzer_container_end", 1},
     &DebugIteratorModeling::analyzerContainerEnd},
    {{0, "clang_analyzer_iterator_position", 1},
     &DebugIteratorModeling::analyzerIteratorPosition},
    {{0, "clang_analyzer_iterator_container", 1},
@@ -86,49 +77,6 @@ bool DebugIteratorModeling::evalCall(const CallEvent &Call,
  return true;
}

template <typename Getter>
void DebugIteratorModeling::analyzerContainerDataField(const CallExpr *CE,
                                                       CheckerContext &C,
                                                       Getter get) const {
  if (CE->getNumArgs() == 0) {
    reportDebugMsg("Missing container argument", C);
    return;
  }

  auto State = C.getState();
  const MemRegion *Cont = C.getSVal(CE->getArg(0)).getAsRegion();
  if (Cont) {
    const auto *Data = getContainerData(State, Cont);
    if (Data) {
      SymbolRef Field = get(Data);
      if (Field) {
        State = State->BindExpr(CE, C.getLocationContext(),
                                nonloc::SymbolVal(Field));
        C.addTransition(State);
        return;
      }
    }
  }

  auto &BVF = C.getSValBuilder().getBasicValueFactory();
  State = State->BindExpr(CE, C.getLocationContext(),
                   nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
}

void DebugIteratorModeling::analyzerContainerBegin(const CallExpr *CE,
                                                   CheckerContext &C) const {
  analyzerContainerDataField(CE, C, [](const ContainerData *D) {
      return D->getBegin();
    });
}

void DebugIteratorModeling::analyzerContainerEnd(const CallExpr *CE,
                                                 CheckerContext &C) const {
  analyzerContainerDataField(CE, C, [](const ContainerData *D) {
      return D->getEnd();
    });
}

template <typename Getter>
void DebugIteratorModeling::analyzerIteratorDataField(const CallExpr *CE,
                                                      CheckerContext &C,
Loading