diff options
Diffstat (limited to 'clang/lib/Analysis/PathDiagnostic.cpp')
| -rw-r--r-- | clang/lib/Analysis/PathDiagnostic.cpp | 1219 | 
1 files changed, 1219 insertions, 0 deletions
| diff --git a/clang/lib/Analysis/PathDiagnostic.cpp b/clang/lib/Analysis/PathDiagnostic.cpp new file mode 100644 index 000000000000..53235ba07699 --- /dev/null +++ b/clang/lib/Analysis/PathDiagnostic.cpp @@ -0,0 +1,1219 @@ +//===- PathDiagnostic.cpp - Path-Specific Diagnostic Handling -------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +//  This file defines the PathDiagnostic-related interfaces. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/PathDiagnostic.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclBase.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/OperationKinds.h" +#include "clang/AST/ParentMap.h" +#include "clang/AST/Stmt.h" +#include "clang/AST/Type.h" +#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Analysis/CFG.h" +#include "clang/Analysis/ProgramPoint.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/None.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" +#include <cassert> +#include <cstring> +#include <memory> +#include <utility> +#include <vector> + +using namespace clang; +using namespace ento; + +static StringRef StripTrailingDots(StringRef s) { +  for (StringRef::size_type i = s.size(); i != 0; --i) +    if (s[i - 1] != '.') +      return s.substr(0, i); +  return {}; +} + +PathDiagnosticPiece::PathDiagnosticPiece(StringRef s, +                                         Kind k, DisplayHint hint) +    : str(StripTrailingDots(s)), kind(k), Hint(hint) {} + +PathDiagnosticPiece::PathDiagnosticPiece(Kind k, DisplayHint hint) +    : kind(k), Hint(hint) {} + +PathDiagnosticPiece::~PathDiagnosticPiece() = default; + +PathDiagnosticEventPiece::~PathDiagnosticEventPiece() = default; + +PathDiagnosticCallPiece::~PathDiagnosticCallPiece() = default; + +PathDiagnosticControlFlowPiece::~PathDiagnosticControlFlowPiece() = default; + +PathDiagnosticMacroPiece::~PathDiagnosticMacroPiece() = default; + +PathDiagnosticNotePiece::~PathDiagnosticNotePiece() = default; + +PathDiagnosticPopUpPiece::~PathDiagnosticPopUpPiece() = default; + +void PathPieces::flattenTo(PathPieces &Primary, PathPieces &Current, +                           bool ShouldFlattenMacros) const { +  for (auto &Piece : *this) { +    switch (Piece->getKind()) { +    case PathDiagnosticPiece::Call: { +      auto &Call = cast<PathDiagnosticCallPiece>(*Piece); +      if (auto CallEnter = Call.getCallEnterEvent()) +        Current.push_back(std::move(CallEnter)); +      Call.path.flattenTo(Primary, Primary, ShouldFlattenMacros); +      if (auto callExit = Call.getCallExitEvent()) +        Current.push_back(std::move(callExit)); +      break; +    } +    case PathDiagnosticPiece::Macro: { +      auto &Macro = cast<PathDiagnosticMacroPiece>(*Piece); +      if (ShouldFlattenMacros) { +        Macro.subPieces.flattenTo(Primary, Primary, ShouldFlattenMacros); +      } else { +        Current.push_back(Piece); +        PathPieces NewPath; +        Macro.subPieces.flattenTo(Primary, NewPath, ShouldFlattenMacros); +        // FIXME: This probably shouldn't mutate the original path piece. +        Macro.subPieces = NewPath; +      } +      break; +    } +    case PathDiagnosticPiece::Event: +    case PathDiagnosticPiece::ControlFlow: +    case PathDiagnosticPiece::Note: +    case PathDiagnosticPiece::PopUp: +      Current.push_back(Piece); +      break; +    } +  } +} + +PathDiagnostic::~PathDiagnostic() = default; + +PathDiagnostic::PathDiagnostic( +    StringRef CheckerName, const Decl *declWithIssue, StringRef bugtype, +    StringRef verboseDesc, StringRef shortDesc, StringRef category, +    PathDiagnosticLocation LocationToUnique, const Decl *DeclToUnique, +    std::unique_ptr<FilesToLineNumsMap> ExecutedLines) +    : CheckerName(CheckerName), DeclWithIssue(declWithIssue), +      BugType(StripTrailingDots(bugtype)), +      VerboseDesc(StripTrailingDots(verboseDesc)), +      ShortDesc(StripTrailingDots(shortDesc)), +      Category(StripTrailingDots(category)), UniqueingLoc(LocationToUnique), +      UniqueingDecl(DeclToUnique), ExecutedLines(std::move(ExecutedLines)), +      path(pathImpl) {} + +void PathDiagnosticConsumer::anchor() {} + +PathDiagnosticConsumer::~PathDiagnosticConsumer() { +  // Delete the contents of the FoldingSet if it isn't empty already. +  for (auto &Diag : Diags) +    delete &Diag; +} + +void PathDiagnosticConsumer::HandlePathDiagnostic( +    std::unique_ptr<PathDiagnostic> D) { +  if (!D || D->path.empty()) +    return; + +  // We need to flatten the locations (convert Stmt* to locations) because +  // the referenced statements may be freed by the time the diagnostics +  // are emitted. +  D->flattenLocations(); + +  // If the PathDiagnosticConsumer does not support diagnostics that +  // cross file boundaries, prune out such diagnostics now. +  if (!supportsCrossFileDiagnostics()) { +    // Verify that the entire path is from the same FileID. +    FileID FID; +    const SourceManager &SMgr = D->path.front()->getLocation().getManager(); +    SmallVector<const PathPieces *, 5> WorkList; +    WorkList.push_back(&D->path); +    SmallString<128> buf; +    llvm::raw_svector_ostream warning(buf); +    warning << "warning: Path diagnostic report is not generated. Current " +            << "output format does not support diagnostics that cross file " +            << "boundaries. Refer to --analyzer-output for valid output " +            << "formats\n"; + +    while (!WorkList.empty()) { +      const PathPieces &path = *WorkList.pop_back_val(); + +      for (const auto &I : path) { +        const PathDiagnosticPiece *piece = I.get(); +        FullSourceLoc L = piece->getLocation().asLocation().getExpansionLoc(); + +        if (FID.isInvalid()) { +          FID = SMgr.getFileID(L); +        } else if (SMgr.getFileID(L) != FID) { +          llvm::errs() << warning.str(); +          return; +        } + +        // Check the source ranges. +        ArrayRef<SourceRange> Ranges = piece->getRanges(); +        for (const auto &I : Ranges) { +          SourceLocation L = SMgr.getExpansionLoc(I.getBegin()); +          if (!L.isFileID() || SMgr.getFileID(L) != FID) { +            llvm::errs() << warning.str(); +            return; +          } +          L = SMgr.getExpansionLoc(I.getEnd()); +          if (!L.isFileID() || SMgr.getFileID(L) != FID) { +            llvm::errs() << warning.str(); +            return; +          } +        } + +        if (const auto *call = dyn_cast<PathDiagnosticCallPiece>(piece)) +          WorkList.push_back(&call->path); +        else if (const auto *macro = dyn_cast<PathDiagnosticMacroPiece>(piece)) +          WorkList.push_back(¯o->subPieces); +      } +    } + +    if (FID.isInvalid()) +      return; // FIXME: Emit a warning? +  } + +  // Profile the node to see if we already have something matching it +  llvm::FoldingSetNodeID profile; +  D->Profile(profile); +  void *InsertPos = nullptr; + +  if (PathDiagnostic *orig = Diags.FindNodeOrInsertPos(profile, InsertPos)) { +    // Keep the PathDiagnostic with the shorter path. +    // Note, the enclosing routine is called in deterministic order, so the +    // results will be consistent between runs (no reason to break ties if the +    // size is the same). +    const unsigned orig_size = orig->full_size(); +    const unsigned new_size = D->full_size(); +    if (orig_size <= new_size) +      return; + +    assert(orig != D.get()); +    Diags.RemoveNode(orig); +    delete orig; +  } + +  Diags.InsertNode(D.release()); +} + +static Optional<bool> comparePath(const PathPieces &X, const PathPieces &Y); + +static Optional<bool> +compareControlFlow(const PathDiagnosticControlFlowPiece &X, +                   const PathDiagnosticControlFlowPiece &Y) { +  FullSourceLoc XSL = X.getStartLocation().asLocation(); +  FullSourceLoc YSL = Y.getStartLocation().asLocation(); +  if (XSL != YSL) +    return XSL.isBeforeInTranslationUnitThan(YSL); +  FullSourceLoc XEL = X.getEndLocation().asLocation(); +  FullSourceLoc YEL = Y.getEndLocation().asLocation(); +  if (XEL != YEL) +    return XEL.isBeforeInTranslationUnitThan(YEL); +  return None; +} + +static Optional<bool> compareMacro(const PathDiagnosticMacroPiece &X, +                                   const PathDiagnosticMacroPiece &Y) { +  return comparePath(X.subPieces, Y.subPieces); +} + +static Optional<bool> compareCall(const PathDiagnosticCallPiece &X, +                                  const PathDiagnosticCallPiece &Y) { +  FullSourceLoc X_CEL = X.callEnter.asLocation(); +  FullSourceLoc Y_CEL = Y.callEnter.asLocation(); +  if (X_CEL != Y_CEL) +    return X_CEL.isBeforeInTranslationUnitThan(Y_CEL); +  FullSourceLoc X_CEWL = X.callEnterWithin.asLocation(); +  FullSourceLoc Y_CEWL = Y.callEnterWithin.asLocation(); +  if (X_CEWL != Y_CEWL) +    return X_CEWL.isBeforeInTranslationUnitThan(Y_CEWL); +  FullSourceLoc X_CRL = X.callReturn.asLocation(); +  FullSourceLoc Y_CRL = Y.callReturn.asLocation(); +  if (X_CRL != Y_CRL) +    return X_CRL.isBeforeInTranslationUnitThan(Y_CRL); +  return comparePath(X.path, Y.path); +} + +static Optional<bool> comparePiece(const PathDiagnosticPiece &X, +                                   const PathDiagnosticPiece &Y) { +  if (X.getKind() != Y.getKind()) +    return X.getKind() < Y.getKind(); + +  FullSourceLoc XL = X.getLocation().asLocation(); +  FullSourceLoc YL = Y.getLocation().asLocation(); +  if (XL != YL) +    return XL.isBeforeInTranslationUnitThan(YL); + +  if (X.getString() != Y.getString()) +    return X.getString() < Y.getString(); + +  if (X.getRanges().size() != Y.getRanges().size()) +    return X.getRanges().size() < Y.getRanges().size(); + +  const SourceManager &SM = XL.getManager(); + +  for (unsigned i = 0, n = X.getRanges().size(); i < n; ++i) { +    SourceRange XR = X.getRanges()[i]; +    SourceRange YR = Y.getRanges()[i]; +    if (XR != YR) { +      if (XR.getBegin() != YR.getBegin()) +        return SM.isBeforeInTranslationUnit(XR.getBegin(), YR.getBegin()); +      return SM.isBeforeInTranslationUnit(XR.getEnd(), YR.getEnd()); +    } +  } + +  switch (X.getKind()) { +    case PathDiagnosticPiece::ControlFlow: +      return compareControlFlow(cast<PathDiagnosticControlFlowPiece>(X), +                                cast<PathDiagnosticControlFlowPiece>(Y)); +    case PathDiagnosticPiece::Macro: +      return compareMacro(cast<PathDiagnosticMacroPiece>(X), +                          cast<PathDiagnosticMacroPiece>(Y)); +    case PathDiagnosticPiece::Call: +      return compareCall(cast<PathDiagnosticCallPiece>(X), +                         cast<PathDiagnosticCallPiece>(Y)); +    case PathDiagnosticPiece::Event: +    case PathDiagnosticPiece::Note: +    case PathDiagnosticPiece::PopUp: +      return None; +  } +  llvm_unreachable("all cases handled"); +} + +static Optional<bool> comparePath(const PathPieces &X, const PathPieces &Y) { +  if (X.size() != Y.size()) +    return X.size() < Y.size(); + +  PathPieces::const_iterator X_I = X.begin(), X_end = X.end(); +  PathPieces::const_iterator Y_I = Y.begin(), Y_end = Y.end(); + +  for ( ; X_I != X_end && Y_I != Y_end; ++X_I, ++Y_I) { +    Optional<bool> b = comparePiece(**X_I, **Y_I); +    if (b.hasValue()) +      return b.getValue(); +  } + +  return None; +} + +static bool compareCrossTUSourceLocs(FullSourceLoc XL, FullSourceLoc YL) { +  std::pair<FileID, unsigned> XOffs = XL.getDecomposedLoc(); +  std::pair<FileID, unsigned> YOffs = YL.getDecomposedLoc(); +  const SourceManager &SM = XL.getManager(); +  std::pair<bool, bool> InSameTU = SM.isInTheSameTranslationUnit(XOffs, YOffs); +  if (InSameTU.first) +    return XL.isBeforeInTranslationUnitThan(YL); +  const FileEntry *XFE = SM.getFileEntryForID(XL.getSpellingLoc().getFileID()); +  const FileEntry *YFE = SM.getFileEntryForID(YL.getSpellingLoc().getFileID()); +  if (!XFE || !YFE) +    return XFE && !YFE; +  int NameCmp = XFE->getName().compare(YFE->getName()); +  if (NameCmp != 0) +    return NameCmp == -1; +  // Last resort: Compare raw file IDs that are possibly expansions. +  return XL.getFileID() < YL.getFileID(); +} + +static bool compare(const PathDiagnostic &X, const PathDiagnostic &Y) { +  FullSourceLoc XL = X.getLocation().asLocation(); +  FullSourceLoc YL = Y.getLocation().asLocation(); +  if (XL != YL) +    return compareCrossTUSourceLocs(XL, YL); +  if (X.getBugType() != Y.getBugType()) +    return X.getBugType() < Y.getBugType(); +  if (X.getCategory() != Y.getCategory()) +    return X.getCategory() < Y.getCategory(); +  if (X.getVerboseDescription() != Y.getVerboseDescription()) +    return X.getVerboseDescription() < Y.getVerboseDescription(); +  if (X.getShortDescription() != Y.getShortDescription()) +    return X.getShortDescription() < Y.getShortDescription(); +  if (X.getDeclWithIssue() != Y.getDeclWithIssue()) { +    const Decl *XD = X.getDeclWithIssue(); +    if (!XD) +      return true; +    const Decl *YD = Y.getDeclWithIssue(); +    if (!YD) +      return false; +    SourceLocation XDL = XD->getLocation(); +    SourceLocation YDL = YD->getLocation(); +    if (XDL != YDL) { +      const SourceManager &SM = XL.getManager(); +      return compareCrossTUSourceLocs(FullSourceLoc(XDL, SM), +                                      FullSourceLoc(YDL, SM)); +    } +  } +  PathDiagnostic::meta_iterator XI = X.meta_begin(), XE = X.meta_end(); +  PathDiagnostic::meta_iterator YI = Y.meta_begin(), YE = Y.meta_end(); +  if (XE - XI != YE - YI) +    return (XE - XI) < (YE - YI); +  for ( ; XI != XE ; ++XI, ++YI) { +    if (*XI != *YI) +      return (*XI) < (*YI); +  } +  Optional<bool> b = comparePath(X.path, Y.path); +  assert(b.hasValue()); +  return b.getValue(); +} + +void PathDiagnosticConsumer::FlushDiagnostics( +                                     PathDiagnosticConsumer::FilesMade *Files) { +  if (flushed) +    return; + +  flushed = true; + +  std::vector<const PathDiagnostic *> BatchDiags; +  for (const auto &D : Diags) +    BatchDiags.push_back(&D); + +  // Sort the diagnostics so that they are always emitted in a deterministic +  // order. +  int (*Comp)(const PathDiagnostic *const *, const PathDiagnostic *const *) = +      [](const PathDiagnostic *const *X, const PathDiagnostic *const *Y) { +        assert(*X != *Y && "PathDiagnostics not uniqued!"); +        if (compare(**X, **Y)) +          return -1; +        assert(compare(**Y, **X) && "Not a total order!"); +        return 1; +      }; +  array_pod_sort(BatchDiags.begin(), BatchDiags.end(), Comp); + +  FlushDiagnosticsImpl(BatchDiags, Files); + +  // Delete the flushed diagnostics. +  for (const auto D : BatchDiags) +    delete D; + +  // Clear out the FoldingSet. +  Diags.clear(); +} + +PathDiagnosticConsumer::FilesMade::~FilesMade() { +  for (PDFileEntry &Entry : Set) +    Entry.~PDFileEntry(); +} + +void PathDiagnosticConsumer::FilesMade::addDiagnostic(const PathDiagnostic &PD, +                                                      StringRef ConsumerName, +                                                      StringRef FileName) { +  llvm::FoldingSetNodeID NodeID; +  NodeID.Add(PD); +  void *InsertPos; +  PDFileEntry *Entry = Set.FindNodeOrInsertPos(NodeID, InsertPos); +  if (!Entry) { +    Entry = Alloc.Allocate<PDFileEntry>(); +    Entry = new (Entry) PDFileEntry(NodeID); +    Set.InsertNode(Entry, InsertPos); +  } + +  // Allocate persistent storage for the file name. +  char *FileName_cstr = (char*) Alloc.Allocate(FileName.size(), 1); +  memcpy(FileName_cstr, FileName.data(), FileName.size()); + +  Entry->files.push_back(std::make_pair(ConsumerName, +                                        StringRef(FileName_cstr, +                                                  FileName.size()))); +} + +PathDiagnosticConsumer::PDFileEntry::ConsumerFiles * +PathDiagnosticConsumer::FilesMade::getFiles(const PathDiagnostic &PD) { +  llvm::FoldingSetNodeID NodeID; +  NodeID.Add(PD); +  void *InsertPos; +  PDFileEntry *Entry = Set.FindNodeOrInsertPos(NodeID, InsertPos); +  if (!Entry) +    return nullptr; +  return &Entry->files; +} + +//===----------------------------------------------------------------------===// +// PathDiagnosticLocation methods. +//===----------------------------------------------------------------------===// + +SourceLocation PathDiagnosticLocation::getValidSourceLocation( +    const Stmt *S, LocationOrAnalysisDeclContext LAC, bool UseEndOfStatement) { +  SourceLocation L = UseEndOfStatement ? S->getEndLoc() : S->getBeginLoc(); +  assert(!LAC.isNull() && +         "A valid LocationContext or AnalysisDeclContext should be passed to " +         "PathDiagnosticLocation upon creation."); + +  // S might be a temporary statement that does not have a location in the +  // source code, so find an enclosing statement and use its location. +  if (!L.isValid()) { +    AnalysisDeclContext *ADC; +    if (LAC.is<const LocationContext*>()) +      ADC = LAC.get<const LocationContext*>()->getAnalysisDeclContext(); +    else +      ADC = LAC.get<AnalysisDeclContext*>(); + +    ParentMap &PM = ADC->getParentMap(); + +    const Stmt *Parent = S; +    do { +      Parent = PM.getParent(Parent); + +      // In rare cases, we have implicit top-level expressions, +      // such as arguments for implicit member initializers. +      // In this case, fall back to the start of the body (even if we were +      // asked for the statement end location). +      if (!Parent) { +        const Stmt *Body = ADC->getBody(); +        if (Body) +          L = Body->getBeginLoc(); +        else +          L = ADC->getDecl()->getEndLoc(); +        break; +      } + +      L = UseEndOfStatement ? Parent->getEndLoc() : Parent->getBeginLoc(); +    } while (!L.isValid()); +  } + +  // FIXME: Ironically, this assert actually fails in some cases. +  //assert(L.isValid()); +  return L; +} + +static PathDiagnosticLocation +getLocationForCaller(const StackFrameContext *SFC, +                     const LocationContext *CallerCtx, +                     const SourceManager &SM) { +  const CFGBlock &Block = *SFC->getCallSiteBlock(); +  CFGElement Source = Block[SFC->getIndex()]; + +  switch (Source.getKind()) { +  case CFGElement::Statement: +  case CFGElement::Constructor: +  case CFGElement::CXXRecordTypedCall: +    return PathDiagnosticLocation(Source.castAs<CFGStmt>().getStmt(), +                                  SM, CallerCtx); +  case CFGElement::Initializer: { +    const CFGInitializer &Init = Source.castAs<CFGInitializer>(); +    return PathDiagnosticLocation(Init.getInitializer()->getInit(), +                                  SM, CallerCtx); +  } +  case CFGElement::AutomaticObjectDtor: { +    const CFGAutomaticObjDtor &Dtor = Source.castAs<CFGAutomaticObjDtor>(); +    return PathDiagnosticLocation::createEnd(Dtor.getTriggerStmt(), +                                             SM, CallerCtx); +  } +  case CFGElement::DeleteDtor: { +    const CFGDeleteDtor &Dtor = Source.castAs<CFGDeleteDtor>(); +    return PathDiagnosticLocation(Dtor.getDeleteExpr(), SM, CallerCtx); +  } +  case CFGElement::BaseDtor: +  case CFGElement::MemberDtor: { +    const AnalysisDeclContext *CallerInfo = CallerCtx->getAnalysisDeclContext(); +    if (const Stmt *CallerBody = CallerInfo->getBody()) +      return PathDiagnosticLocation::createEnd(CallerBody, SM, CallerCtx); +    return PathDiagnosticLocation::create(CallerInfo->getDecl(), SM); +  } +  case CFGElement::NewAllocator: { +    const CFGNewAllocator &Alloc = Source.castAs<CFGNewAllocator>(); +    return PathDiagnosticLocation(Alloc.getAllocatorExpr(), SM, CallerCtx); +  } +  case CFGElement::TemporaryDtor: { +    // Temporary destructors are for temporaries. They die immediately at around +    // the location of CXXBindTemporaryExpr. If they are lifetime-extended, +    // they'd be dealt with via an AutomaticObjectDtor instead. +    const auto &Dtor = Source.castAs<CFGTemporaryDtor>(); +    return PathDiagnosticLocation::createEnd(Dtor.getBindTemporaryExpr(), SM, +                                             CallerCtx); +  } +  case CFGElement::ScopeBegin: +  case CFGElement::ScopeEnd: +    llvm_unreachable("not yet implemented!"); +  case CFGElement::LifetimeEnds: +  case CFGElement::LoopExit: +    llvm_unreachable("CFGElement kind should not be on callsite!"); +  } + +  llvm_unreachable("Unknown CFGElement kind"); +} + +PathDiagnosticLocation +PathDiagnosticLocation::createBegin(const Decl *D, +                                    const SourceManager &SM) { +  return PathDiagnosticLocation(D->getBeginLoc(), SM, SingleLocK); +} + +PathDiagnosticLocation +PathDiagnosticLocation::createBegin(const Stmt *S, +                                    const SourceManager &SM, +                                    LocationOrAnalysisDeclContext LAC) { +  return PathDiagnosticLocation(getValidSourceLocation(S, LAC), +                                SM, SingleLocK); +} + +PathDiagnosticLocation +PathDiagnosticLocation::createEnd(const Stmt *S, +                                  const SourceManager &SM, +                                  LocationOrAnalysisDeclContext LAC) { +  if (const auto *CS = dyn_cast<CompoundStmt>(S)) +    return createEndBrace(CS, SM); +  return PathDiagnosticLocation(getValidSourceLocation(S, LAC, /*End=*/true), +                                SM, SingleLocK); +} + +PathDiagnosticLocation +PathDiagnosticLocation::createOperatorLoc(const BinaryOperator *BO, +                                          const SourceManager &SM) { +  return PathDiagnosticLocation(BO->getOperatorLoc(), SM, SingleLocK); +} + +PathDiagnosticLocation +PathDiagnosticLocation::createConditionalColonLoc( +                                            const ConditionalOperator *CO, +                                            const SourceManager &SM) { +  return PathDiagnosticLocation(CO->getColonLoc(), SM, SingleLocK); +} + +PathDiagnosticLocation +PathDiagnosticLocation::createMemberLoc(const MemberExpr *ME, +                                        const SourceManager &SM) { + +  assert(ME->getMemberLoc().isValid() || ME->getBeginLoc().isValid()); + +  // In some cases, getMemberLoc isn't valid -- in this case we'll return with +  // some other related valid SourceLocation. +  if (ME->getMemberLoc().isValid()) +    return PathDiagnosticLocation(ME->getMemberLoc(), SM, SingleLocK); + +  return PathDiagnosticLocation(ME->getBeginLoc(), SM, SingleLocK); +} + +PathDiagnosticLocation +PathDiagnosticLocation::createBeginBrace(const CompoundStmt *CS, +                                         const SourceManager &SM) { +  SourceLocation L = CS->getLBracLoc(); +  return PathDiagnosticLocation(L, SM, SingleLocK); +} + +PathDiagnosticLocation +PathDiagnosticLocation::createEndBrace(const CompoundStmt *CS, +                                       const SourceManager &SM) { +  SourceLocation L = CS->getRBracLoc(); +  return PathDiagnosticLocation(L, SM, SingleLocK); +} + +PathDiagnosticLocation +PathDiagnosticLocation::createDeclBegin(const LocationContext *LC, +                                        const SourceManager &SM) { +  // FIXME: Should handle CXXTryStmt if analyser starts supporting C++. +  if (const auto *CS = dyn_cast_or_null<CompoundStmt>(LC->getDecl()->getBody())) +    if (!CS->body_empty()) { +      SourceLocation Loc = (*CS->body_begin())->getBeginLoc(); +      return PathDiagnosticLocation(Loc, SM, SingleLocK); +    } + +  return PathDiagnosticLocation(); +} + +PathDiagnosticLocation +PathDiagnosticLocation::createDeclEnd(const LocationContext *LC, +                                      const SourceManager &SM) { +  SourceLocation L = LC->getDecl()->getBodyRBrace(); +  return PathDiagnosticLocation(L, SM, SingleLocK); +} + +PathDiagnosticLocation +PathDiagnosticLocation::create(const ProgramPoint& P, +                               const SourceManager &SMng) { +  const Stmt* S = nullptr; +  if (Optional<BlockEdge> BE = P.getAs<BlockEdge>()) { +    const CFGBlock *BSrc = BE->getSrc(); +    if (BSrc->getTerminator().isVirtualBaseBranch()) { +      // TODO: VirtualBaseBranches should also appear for destructors. +      // In this case we should put the diagnostic at the end of decl. +      return PathDiagnosticLocation::createBegin( +          P.getLocationContext()->getDecl(), SMng); + +    } else { +      S = BSrc->getTerminatorCondition(); +      if (!S) { +        // If the BlockEdge has no terminator condition statement but its +        // source is the entry of the CFG (e.g. a checker crated the branch at +        // the beginning of a function), use the function's declaration instead. +        assert(BSrc == &BSrc->getParent()->getEntry() && "CFGBlock has no " +               "TerminatorCondition and is not the enrty block of the CFG"); +        return PathDiagnosticLocation::createBegin( +            P.getLocationContext()->getDecl(), SMng); +      } +    } +  } else if (Optional<StmtPoint> SP = P.getAs<StmtPoint>()) { +    S = SP->getStmt(); +    if (P.getAs<PostStmtPurgeDeadSymbols>()) +      return PathDiagnosticLocation::createEnd(S, SMng, P.getLocationContext()); +  } else if (Optional<PostInitializer> PIP = P.getAs<PostInitializer>()) { +    return PathDiagnosticLocation(PIP->getInitializer()->getSourceLocation(), +                                  SMng); +  } else if (Optional<PreImplicitCall> PIC = P.getAs<PreImplicitCall>()) { +    return PathDiagnosticLocation(PIC->getLocation(), SMng); +  } else if (Optional<PostImplicitCall> PIE = P.getAs<PostImplicitCall>()) { +    return PathDiagnosticLocation(PIE->getLocation(), SMng); +  } else if (Optional<CallEnter> CE = P.getAs<CallEnter>()) { +    return getLocationForCaller(CE->getCalleeContext(), +                                CE->getLocationContext(), +                                SMng); +  } else if (Optional<CallExitEnd> CEE = P.getAs<CallExitEnd>()) { +    return getLocationForCaller(CEE->getCalleeContext(), +                                CEE->getLocationContext(), +                                SMng); +  } else if (auto CEB = P.getAs<CallExitBegin>()) { +    if (const ReturnStmt *RS = CEB->getReturnStmt()) +      return PathDiagnosticLocation::createBegin(RS, SMng, +                                                 CEB->getLocationContext()); +    return PathDiagnosticLocation( +        CEB->getLocationContext()->getDecl()->getSourceRange().getEnd(), SMng); +  } else if (Optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) { +    if (Optional<CFGElement> BlockFront = BE->getFirstElement()) { +      if (auto StmtElt = BlockFront->getAs<CFGStmt>()) { +        return PathDiagnosticLocation(StmtElt->getStmt()->getBeginLoc(), SMng); +      } else if (auto NewAllocElt = BlockFront->getAs<CFGNewAllocator>()) { +        return PathDiagnosticLocation( +            NewAllocElt->getAllocatorExpr()->getBeginLoc(), SMng); +      } +      llvm_unreachable("Unexpected CFG element at front of block"); +    } + +    return PathDiagnosticLocation( +        BE->getBlock()->getTerminatorStmt()->getBeginLoc(), SMng); +  } else if (Optional<FunctionExitPoint> FE = P.getAs<FunctionExitPoint>()) { +    return PathDiagnosticLocation(FE->getStmt(), SMng, +                                  FE->getLocationContext()); +  } else { +    llvm_unreachable("Unexpected ProgramPoint"); +  } + +  return PathDiagnosticLocation(S, SMng, P.getLocationContext()); +} + +PathDiagnosticLocation PathDiagnosticLocation::createSingleLocation( +                                           const PathDiagnosticLocation &PDL) { +  FullSourceLoc L = PDL.asLocation(); +  return PathDiagnosticLocation(L, L.getManager(), SingleLocK); +} + +FullSourceLoc +  PathDiagnosticLocation::genLocation(SourceLocation L, +                                      LocationOrAnalysisDeclContext LAC) const { +  assert(isValid()); +  // Note that we want a 'switch' here so that the compiler can warn us in +  // case we add more cases. +  switch (K) { +    case SingleLocK: +    case RangeK: +      break; +    case StmtK: +      // Defensive checking. +      if (!S) +        break; +      return FullSourceLoc(getValidSourceLocation(S, LAC), +                           const_cast<SourceManager&>(*SM)); +    case DeclK: +      // Defensive checking. +      if (!D) +        break; +      return FullSourceLoc(D->getLocation(), const_cast<SourceManager&>(*SM)); +  } + +  return FullSourceLoc(L, const_cast<SourceManager&>(*SM)); +} + +PathDiagnosticRange +  PathDiagnosticLocation::genRange(LocationOrAnalysisDeclContext LAC) const { +  assert(isValid()); +  // Note that we want a 'switch' here so that the compiler can warn us in +  // case we add more cases. +  switch (K) { +    case SingleLocK: +      return PathDiagnosticRange(SourceRange(Loc,Loc), true); +    case RangeK: +      break; +    case StmtK: { +      const Stmt *S = asStmt(); +      switch (S->getStmtClass()) { +        default: +          break; +        case Stmt::DeclStmtClass: { +          const auto *DS = cast<DeclStmt>(S); +          if (DS->isSingleDecl()) { +            // Should always be the case, but we'll be defensive. +            return SourceRange(DS->getBeginLoc(), +                               DS->getSingleDecl()->getLocation()); +          } +          break; +        } +          // FIXME: Provide better range information for different +          //  terminators. +        case Stmt::IfStmtClass: +        case Stmt::WhileStmtClass: +        case Stmt::DoStmtClass: +        case Stmt::ForStmtClass: +        case Stmt::ChooseExprClass: +        case Stmt::IndirectGotoStmtClass: +        case Stmt::SwitchStmtClass: +        case Stmt::BinaryConditionalOperatorClass: +        case Stmt::ConditionalOperatorClass: +        case Stmt::ObjCForCollectionStmtClass: { +          SourceLocation L = getValidSourceLocation(S, LAC); +          return SourceRange(L, L); +        } +      } +      SourceRange R = S->getSourceRange(); +      if (R.isValid()) +        return R; +      break; +    } +    case DeclK: +      if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) +        return MD->getSourceRange(); +      if (const auto *FD = dyn_cast<FunctionDecl>(D)) { +        if (Stmt *Body = FD->getBody()) +          return Body->getSourceRange(); +      } +      else { +        SourceLocation L = D->getLocation(); +        return PathDiagnosticRange(SourceRange(L, L), true); +      } +  } + +  return SourceRange(Loc, Loc); +} + +void PathDiagnosticLocation::flatten() { +  if (K == StmtK) { +    K = RangeK; +    S = nullptr; +    D = nullptr; +  } +  else if (K == DeclK) { +    K = SingleLocK; +    S = nullptr; +    D = nullptr; +  } +} + +//===----------------------------------------------------------------------===// +// Manipulation of PathDiagnosticCallPieces. +//===----------------------------------------------------------------------===// + +std::shared_ptr<PathDiagnosticCallPiece> +PathDiagnosticCallPiece::construct(const CallExitEnd &CE, +                                   const SourceManager &SM) { +  const Decl *caller = CE.getLocationContext()->getDecl(); +  PathDiagnosticLocation pos = getLocationForCaller(CE.getCalleeContext(), +                                                    CE.getLocationContext(), +                                                    SM); +  return std::shared_ptr<PathDiagnosticCallPiece>( +      new PathDiagnosticCallPiece(caller, pos)); +} + +PathDiagnosticCallPiece * +PathDiagnosticCallPiece::construct(PathPieces &path, +                                   const Decl *caller) { +  std::shared_ptr<PathDiagnosticCallPiece> C( +      new PathDiagnosticCallPiece(path, caller)); +  path.clear(); +  auto *R = C.get(); +  path.push_front(std::move(C)); +  return R; +} + +void PathDiagnosticCallPiece::setCallee(const CallEnter &CE, +                                        const SourceManager &SM) { +  const StackFrameContext *CalleeCtx = CE.getCalleeContext(); +  Callee = CalleeCtx->getDecl(); + +  callEnterWithin = PathDiagnosticLocation::createBegin(Callee, SM); +  callEnter = getLocationForCaller(CalleeCtx, CE.getLocationContext(), SM); + +  // Autosynthesized property accessors are special because we'd never +  // pop back up to non-autosynthesized code until we leave them. +  // This is not generally true for autosynthesized callees, which may call +  // non-autosynthesized callbacks. +  // Unless set here, the IsCalleeAnAutosynthesizedPropertyAccessor flag +  // defaults to false. +  if (const auto *MD = dyn_cast<ObjCMethodDecl>(Callee)) +    IsCalleeAnAutosynthesizedPropertyAccessor = ( +        MD->isPropertyAccessor() && +        CalleeCtx->getAnalysisDeclContext()->isBodyAutosynthesized()); +} + +static void describeTemplateParameters(raw_ostream &Out, +                                       const ArrayRef<TemplateArgument> TAList, +                                       const LangOptions &LO, +                                       StringRef Prefix = StringRef(), +                                       StringRef Postfix = StringRef()); + +static void describeTemplateParameter(raw_ostream &Out, +                                      const TemplateArgument &TArg, +                                      const LangOptions &LO) { + +  if (TArg.getKind() == TemplateArgument::ArgKind::Pack) { +    describeTemplateParameters(Out, TArg.getPackAsArray(), LO); +  } else { +    TArg.print(PrintingPolicy(LO), Out); +  } +} + +static void describeTemplateParameters(raw_ostream &Out, +                                       const ArrayRef<TemplateArgument> TAList, +                                       const LangOptions &LO, +                                       StringRef Prefix, StringRef Postfix) { +  if (TAList.empty()) +    return; + +  Out << Prefix; +  for (int I = 0, Last = TAList.size() - 1; I != Last; ++I) { +    describeTemplateParameter(Out, TAList[I], LO); +    Out << ", "; +  } +  describeTemplateParameter(Out, TAList[TAList.size() - 1], LO); +  Out << Postfix; +} + +static void describeClass(raw_ostream &Out, const CXXRecordDecl *D, +                          StringRef Prefix = StringRef()) { +  if (!D->getIdentifier()) +    return; +  Out << Prefix << '\'' << *D; +  if (const auto T = dyn_cast<ClassTemplateSpecializationDecl>(D)) +    describeTemplateParameters(Out, T->getTemplateArgs().asArray(), +                               D->getASTContext().getLangOpts(), "<", ">"); + +  Out << '\''; +} + +static bool describeCodeDecl(raw_ostream &Out, const Decl *D, +                             bool ExtendedDescription, +                             StringRef Prefix = StringRef()) { +  if (!D) +    return false; + +  if (isa<BlockDecl>(D)) { +    if (ExtendedDescription) +      Out << Prefix << "anonymous block"; +    return ExtendedDescription; +  } + +  if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) { +    Out << Prefix; +    if (ExtendedDescription && !MD->isUserProvided()) { +      if (MD->isExplicitlyDefaulted()) +        Out << "defaulted "; +      else +        Out << "implicit "; +    } + +    if (const auto *CD = dyn_cast<CXXConstructorDecl>(MD)) { +      if (CD->isDefaultConstructor()) +        Out << "default "; +      else if (CD->isCopyConstructor()) +        Out << "copy "; +      else if (CD->isMoveConstructor()) +        Out << "move "; + +      Out << "constructor"; +      describeClass(Out, MD->getParent(), " for "); +    } else if (isa<CXXDestructorDecl>(MD)) { +      if (!MD->isUserProvided()) { +        Out << "destructor"; +        describeClass(Out, MD->getParent(), " for "); +      } else { +        // Use ~Foo for explicitly-written destructors. +        Out << "'" << *MD << "'"; +      } +    } else if (MD->isCopyAssignmentOperator()) { +        Out << "copy assignment operator"; +        describeClass(Out, MD->getParent(), " for "); +    } else if (MD->isMoveAssignmentOperator()) { +        Out << "move assignment operator"; +        describeClass(Out, MD->getParent(), " for "); +    } else { +      if (MD->getParent()->getIdentifier()) +        Out << "'" << *MD->getParent() << "::" << *MD << "'"; +      else +        Out << "'" << *MD << "'"; +    } + +    return true; +  } + +  Out << Prefix << '\'' << cast<NamedDecl>(*D); + +  // Adding template parameters. +  if (const auto FD = dyn_cast<FunctionDecl>(D)) +    if (const TemplateArgumentList *TAList = +                                    FD->getTemplateSpecializationArgs()) +      describeTemplateParameters(Out, TAList->asArray(), +                                 FD->getASTContext().getLangOpts(), "<", ">"); + +  Out << '\''; +  return true; +} + +std::shared_ptr<PathDiagnosticEventPiece> +PathDiagnosticCallPiece::getCallEnterEvent() const { +  // We do not produce call enters and call exits for autosynthesized property +  // accessors. We do generally produce them for other functions coming from +  // the body farm because they may call callbacks that bring us back into +  // visible code. +  if (!Callee || IsCalleeAnAutosynthesizedPropertyAccessor) +    return nullptr; + +  SmallString<256> buf; +  llvm::raw_svector_ostream Out(buf); + +  Out << "Calling "; +  describeCodeDecl(Out, Callee, /*ExtendedDescription=*/true); + +  assert(callEnter.asLocation().isValid()); +  return std::make_shared<PathDiagnosticEventPiece>(callEnter, Out.str()); +} + +std::shared_ptr<PathDiagnosticEventPiece> +PathDiagnosticCallPiece::getCallEnterWithinCallerEvent() const { +  if (!callEnterWithin.asLocation().isValid()) +    return nullptr; +  if (Callee->isImplicit() || !Callee->hasBody()) +    return nullptr; +  if (const auto *MD = dyn_cast<CXXMethodDecl>(Callee)) +    if (MD->isDefaulted()) +      return nullptr; + +  SmallString<256> buf; +  llvm::raw_svector_ostream Out(buf); + +  Out << "Entered call"; +  describeCodeDecl(Out, Caller, /*ExtendedDescription=*/false, " from "); + +  return std::make_shared<PathDiagnosticEventPiece>(callEnterWithin, Out.str()); +} + +std::shared_ptr<PathDiagnosticEventPiece> +PathDiagnosticCallPiece::getCallExitEvent() const { +  // We do not produce call enters and call exits for autosynthesized property +  // accessors. We do generally produce them for other functions coming from +  // the body farm because they may call callbacks that bring us back into +  // visible code. +  if (NoExit || IsCalleeAnAutosynthesizedPropertyAccessor) +    return nullptr; + +  SmallString<256> buf; +  llvm::raw_svector_ostream Out(buf); + +  if (!CallStackMessage.empty()) { +    Out << CallStackMessage; +  } else { +    bool DidDescribe = describeCodeDecl(Out, Callee, +                                        /*ExtendedDescription=*/false, +                                        "Returning from "); +    if (!DidDescribe) +      Out << "Returning to caller"; +  } + +  assert(callReturn.asLocation().isValid()); +  return std::make_shared<PathDiagnosticEventPiece>(callReturn, Out.str()); +} + +static void compute_path_size(const PathPieces &pieces, unsigned &size) { +  for (const auto &I : pieces) { +    const PathDiagnosticPiece *piece = I.get(); +    if (const auto *cp = dyn_cast<PathDiagnosticCallPiece>(piece)) +      compute_path_size(cp->path, size); +    else +      ++size; +  } +} + +unsigned PathDiagnostic::full_size() { +  unsigned size = 0; +  compute_path_size(path, size); +  return size; +} + +//===----------------------------------------------------------------------===// +// FoldingSet profiling methods. +//===----------------------------------------------------------------------===// + +void PathDiagnosticLocation::Profile(llvm::FoldingSetNodeID &ID) const { +  ID.AddInteger(Range.getBegin().getRawEncoding()); +  ID.AddInteger(Range.getEnd().getRawEncoding()); +  ID.AddInteger(Loc.getRawEncoding()); +} + +void PathDiagnosticPiece::Profile(llvm::FoldingSetNodeID &ID) const { +  ID.AddInteger((unsigned) getKind()); +  ID.AddString(str); +  // FIXME: Add profiling support for code hints. +  ID.AddInteger((unsigned) getDisplayHint()); +  ArrayRef<SourceRange> Ranges = getRanges(); +  for (const auto &I : Ranges) { +    ID.AddInteger(I.getBegin().getRawEncoding()); +    ID.AddInteger(I.getEnd().getRawEncoding()); +  } +} + +void PathDiagnosticCallPiece::Profile(llvm::FoldingSetNodeID &ID) const { +  PathDiagnosticPiece::Profile(ID); +  for (const auto &I : path) +    ID.Add(*I); +} + +void PathDiagnosticSpotPiece::Profile(llvm::FoldingSetNodeID &ID) const { +  PathDiagnosticPiece::Profile(ID); +  ID.Add(Pos); +} + +void PathDiagnosticControlFlowPiece::Profile(llvm::FoldingSetNodeID &ID) const { +  PathDiagnosticPiece::Profile(ID); +  for (const auto &I : *this) +    ID.Add(I); +} + +void PathDiagnosticMacroPiece::Profile(llvm::FoldingSetNodeID &ID) const { +  PathDiagnosticSpotPiece::Profile(ID); +  for (const auto &I : subPieces) +    ID.Add(*I); +} + +void PathDiagnosticNotePiece::Profile(llvm::FoldingSetNodeID &ID) const { +  PathDiagnosticSpotPiece::Profile(ID); +} + +void PathDiagnosticPopUpPiece::Profile(llvm::FoldingSetNodeID &ID) const { +  PathDiagnosticSpotPiece::Profile(ID); +} + +void PathDiagnostic::Profile(llvm::FoldingSetNodeID &ID) const { +  ID.Add(getLocation()); +  ID.AddString(BugType); +  ID.AddString(VerboseDesc); +  ID.AddString(Category); +} + +void PathDiagnostic::FullProfile(llvm::FoldingSetNodeID &ID) const { +  Profile(ID); +  for (const auto &I : path) +    ID.Add(*I); +  for (meta_iterator I = meta_begin(), E = meta_end(); I != E; ++I) +    ID.AddString(*I); +} + +LLVM_DUMP_METHOD void PathPieces::dump() const { +  unsigned index = 0; +  for (PathPieces::const_iterator I = begin(), E = end(); I != E; ++I) { +    llvm::errs() << "[" << index++ << "]  "; +    (*I)->dump(); +    llvm::errs() << "\n"; +  } +} + +LLVM_DUMP_METHOD void PathDiagnosticCallPiece::dump() const { +  llvm::errs() << "CALL\n--------------\n"; + +  if (const Stmt *SLoc = getLocation().getStmtOrNull()) +    SLoc->dump(); +  else if (const auto *ND = dyn_cast_or_null<NamedDecl>(getCallee())) +    llvm::errs() << *ND << "\n"; +  else +    getLocation().dump(); +} + +LLVM_DUMP_METHOD void PathDiagnosticEventPiece::dump() const { +  llvm::errs() << "EVENT\n--------------\n"; +  llvm::errs() << getString() << "\n"; +  llvm::errs() << " ---- at ----\n"; +  getLocation().dump(); +} + +LLVM_DUMP_METHOD void PathDiagnosticControlFlowPiece::dump() const { +  llvm::errs() << "CONTROL\n--------------\n"; +  getStartLocation().dump(); +  llvm::errs() << " ---- to ----\n"; +  getEndLocation().dump(); +} + +LLVM_DUMP_METHOD void PathDiagnosticMacroPiece::dump() const { +  llvm::errs() << "MACRO\n--------------\n"; +  // FIXME: Print which macro is being invoked. +} + +LLVM_DUMP_METHOD void PathDiagnosticNotePiece::dump() const { +  llvm::errs() << "NOTE\n--------------\n"; +  llvm::errs() << getString() << "\n"; +  llvm::errs() << " ---- at ----\n"; +  getLocation().dump(); +} + +LLVM_DUMP_METHOD void PathDiagnosticPopUpPiece::dump() const { +  llvm::errs() << "POP-UP\n--------------\n"; +  llvm::errs() << getString() << "\n"; +  llvm::errs() << " ---- at ----\n"; +  getLocation().dump(); +} + +LLVM_DUMP_METHOD void PathDiagnosticLocation::dump() const { +  if (!isValid()) { +    llvm::errs() << "<INVALID>\n"; +    return; +  } + +  switch (K) { +  case RangeK: +    // FIXME: actually print the range. +    llvm::errs() << "<range>\n"; +    break; +  case SingleLocK: +    asLocation().dump(); +    llvm::errs() << "\n"; +    break; +  case StmtK: +    if (S) +      S->dump(); +    else +      llvm::errs() << "<NULL STMT>\n"; +    break; +  case DeclK: +    if (const auto *ND = dyn_cast_or_null<NamedDecl>(D)) +      llvm::errs() << *ND << "\n"; +    else if (isa<BlockDecl>(D)) +      // FIXME: Make this nicer. +      llvm::errs() << "<block>\n"; +    else if (D) +      llvm::errs() << "<unknown decl>\n"; +    else +      llvm::errs() << "<NULL DECL>\n"; +    break; +  } +} | 
