diff options
Diffstat (limited to 'contrib/llvm-project/clang/lib/Frontend/SARIFDiagnostic.cpp')
| -rw-r--r-- | contrib/llvm-project/clang/lib/Frontend/SARIFDiagnostic.cpp | 225 | 
1 files changed, 225 insertions, 0 deletions
diff --git a/contrib/llvm-project/clang/lib/Frontend/SARIFDiagnostic.cpp b/contrib/llvm-project/clang/lib/Frontend/SARIFDiagnostic.cpp new file mode 100644 index 000000000000..416e9132afaf --- /dev/null +++ b/contrib/llvm-project/clang/lib/Frontend/SARIFDiagnostic.cpp @@ -0,0 +1,225 @@ +//===--------- SARIFDiagnostic.cpp - SARIF Diagnostic Formatting ----------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/SARIFDiagnostic.h" +#include "clang/Basic/CharInfo.h" +#include "clang/Basic/DiagnosticOptions.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/Sarif.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Lex/Lexer.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/ConvertUTF.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/Locale.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <string> + +namespace clang { + +SARIFDiagnostic::SARIFDiagnostic(raw_ostream &OS, const LangOptions &LangOpts, +                                 DiagnosticOptions *DiagOpts, +                                 SarifDocumentWriter *Writer) +    : DiagnosticRenderer(LangOpts, DiagOpts), Writer(Writer) {} + +// FIXME(llvm-project/issues/57323): Refactor Diagnostic classes. +void SARIFDiagnostic::emitDiagnosticMessage( +    FullSourceLoc Loc, PresumedLoc PLoc, DiagnosticsEngine::Level Level, +    StringRef Message, ArrayRef<clang::CharSourceRange> Ranges, +    DiagOrStoredDiag D) { + +  const auto *Diag = D.dyn_cast<const Diagnostic *>(); + +  if (!Diag) +    return; + +  SarifRule Rule = SarifRule::create().setRuleId(std::to_string(Diag->getID())); + +  Rule = addDiagnosticLevelToRule(Rule, Level); + +  unsigned RuleIdx = Writer->createRule(Rule); + +  SarifResult Result = +      SarifResult::create(RuleIdx).setDiagnosticMessage(Message); + +  if (Loc.isValid()) +    Result = addLocationToResult(Result, Loc, PLoc, Ranges, *Diag); + +  Writer->appendResult(Result); +} + +SarifResult SARIFDiagnostic::addLocationToResult( +    SarifResult Result, FullSourceLoc Loc, PresumedLoc PLoc, +    ArrayRef<CharSourceRange> Ranges, const Diagnostic &Diag) { +  SmallVector<CharSourceRange> Locations = {}; + +  if (PLoc.isInvalid()) { +    // At least add the file name if available: +    FileID FID = Loc.getFileID(); +    if (FID.isValid()) { +      if (const FileEntry *FE = Loc.getFileEntry()) { +        emitFilename(FE->getName(), Loc.getManager()); +        // FIXME(llvm-project/issues/57366): File-only locations +      } +    } +    return Result; +  } + +  FileID CaretFileID = Loc.getExpansionLoc().getFileID(); + +  for (const CharSourceRange Range : Ranges) { +    // Ignore invalid ranges. +    if (Range.isInvalid()) +      continue; + +    auto &SM = Loc.getManager(); +    SourceLocation B = SM.getExpansionLoc(Range.getBegin()); +    CharSourceRange ERange = SM.getExpansionRange(Range.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); +    SourceLocation BeginLoc = SM.translateLineCol( +        BF.getFileID(), BF.getLineNumber(), BF.getColumnNumber()); +    SourceLocation EndLoc = SM.translateLineCol( +        EF.getFileID(), EF.getLineNumber(), EF.getColumnNumber() + TokSize); + +    Locations.push_back( +        CharSourceRange{SourceRange{BeginLoc, EndLoc}, /* ITR = */ false}); +    // FIXME: Additional ranges should use presumed location in both +    // Text and SARIF diagnostics. +  } + +  auto &SM = Loc.getManager(); +  auto FID = PLoc.getFileID(); +  // Visual Studio 2010 or earlier expects column number to be off by one. +  unsigned int ColNo = (LangOpts.MSCompatibilityVersion && +                        !LangOpts.isCompatibleWithMSVC(LangOptions::MSVC2012)) +                           ? PLoc.getColumn() - 1 +                           : PLoc.getColumn(); +  SourceLocation DiagLoc = SM.translateLineCol(FID, PLoc.getLine(), ColNo); + +  // FIXME(llvm-project/issues/57366): Properly process #line directives. +  Locations.push_back( +      CharSourceRange{SourceRange{DiagLoc, DiagLoc}, /* ITR = */ false}); + +  return Result.setLocations(Locations); +} + +SarifRule +SARIFDiagnostic::addDiagnosticLevelToRule(SarifRule Rule, +                                          DiagnosticsEngine::Level Level) { +  auto Config = SarifReportingConfiguration::create(); + +  switch (Level) { +  case DiagnosticsEngine::Note: +    Config = Config.setLevel(SarifResultLevel::Note); +    break; +  case DiagnosticsEngine::Remark: +    Config = Config.setLevel(SarifResultLevel::None); +    break; +  case DiagnosticsEngine::Warning: +    Config = Config.setLevel(SarifResultLevel::Warning); +    break; +  case DiagnosticsEngine::Error: +    Config = Config.setLevel(SarifResultLevel::Error).setRank(50); +    break; +  case DiagnosticsEngine::Fatal: +    Config = Config.setLevel(SarifResultLevel::Error).setRank(100); +    break; +  case DiagnosticsEngine::Ignored: +    assert(false && "Invalid diagnostic type"); +  } + +  return Rule.setDefaultConfiguration(Config); +} + +llvm::StringRef SARIFDiagnostic::emitFilename(StringRef Filename, +                                              const SourceManager &SM) { +  if (DiagOpts->AbsolutePath) { +    llvm::ErrorOr<const FileEntry *> File = +        SM.getFileManager().getFile(Filename); +    if (File) { +      // We want to print a simplified absolute path, i. e. without "dots". +      // +      // The hardest part here are the paths like "<part1>/<link>/../<part2>". +      // On Unix-like systems, we cannot just collapse "<link>/..", because +      // paths are resolved sequentially, and, thereby, the path +      // "<part1>/<part2>" may point to a different location. That is why +      // we use FileManager::getCanonicalName(), which expands all indirections +      // with llvm::sys::fs::real_path() and caches the result. +      // +      // On the other hand, it would be better to preserve as much of the +      // original path as possible, because that helps a user to recognize it. +      // real_path() expands all links, which is sometimes too much. Luckily, +      // on Windows we can just use llvm::sys::path::remove_dots(), because, +      // on that system, both aforementioned paths point to the same place. +#ifdef _WIN32 +      SmallString<256> TmpFilename = (*File)->getName(); +      llvm::sys::fs::make_absolute(TmpFilename); +      llvm::sys::path::native(TmpFilename); +      llvm::sys::path::remove_dots(TmpFilename, /* remove_dot_dot */ true); +      Filename = StringRef(TmpFilename.data(), TmpFilename.size()); +#else +      Filename = SM.getFileManager().getCanonicalName(*File); +#endif +    } +  } + +  return Filename; +} + +/// 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 SARIFDiagnostic::emitDiagnosticLoc(FullSourceLoc Loc, PresumedLoc PLoc, +                                        DiagnosticsEngine::Level Level, +                                        ArrayRef<CharSourceRange> Ranges) { +  assert(false && "Not implemented in SARIF mode"); +} + +void SARIFDiagnostic::emitIncludeLocation(FullSourceLoc Loc, PresumedLoc PLoc) { +  assert(false && "Not implemented in SARIF mode"); +} + +void SARIFDiagnostic::emitImportLocation(FullSourceLoc Loc, PresumedLoc PLoc, +                                         StringRef ModuleName) { +  assert(false && "Not implemented in SARIF mode"); +} + +void SARIFDiagnostic::emitBuildingModuleLocation(FullSourceLoc Loc, +                                                 PresumedLoc PLoc, +                                                 StringRef ModuleName) { +  assert(false && "Not implemented in SARIF mode"); +} +} // namespace clang  | 
