Пример #1
0
Replacement IncludeDirectives::addAngledInclude(const clang::FileEntry *File,
                                                llvm::StringRef Include) {
  FileID FID = Sources.translateFile(File);
  assert(FID.isValid() && "Invalid file entry given!");

  if (hasInclude(File, Include))
    return Replacement();

  unsigned Offset, NLFlags;
  std::tie(Offset, NLFlags) = angledIncludeInsertionOffset(FID);

  StringRef EOL = guessEOL(Sources, FID);
  llvm::SmallString<32> InsertionText;
  if (NLFlags & NL_Prepend)
    InsertionText += EOL;
  if (NLFlags & NL_PrependAnother)
    InsertionText += EOL;
  InsertionText += "#include <";
  InsertionText += Include;
  InsertionText += ">";
  if (NLFlags & NL_AppendTwice) {
    InsertionText += EOL;
    InsertionText += EOL;
  }
  return Replacement(File->getName(), Offset, 0, InsertionText);
}
Пример #2
0
/// EnterMainSourceFile - Enter the specified FileID as the main source file,
/// which implicitly adds the builtin defines etc.
void Preprocessor::EnterMainSourceFile() {
  // We do not allow the preprocessor to reenter the main file.  Doing so will
  // cause FileID's to accumulate information from both runs (e.g. #line
  // information) and predefined macros aren't guaranteed to be set properly.
  assert(NumEnteredSourceFiles == 0 && "Cannot reenter the main file!");
  FileID MainFileID = SourceMgr.getMainFileID();

  // If MainFileID is loaded it means we loaded an AST file, no need to enter
  // a main file.
  if (!SourceMgr.isLoadedFileID(MainFileID)) {
    // Enter the main file source buffer.
    EnterSourceFile(MainFileID, nullptr, SourceLocation());

    // If we've been asked to skip bytes in the main file (e.g., as part of a
    // precompiled preamble), do so now.
    if (SkipMainFilePreamble.first > 0)
      CurLexer->SetByteOffset(SkipMainFilePreamble.first,
                              SkipMainFilePreamble.second);

    // Tell the header info that the main file was entered.  If the file is later
    // #imported, it won't be re-entered.
    if (const FileEntry *FE = SourceMgr.getFileEntryForID(MainFileID))
      HeaderInfo.IncrementIncludeCount(FE);
  }

  // Preprocess Predefines to populate the initial preprocessor state.
  std::unique_ptr<llvm::MemoryBuffer> SB =
    llvm::MemoryBuffer::getMemBufferCopy(Predefines, "<built-in>");
  assert(SB && "Cannot create predefined source buffer");
  FileID FID = SourceMgr.createFileID(std::move(SB));
  assert(FID.isValid() && "Could not create FileID for predefines?");
  setPredefinesFileID(FID);

  // Start parsing the predefines.
  EnterSourceFile(FID, nullptr, SourceLocation());

  if (!PPOpts->PCHThroughHeader.empty()) {
    // Lookup and save the FileID for the through header. If it isn't found
    // in the search path, it's a fatal error.
    const DirectoryLookup *CurDir;
    const FileEntry *File = LookupFile(
        SourceLocation(), PPOpts->PCHThroughHeader,
        /*isAngled=*/false, /*FromDir=*/nullptr, /*FromFile=*/nullptr, CurDir,
        /*SearchPath=*/nullptr, /*RelativePath=*/nullptr,
        /*SuggestedModule=*/nullptr, /*IsMapped=*/nullptr,
        /*IsFrameworkFound=*/nullptr);
    if (!File) {
      Diag(SourceLocation(), diag::err_pp_through_header_not_found)
          << PPOpts->PCHThroughHeader;
      return;
    }
    setPCHThroughHeaderFileID(
        SourceMgr.createFileID(File, SourceLocation(), SrcMgr::C_User));
  }

  // Skip tokens from the Predefines and if needed the main file.
  if ((usingPCHWithThroughHeader() && SkippingUntilPCHThroughHeader) ||
      (usingPCHWithPragmaHdrStop() && SkippingUntilPragmaHdrStop))
    SkipTokensWhileUsingPCH();
}
static bool isPreprocessedEntityIfInFileID(PreprocessedEntity *PPE, FileID FID,
                                           SourceManager &SM) {
  assert(FID.isValid());
  if (!PPE)
    return false;

  SourceLocation Loc = PPE->getSourceRange().getBegin();
  if (Loc.isInvalid())
    return false;

  return SM.isInFileID(SM.getFileLoc(Loc), FID);
}
/// \brief Convenience function to get rewritten content for \c Filename from
/// \c Rewrites.
///
/// \pre Replacements[i].getFilePath() == Replacements[i+1].getFilePath().
/// \post Replacements.empty() -> Result.empty()
///
/// \param[in] Replacements Replacements to apply
/// \param[in] Rewrites Rewriter to use to apply replacements.
/// \param[out] Result Contents of the file after applying replacements if
/// replacements were provided.
///
/// \returns \parblock
///          \li true if all replacements were applied successfully.
///          \li false if at least one replacement failed to apply.
static bool
getRewrittenData(const std::vector<tooling::Replacement> &Replacements,
                 Rewriter &Rewrites, std::string &Result) {
  if (Replacements.empty()) return true;

  if (!applyAllReplacements(Replacements, Rewrites))
    return false;

  SourceManager &SM = Rewrites.getSourceMgr();
  FileManager &Files = SM.getFileManager();

  StringRef FileName = Replacements.begin()->getFilePath();
  const clang::FileEntry *Entry = Files.getFile(FileName);
  assert(Entry && "Expected an existing file");
  FileID ID = SM.translateFile(Entry);
  assert(ID.isValid() && "Expected a valid FileID");
  const RewriteBuffer *Buffer = Rewrites.getRewriteBufferFor(ID);
  Result = std::string(Buffer->begin(), Buffer->end());

  return true;
}
Пример #5
0
/// EnterMainSourceFile - Enter the specified FileID as the main source file,
/// which implicitly adds the builtin defines etc.
void Preprocessor::EnterMainSourceFile() {
  // We do not allow the preprocessor to reenter the main file.  Doing so will
  // cause FileID's to accumulate information from both runs (e.g. #line
  // information) and predefined macros aren't guaranteed to be set properly.
  assert(NumEnteredSourceFiles == 0 && "Cannot reenter the main file!");
  FileID MainFileID = SourceMgr.getMainFileID();

  // If MainFileID is loaded it means we loaded an AST file, no need to enter
  // a main file.
  if (!SourceMgr.isLoadedFileID(MainFileID)) {
    // Enter the main file source buffer.
    EnterSourceFile(MainFileID, nullptr, SourceLocation());

    // If we've been asked to skip bytes in the main file (e.g., as part of a
    // precompiled preamble), do so now.
    if (SkipMainFilePreamble.first > 0)
      CurLexer->SkipBytes(SkipMainFilePreamble.first, 
                          SkipMainFilePreamble.second);
    
    // Tell the header info that the main file was entered.  If the file is later
    // #imported, it won't be re-entered.
    if (const FileEntry *FE = SourceMgr.getFileEntryForID(MainFileID))
      HeaderInfo.IncrementIncludeCount(FE);
  }

  // Preprocess Predefines to populate the initial preprocessor state.
  std::unique_ptr<llvm::MemoryBuffer> SB =
    llvm::MemoryBuffer::getMemBufferCopy(Predefines, "<built-in>");
  assert(SB && "Cannot create predefined source buffer");
  FileID FID = SourceMgr.createFileID(std::move(SB));
  assert(FID.isValid() && "Could not create FileID for predefines?");
  setPredefinesFileID(FID);

  // Start parsing the predefines.
  EnterSourceFile(FID, nullptr, SourceLocation());
}
Пример #6
0
void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
  FullCommentParts Parts(C, Traits);

  const DeclInfo *DI = C->getDeclInfo();
  StringRef RootEndTag;
  if (DI) {
    switch (DI->getKind()) {
    case DeclInfo::OtherKind:
      RootEndTag = "</Other>";
      Result << "<Other";
      break;
    case DeclInfo::FunctionKind:
      RootEndTag = "</Function>";
      Result << "<Function";
      switch (DI->TemplateKind) {
      case DeclInfo::NotTemplate:
        break;
      case DeclInfo::Template:
        Result << " templateKind=\"template\"";
        break;
      case DeclInfo::TemplateSpecialization:
        Result << " templateKind=\"specialization\"";
        break;
      case DeclInfo::TemplatePartialSpecialization:
        llvm_unreachable("partial specializations of functions "
                         "are not allowed in C++");
      }
      if (DI->IsInstanceMethod)
        Result << " isInstanceMethod=\"1\"";
      if (DI->IsClassMethod)
        Result << " isClassMethod=\"1\"";
      break;
    case DeclInfo::ClassKind:
      RootEndTag = "</Class>";
      Result << "<Class";
      switch (DI->TemplateKind) {
      case DeclInfo::NotTemplate:
        break;
      case DeclInfo::Template:
        Result << " templateKind=\"template\"";
        break;
      case DeclInfo::TemplateSpecialization:
        Result << " templateKind=\"specialization\"";
        break;
      case DeclInfo::TemplatePartialSpecialization:
        Result << " templateKind=\"partialSpecialization\"";
        break;
      }
      break;
    case DeclInfo::VariableKind:
      RootEndTag = "</Variable>";
      Result << "<Variable";
      break;
    case DeclInfo::NamespaceKind:
      RootEndTag = "</Namespace>";
      Result << "<Namespace";
      break;
    case DeclInfo::TypedefKind:
      RootEndTag = "</Typedef>";
      Result << "<Typedef";
      break;
    case DeclInfo::EnumKind:
      RootEndTag = "</Enum>";
      Result << "<Enum";
      break;
    }

    {
      // Print line and column number.
      SourceLocation Loc = DI->CurrentDecl->getLocation();
      std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
      FileID FID = LocInfo.first;
      unsigned FileOffset = LocInfo.second;

      if (FID.isValid()) {
        if (const FileEntry *FE = SM.getFileEntryForID(FID)) {
          Result << " file=\"";
          appendToResultWithXMLEscaping(FE->getName());
          Result << "\"";
        }
        Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
               << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
               << "\"";
      }
    }

    // Finish the root tag.
    Result << ">";

    bool FoundName = false;
    if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) {
      if (DeclarationName DeclName = ND->getDeclName()) {
        Result << "<Name>";
        std::string Name = DeclName.getAsString();
        appendToResultWithXMLEscaping(Name);
        FoundName = true;
        Result << "</Name>";
      }
    }
    if (!FoundName)
      Result << "<Name>&lt;anonymous&gt;</Name>";

    {
      // Print USR.
      SmallString<128> USR;
      generateUSRForDecl(DI->CommentDecl, USR);
      if (!USR.empty()) {
        Result << "<USR>";
        appendToResultWithXMLEscaping(USR);
        Result << "</USR>";
      }
    }
  } else {
    // No DeclInfo -- just emit some root tag and name tag.
    RootEndTag = "</Other>";
    Result << "<Other><Name>unknown</Name>";
  }

  if (Parts.Headerfile) {
    Result << "<Headerfile>";
    visit(Parts.Headerfile);
    Result << "</Headerfile>";
  }

  {
    // Pretty-print the declaration.
    Result << "<Declaration>";
    SmallString<128> Declaration;
    getSourceTextOfDeclaration(DI, Declaration);
    formatTextOfDeclaration(DI, Declaration);
    appendToResultWithXMLEscaping(Declaration);
    Result << "</Declaration>";
  }

  bool FirstParagraphIsBrief = false;
  if (Parts.Brief) {
    Result << "<Abstract>";
    visit(Parts.Brief);
    Result << "</Abstract>";
  } else if (Parts.FirstParagraph) {
    Result << "<Abstract>";
    visit(Parts.FirstParagraph);
    Result << "</Abstract>";
    FirstParagraphIsBrief = true;
  }

  if (Parts.TParams.size() != 0) {
    Result << "<TemplateParameters>";
    for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
      visit(Parts.TParams[i]);
    Result << "</TemplateParameters>";
  }

  if (Parts.Params.size() != 0) {
    Result << "<Parameters>";
    for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
      visit(Parts.Params[i]);
    Result << "</Parameters>";
  }

  if (Parts.Exceptions.size() != 0) {
    Result << "<Exceptions>";
    for (unsigned i = 0, e = Parts.Exceptions.size(); i != e; ++i)
      visit(Parts.Exceptions[i]);
    Result << "</Exceptions>";
  }

  if (Parts.Returns.size() != 0) {
    Result << "<ResultDiscussion>";
    for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
      visit(Parts.Returns[i]);
    Result << "</ResultDiscussion>";
  }

  if (DI->CommentDecl->hasAttrs()) {
    const AttrVec &Attrs = DI->CommentDecl->getAttrs();
    for (unsigned i = 0, e = Attrs.size(); i != e; i++) {
      const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]);
      if (!AA) {
        if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) {
          if (DA->getMessage().empty())
            Result << "<Deprecated/>";
          else {
            Result << "<Deprecated>";
            appendToResultWithXMLEscaping(DA->getMessage());
            Result << "</Deprecated>";
          }
        }
        else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) {
          if (UA->getMessage().empty())
            Result << "<Unavailable/>";
          else {
            Result << "<Unavailable>";
            appendToResultWithXMLEscaping(UA->getMessage());
            Result << "</Unavailable>";
          }
        }
        continue;
      }

      // 'availability' attribute.
      Result << "<Availability";
      StringRef Distribution;
      if (AA->getPlatform()) {
        Distribution = AvailabilityAttr::getPrettyPlatformName(
                                        AA->getPlatform()->getName());
        if (Distribution.empty())
          Distribution = AA->getPlatform()->getName();
      }
      Result << " distribution=\"" << Distribution << "\">";
      VersionTuple IntroducedInVersion = AA->getIntroduced();
      if (!IntroducedInVersion.empty()) {
        Result << "<IntroducedInVersion>"
               << IntroducedInVersion.getAsString()
               << "</IntroducedInVersion>";
      }
      VersionTuple DeprecatedInVersion = AA->getDeprecated();
      if (!DeprecatedInVersion.empty()) {
        Result << "<DeprecatedInVersion>"
               << DeprecatedInVersion.getAsString()
               << "</DeprecatedInVersion>";
      }
      VersionTuple RemovedAfterVersion = AA->getObsoleted();
      if (!RemovedAfterVersion.empty()) {
        Result << "<RemovedAfterVersion>"
               << RemovedAfterVersion.getAsString()
               << "</RemovedAfterVersion>";
      }
      StringRef DeprecationSummary = AA->getMessage();
      if (!DeprecationSummary.empty()) {
        Result << "<DeprecationSummary>";
        appendToResultWithXMLEscaping(DeprecationSummary);
        Result << "</DeprecationSummary>";
      }
      if (AA->getUnavailable())
        Result << "<Unavailable/>";
      Result << "</Availability>";
    }
  }

  {
    bool StartTagEmitted = false;
    for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
      const Comment *C = Parts.MiscBlocks[i];
      if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
        continue;
      if (!StartTagEmitted) {
        Result << "<Discussion>";
        StartTagEmitted = true;
      }
      visit(C);
    }
    if (StartTagEmitted)
      Result << "</Discussion>";
  }

  Result << RootEndTag;
}
Пример #7
0
/// \brief Print out the file/line/column information and include trace.
///
/// This method handlen the emission of the diagnostic location information.
/// This includes extracting as much location information as is present for
/// the diagnostic and printing it, as well as any include stack or source
/// ranges necessary.
void TextDiagnostic::emitDiagnosticLoc(SourceLocation Loc, PresumedLoc PLoc,
                                       DiagnosticsEngine::Level Level,
                                       ArrayRef<CharSourceRange> Ranges,
                                       const SourceManager &SM) {
  if (PLoc.isInvalid()) {
    // At least print the file name if available:
    FileID FID = SM.getFileID(Loc);
    if (FID.isValid()) {
      const FileEntry* FE = SM.getFileEntryForID(FID);
      if (FE && FE->isValid()) {
        emitFilename(FE->getName(), SM);
        if (FE->isInPCH())
          OS << " (in PCH)";
        OS << ": ";
      }
    }
    return;
  }
  unsigned LineNo = PLoc.getLine();

  if (!DiagOpts->ShowLocation)
    return;

  if (DiagOpts->ShowColors)
    OS.changeColor(savedColor, true);

  emitFilename(PLoc.getFilename(), SM);
  switch (DiagOpts->getFormat()) {
  case DiagnosticOptions::Clang: OS << ':'  << LineNo; break;
  case DiagnosticOptions::MSVC:  OS << '('  << LineNo; break;
  case DiagnosticOptions::Vi:    OS << " +" << LineNo; break;
  }

  if (DiagOpts->ShowColumn)
    // Compute the column number.
    if (unsigned ColNo = PLoc.getColumn()) {
      if (DiagOpts->getFormat() == DiagnosticOptions::MSVC) {
        OS << ',';
        // Visual Studio 2010 or earlier expects column number to be off by one
        if (LangOpts.MSCompatibilityVersion &&
            !LangOpts.isCompatibleWithMSVC(LangOptions::MSVC2012))
          ColNo--;
      } else
        OS << ':';
      OS << ColNo;
    }
  switch (DiagOpts->getFormat()) {
  case DiagnosticOptions::Clang:
  case DiagnosticOptions::Vi:    OS << ':';    break;
  case DiagnosticOptions::MSVC:
    // MSVC2013 and before print 'file(4) : error'. MSVC2015 gets rid of the
    // space and prints 'file(4): error'.
    OS << ')';
    if (LangOpts.MSCompatibilityVersion &&
        !LangOpts.isCompatibleWithMSVC(LangOptions::MSVC2015))
      OS << ' ';
    OS << ": ";
    break;
  }

  if (DiagOpts->ShowSourceRanges && !Ranges.empty()) {
    FileID CaretFileID =
      SM.getFileID(SM.getExpansionLoc(Loc));
    bool PrintedRange = false;

    for (ArrayRef<CharSourceRange>::const_iterator RI = Ranges.begin(),
         RE = Ranges.end();
         RI != RE; ++RI) {
      // Ignore invalid ranges.
      if (!RI->isValid()) continue;

      SourceLocation B = SM.getExpansionLoc(RI->getBegin());
      SourceLocation E = SM.getExpansionLoc(RI->getEnd());

      // If the End location and the start location are the same and are a
      // macro location, then the range was something that came from a
      // macro expansion or _Pragma.  If this is an object-like macro, the
      // best we can do is to highlight the range.  If this is a
      // function-like macro, we'd also like to highlight the arguments.
      if (B == E && RI->getEnd().isMacroID())
        E = SM.getExpansionRange(RI->getEnd()).second;

      std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(B);
      std::pair<FileID, unsigned> EInfo = SM.getDecomposedLoc(E);

      // If the start or end of the range is in another file, just discard
      // it.
      if (BInfo.first != CaretFileID || EInfo.first != CaretFileID)
        continue;

      // Add in the length of the token, so that we cover multi-char
      // tokens.
      unsigned TokSize = 0;
      if (RI->isTokenRange())
        TokSize = Lexer::MeasureTokenLength(E, SM, LangOpts);

      OS << '{' << SM.getLineNumber(BInfo.first, BInfo.second) << ':'
        << SM.getColumnNumber(BInfo.first, BInfo.second) << '-'
        << SM.getLineNumber(EInfo.first, EInfo.second) << ':'
        << (SM.getColumnNumber(EInfo.first, EInfo.second)+TokSize)
        << '}';
      PrintedRange = true;
    }

    if (PrintedRange)
      OS << ':';
  }
  OS << ' ';
}
Пример #8
0
/// Print out the file/line/column information and include trace.
///
/// This method handlen the emission of the diagnostic location information.
/// This includes extracting as much location information as is present for
/// the diagnostic and printing it, as well as any include stack or source
/// ranges necessary.
void TextDiagnostic::emitDiagnosticLoc(FullSourceLoc Loc, PresumedLoc PLoc,
                                       DiagnosticsEngine::Level Level,
                                       ArrayRef<CharSourceRange> Ranges) {
  if (PLoc.isInvalid()) {
    // At least print the file name if available:
    FileID FID = Loc.getFileID();
    if (FID.isValid()) {
      const FileEntry *FE = Loc.getFileEntry();
      if (FE && FE->isValid()) {
        emitFilename(FE->getName(), Loc.getManager());
        if (FE->isInPCH())
          OS << " (in PCH)";
        OS << ": ";
      }
    }
    return;
  }
  unsigned LineNo = PLoc.getLine();

  if (!DiagOpts->ShowLocation)
    return;

  if (DiagOpts->ShowColors)
    OS.changeColor(savedColor, true);

  emitFilename(PLoc.getFilename(), Loc.getManager());
  switch (DiagOpts->getFormat()) {
  case DiagnosticOptions::Clang: OS << ':'  << LineNo; break;
  case DiagnosticOptions::MSVC:  OS << '('  << LineNo; break;
  case DiagnosticOptions::Vi:    OS << " +" << LineNo; break;
  }

  if (DiagOpts->ShowColumn)
    // Compute the column number.
    if (unsigned ColNo = PLoc.getColumn()) {
      if (DiagOpts->getFormat() == DiagnosticOptions::MSVC) {
        OS << ',';
        // Visual Studio 2010 or earlier expects column number to be off by one
        if (LangOpts.MSCompatibilityVersion &&
            !LangOpts.isCompatibleWithMSVC(LangOptions::MSVC2012))
          ColNo--;
      } else
        OS << ':';
      OS << ColNo;
    }
  switch (DiagOpts->getFormat()) {
  case DiagnosticOptions::Clang:
  case DiagnosticOptions::Vi:    OS << ':';    break;
  case DiagnosticOptions::MSVC:
    // MSVC2013 and before print 'file(4) : error'. MSVC2015 gets rid of the
    // space and prints 'file(4): error'.
    OS << ')';
    if (LangOpts.MSCompatibilityVersion &&
        !LangOpts.isCompatibleWithMSVC(LangOptions::MSVC2015))
      OS << ' ';
    OS << ": ";
    break;
  }

  if (DiagOpts->ShowSourceRanges && !Ranges.empty()) {
    FileID CaretFileID = Loc.getExpansionLoc().getFileID();
    bool PrintedRange = false;

    for (ArrayRef<CharSourceRange>::const_iterator RI = Ranges.begin(),
         RE = Ranges.end();
         RI != RE; ++RI) {
      // Ignore invalid ranges.
      if (!RI->isValid()) continue;

      auto &SM = Loc.getManager();
      SourceLocation B = SM.getExpansionLoc(RI->getBegin());
      CharSourceRange ERange = SM.getExpansionRange(RI->getEnd());
      SourceLocation E = ERange.getEnd();
      bool IsTokenRange = ERange.isTokenRange();

      std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(B);
      std::pair<FileID, unsigned> EInfo = SM.getDecomposedLoc(E);

      // If the start or end of the range is in another file, just discard
      // it.
      if (BInfo.first != CaretFileID || EInfo.first != CaretFileID)
        continue;

      // Add in the length of the token, so that we cover multi-char
      // tokens.
      unsigned TokSize = 0;
      if (IsTokenRange)
        TokSize = Lexer::MeasureTokenLength(E, SM, LangOpts);

      FullSourceLoc BF(B, SM), EF(E, SM);
      OS << '{'
         << BF.getLineNumber() << ':' << BF.getColumnNumber() << '-'
         << EF.getLineNumber() << ':' << (EF.getColumnNumber() + TokSize)
         << '}';
      PrintedRange = true;
    }

    if (PrintedRange)
      OS << ':';
  }
  OS << ' ';
}
Пример #9
0
void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D,
                                 FilesMade *filesMade) {

  // Create the HTML directory if it is missing.
  if (!createdDir) {
    createdDir = true;
    if (std::error_code ec = llvm::sys::fs::create_directories(Directory)) {
      llvm::errs() << "warning: could not create directory '"
                   << Directory << "': " << ec.message() << '\n';

      noDir = true;

      return;
    }
  }

  if (noDir)
    return;

  // First flatten out the entire path to make it easier to use.
  PathPieces path = D.path.flatten(/*ShouldFlattenMacros=*/false);

  // The path as already been prechecked that all parts of the path are
  // from the same file and that it is non-empty.
  const SourceManager &SMgr = path.front()->getLocation().getManager();
  assert(!path.empty());
  FileID FID =
    path.front()->getLocation().asLocation().getExpansionLoc().getFileID();
  assert(FID.isValid());

  // Create a new rewriter to generate HTML.
  Rewriter R(const_cast<SourceManager&>(SMgr), PP.getLangOpts());

  // Get the function/method name
  SmallString<128> declName("unknown");
  int offsetDecl = 0;
  if (const Decl *DeclWithIssue = D.getDeclWithIssue()) {
      if (const NamedDecl *ND = dyn_cast<NamedDecl>(DeclWithIssue)) {
          declName = ND->getDeclName().getAsString();
      }

      if (const Stmt *Body = DeclWithIssue->getBody()) {
          // Retrieve the relative position of the declaration which will be used
          // for the file name
          FullSourceLoc L(
              SMgr.getExpansionLoc(path.back()->getLocation().asLocation()),
              SMgr);
          FullSourceLoc FunL(SMgr.getExpansionLoc(Body->getLocStart()), SMgr);
          offsetDecl = L.getExpansionLineNumber() - FunL.getExpansionLineNumber();
      }
  }

  // Process the path.
  // Maintain the counts of extra note pieces separately.
  unsigned TotalPieces = path.size();
  unsigned TotalNotePieces =
      std::count_if(path.begin(), path.end(),
                    [](const std::shared_ptr<PathDiagnosticPiece> &p) {
                      return isa<PathDiagnosticNotePiece>(*p);
                    });

  unsigned TotalRegularPieces = TotalPieces - TotalNotePieces;
  unsigned NumRegularPieces = TotalRegularPieces;
  unsigned NumNotePieces = TotalNotePieces;

  for (auto I = path.rbegin(), E = path.rend(); I != E; ++I) {
    if (isa<PathDiagnosticNotePiece>(I->get())) {
      // This adds diagnostic bubbles, but not navigation.
      // Navigation through note pieces would be added later,
      // as a separate pass through the piece list.
      HandlePiece(R, FID, **I, NumNotePieces, TotalNotePieces);
      --NumNotePieces;
    } else {
      HandlePiece(R, FID, **I, NumRegularPieces, TotalRegularPieces);
      --NumRegularPieces;
    }
  }

  // Add line numbers, header, footer, etc.

  // unsigned FID = R.getSourceMgr().getMainFileID();
  html::EscapeText(R, FID);
  html::AddLineNumbers(R, FID);

  // If we have a preprocessor, relex the file and syntax highlight.
  // We might not have a preprocessor if we come from a deserialized AST file,
  // for example.

  html::SyntaxHighlight(R, FID, PP);
  html::HighlightMacros(R, FID, PP);

  // Get the full directory name of the analyzed file.

  const FileEntry* Entry = SMgr.getFileEntryForID(FID);

  // This is a cludge; basically we want to append either the full
  // working directory if we have no directory information.  This is
  // a work in progress.

  llvm::SmallString<0> DirName;

  if (llvm::sys::path::is_relative(Entry->getName())) {
    llvm::sys::fs::current_path(DirName);
    DirName += '/';
  }

  int LineNumber = path.back()->getLocation().asLocation().getExpansionLineNumber();
  int ColumnNumber = path.back()->getLocation().asLocation().getExpansionColumnNumber();

  // Add the name of the file as an <h1> tag.
  {
    std::string s;
    llvm::raw_string_ostream os(s);

    os << "<!-- REPORTHEADER -->\n"
       << "<h3>Bug Summary</h3>\n<table class=\"simpletable\">\n"
          "<tr><td class=\"rowname\">File:</td><td>"
       << html::EscapeText(DirName)
       << html::EscapeText(Entry->getName())
       << "</td></tr>\n<tr><td class=\"rowname\">Warning:</td><td>"
          "<a href=\"#EndPath\">line "
       << LineNumber
       << ", column "
       << ColumnNumber
       << "</a><br />"
       << D.getVerboseDescription() << "</td></tr>\n";

    // The navigation across the extra notes pieces.
    unsigned NumExtraPieces = 0;
    for (const auto &Piece : path) {
      if (const auto *P = dyn_cast<PathDiagnosticNotePiece>(Piece.get())) {
        int LineNumber =
            P->getLocation().asLocation().getExpansionLineNumber();
        int ColumnNumber =
            P->getLocation().asLocation().getExpansionColumnNumber();
        os << "<tr><td class=\"rowname\">Note:</td><td>"
           << "<a href=\"#Note" << NumExtraPieces << "\">line "
           << LineNumber << ", column " << ColumnNumber << "</a><br />"
           << P->getString() << "</td></tr>";
        ++NumExtraPieces;
      }
    }

    // Output any other meta data.

    for (PathDiagnostic::meta_iterator I=D.meta_begin(), E=D.meta_end();
         I!=E; ++I) {
      os << "<tr><td></td><td>" << html::EscapeText(*I) << "</td></tr>\n";
    }

    os << "</table>\n<!-- REPORTSUMMARYEXTRA -->\n"
          "<h3>Annotated Source Code</h3>\n";

    R.InsertTextBefore(SMgr.getLocForStartOfFile(FID), os.str());
  }

  // Embed meta-data tags.
  {
    std::string s;
    llvm::raw_string_ostream os(s);

    StringRef BugDesc = D.getVerboseDescription();
    if (!BugDesc.empty())
      os << "\n<!-- BUGDESC " << BugDesc << " -->\n";

    StringRef BugType = D.getBugType();
    if (!BugType.empty())
      os << "\n<!-- BUGTYPE " << BugType << " -->\n";

    PathDiagnosticLocation UPDLoc = D.getUniqueingLoc();
    FullSourceLoc L(SMgr.getExpansionLoc(UPDLoc.isValid()
                                             ? UPDLoc.asLocation()
                                             : D.getLocation().asLocation()),
                    SMgr);
    const Decl *DeclWithIssue = D.getDeclWithIssue();

    StringRef BugCategory = D.getCategory();
    if (!BugCategory.empty())
      os << "\n<!-- BUGCATEGORY " << BugCategory << " -->\n";

    os << "\n<!-- BUGFILE " << DirName << Entry->getName() << " -->\n";

    os << "\n<!-- FILENAME " << llvm::sys::path::filename(Entry->getName()) << " -->\n";

    os  << "\n<!-- FUNCTIONNAME " <<  declName << " -->\n";

    os << "\n<!-- ISSUEHASHCONTENTOFLINEINCONTEXT "
       << GetIssueHash(SMgr, L, D.getCheckName(), D.getBugType(), DeclWithIssue,
                       PP.getLangOpts()) << " -->\n";

    os << "\n<!-- BUGLINE "
       << LineNumber
       << " -->\n";

    os << "\n<!-- BUGCOLUMN "
      << ColumnNumber
      << " -->\n";

    os << "\n<!-- BUGPATHLENGTH " << path.size() << " -->\n";

    // Mark the end of the tags.
    os << "\n<!-- BUGMETAEND -->\n";

    // Insert the text.
    R.InsertTextBefore(SMgr.getLocForStartOfFile(FID), os.str());
  }

  // Add CSS, header, and footer.

  html::AddHeaderFooterInternalBuiltinCSS(R, FID, Entry->getName());

  // Get the rewrite buffer.
  const RewriteBuffer *Buf = R.getRewriteBufferFor(FID);

  if (!Buf) {
    llvm::errs() << "warning: no diagnostics generated for main file.\n";
    return;
  }

  // Create a path for the target HTML file.
  int FD;
  SmallString<128> Model, ResultPath;

  if (!AnalyzerOpts.shouldWriteStableReportFilename()) {
      llvm::sys::path::append(Model, Directory, "report-%%%%%%.html");
      if (std::error_code EC =
          llvm::sys::fs::make_absolute(Model)) {
          llvm::errs() << "warning: could not make '" << Model
                       << "' absolute: " << EC.message() << '\n';
        return;
      }
      if (std::error_code EC =
          llvm::sys::fs::createUniqueFile(Model, FD, ResultPath)) {
          llvm::errs() << "warning: could not create file in '" << Directory
                       << "': " << EC.message() << '\n';
          return;
      }

  } else {
      int i = 1;
      std::error_code EC;
      do {
          // Find a filename which is not already used
          std::stringstream filename;
          Model = "";
          filename << "report-"
                   << llvm::sys::path::filename(Entry->getName()).str()
                   << "-" << declName.c_str()
                   << "-" << offsetDecl
                   << "-" << i << ".html";
          llvm::sys::path::append(Model, Directory,
                                  filename.str());
          EC = llvm::sys::fs::openFileForWrite(Model,
                                               FD,
                                               llvm::sys::fs::F_RW |
                                               llvm::sys::fs::F_Excl);
          if (EC && EC != llvm::errc::file_exists) {
              llvm::errs() << "warning: could not create file '" << Model
                           << "': " << EC.message() << '\n';
              return;
          }
          i++;
      } while (EC);
  }

  llvm::raw_fd_ostream os(FD, true);

  if (filesMade)
    filesMade->addDiagnostic(D, getName(),
                             llvm::sys::path::filename(ResultPath));

  // Emit the HTML to disk.
  for (RewriteBuffer::iterator I = Buf->begin(), E = Buf->end(); I!=E; ++I)
      os << *I;
}