SARIFDiagnostic.cpp 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. //===--------- SARIFDiagnostic.cpp - SARIF Diagnostic Formatting ----------===//
  2. //
  3. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4. // See https://llvm.org/LICENSE.txt for license information.
  5. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8. #include "clang/Frontend/SARIFDiagnostic.h"
  9. #include "clang/Basic/CharInfo.h"
  10. #include "clang/Basic/DiagnosticOptions.h"
  11. #include "clang/Basic/FileManager.h"
  12. #include "clang/Basic/Sarif.h"
  13. #include "clang/Basic/SourceLocation.h"
  14. #include "clang/Basic/SourceManager.h"
  15. #include "clang/Lex/Lexer.h"
  16. #include "llvm/ADT/ArrayRef.h"
  17. #include "llvm/ADT/SmallString.h"
  18. #include "llvm/ADT/StringExtras.h"
  19. #include "llvm/ADT/StringRef.h"
  20. #include "llvm/Support/Casting.h"
  21. #include "llvm/Support/ConvertUTF.h"
  22. #include "llvm/Support/ErrorHandling.h"
  23. #include "llvm/Support/ErrorOr.h"
  24. #include "llvm/Support/Locale.h"
  25. #include "llvm/Support/Path.h"
  26. #include "llvm/Support/raw_ostream.h"
  27. #include <algorithm>
  28. #include <string>
  29. namespace clang {
  30. SARIFDiagnostic::SARIFDiagnostic(raw_ostream &OS, const LangOptions &LangOpts,
  31. DiagnosticOptions *DiagOpts,
  32. SarifDocumentWriter *Writer)
  33. : DiagnosticRenderer(LangOpts, DiagOpts), Writer(Writer) {}
  34. // FIXME(llvm-project/issues/57323): Refactor Diagnostic classes.
  35. void SARIFDiagnostic::emitDiagnosticMessage(
  36. FullSourceLoc Loc, PresumedLoc PLoc, DiagnosticsEngine::Level Level,
  37. StringRef Message, ArrayRef<clang::CharSourceRange> Ranges,
  38. DiagOrStoredDiag D) {
  39. const auto *Diag = D.dyn_cast<const Diagnostic *>();
  40. if (!Diag)
  41. return;
  42. SarifRule Rule = SarifRule::create().setRuleId(std::to_string(Diag->getID()));
  43. Rule = addDiagnosticLevelToRule(Rule, Level);
  44. unsigned RuleIdx = Writer->createRule(Rule);
  45. SarifResult Result =
  46. SarifResult::create(RuleIdx).setDiagnosticMessage(Message);
  47. if (Loc.isValid())
  48. Result = addLocationToResult(Result, Loc, PLoc, Ranges, *Diag);
  49. Writer->appendResult(Result);
  50. }
  51. SarifResult SARIFDiagnostic::addLocationToResult(
  52. SarifResult Result, FullSourceLoc Loc, PresumedLoc PLoc,
  53. ArrayRef<CharSourceRange> Ranges, const Diagnostic &Diag) {
  54. SmallVector<CharSourceRange> Locations = {};
  55. if (PLoc.isInvalid()) {
  56. // At least add the file name if available:
  57. FileID FID = Loc.getFileID();
  58. if (FID.isValid()) {
  59. if (const FileEntry *FE = Loc.getFileEntry()) {
  60. emitFilename(FE->getName(), Loc.getManager());
  61. // FIXME(llvm-project/issues/57366): File-only locations
  62. }
  63. }
  64. return Result;
  65. }
  66. FileID CaretFileID = Loc.getExpansionLoc().getFileID();
  67. for (const CharSourceRange Range : Ranges) {
  68. // Ignore invalid ranges.
  69. if (Range.isInvalid())
  70. continue;
  71. auto &SM = Loc.getManager();
  72. SourceLocation B = SM.getExpansionLoc(Range.getBegin());
  73. CharSourceRange ERange = SM.getExpansionRange(Range.getEnd());
  74. SourceLocation E = ERange.getEnd();
  75. bool IsTokenRange = ERange.isTokenRange();
  76. std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(B);
  77. std::pair<FileID, unsigned> EInfo = SM.getDecomposedLoc(E);
  78. // If the start or end of the range is in another file, just discard
  79. // it.
  80. if (BInfo.first != CaretFileID || EInfo.first != CaretFileID)
  81. continue;
  82. // Add in the length of the token, so that we cover multi-char
  83. // tokens.
  84. unsigned TokSize = 0;
  85. if (IsTokenRange)
  86. TokSize = Lexer::MeasureTokenLength(E, SM, LangOpts);
  87. FullSourceLoc BF(B, SM), EF(E, SM);
  88. SourceLocation BeginLoc = SM.translateLineCol(
  89. BF.getFileID(), BF.getLineNumber(), BF.getColumnNumber());
  90. SourceLocation EndLoc = SM.translateLineCol(
  91. EF.getFileID(), EF.getLineNumber(), EF.getColumnNumber() + TokSize);
  92. Locations.push_back(
  93. CharSourceRange{SourceRange{BeginLoc, EndLoc}, /* ITR = */ false});
  94. // FIXME: Additional ranges should use presumed location in both
  95. // Text and SARIF diagnostics.
  96. }
  97. auto &SM = Loc.getManager();
  98. auto FID = PLoc.getFileID();
  99. // Visual Studio 2010 or earlier expects column number to be off by one.
  100. unsigned int ColNo = (LangOpts.MSCompatibilityVersion &&
  101. !LangOpts.isCompatibleWithMSVC(LangOptions::MSVC2012))
  102. ? PLoc.getColumn() - 1
  103. : PLoc.getColumn();
  104. SourceLocation DiagLoc = SM.translateLineCol(FID, PLoc.getLine(), ColNo);
  105. // FIXME(llvm-project/issues/57366): Properly process #line directives.
  106. Locations.push_back(
  107. CharSourceRange{SourceRange{DiagLoc, DiagLoc}, /* ITR = */ false});
  108. return Result.setLocations(Locations);
  109. }
  110. SarifRule
  111. SARIFDiagnostic::addDiagnosticLevelToRule(SarifRule Rule,
  112. DiagnosticsEngine::Level Level) {
  113. auto Config = SarifReportingConfiguration::create();
  114. switch (Level) {
  115. case DiagnosticsEngine::Note:
  116. Config = Config.setLevel(SarifResultLevel::Note);
  117. break;
  118. case DiagnosticsEngine::Remark:
  119. Config = Config.setLevel(SarifResultLevel::None);
  120. break;
  121. case DiagnosticsEngine::Warning:
  122. Config = Config.setLevel(SarifResultLevel::Warning);
  123. break;
  124. case DiagnosticsEngine::Error:
  125. Config = Config.setLevel(SarifResultLevel::Error).setRank(50);
  126. break;
  127. case DiagnosticsEngine::Fatal:
  128. Config = Config.setLevel(SarifResultLevel::Error).setRank(100);
  129. break;
  130. case DiagnosticsEngine::Ignored:
  131. assert(false && "Invalid diagnostic type");
  132. }
  133. return Rule.setDefaultConfiguration(Config);
  134. }
  135. llvm::StringRef SARIFDiagnostic::emitFilename(StringRef Filename,
  136. const SourceManager &SM) {
  137. if (DiagOpts->AbsolutePath) {
  138. llvm::ErrorOr<const FileEntry *> File =
  139. SM.getFileManager().getFile(Filename);
  140. if (File) {
  141. // We want to print a simplified absolute path, i. e. without "dots".
  142. //
  143. // The hardest part here are the paths like "<part1>/<link>/../<part2>".
  144. // On Unix-like systems, we cannot just collapse "<link>/..", because
  145. // paths are resolved sequentially, and, thereby, the path
  146. // "<part1>/<part2>" may point to a different location. That is why
  147. // we use FileManager::getCanonicalName(), which expands all indirections
  148. // with llvm::sys::fs::real_path() and caches the result.
  149. //
  150. // On the other hand, it would be better to preserve as much of the
  151. // original path as possible, because that helps a user to recognize it.
  152. // real_path() expands all links, which is sometimes too much. Luckily,
  153. // on Windows we can just use llvm::sys::path::remove_dots(), because,
  154. // on that system, both aforementioned paths point to the same place.
  155. #ifdef _WIN32
  156. SmallString<256> TmpFilename = (*File)->getName();
  157. llvm::sys::fs::make_absolute(TmpFilename);
  158. llvm::sys::path::native(TmpFilename);
  159. llvm::sys::path::remove_dots(TmpFilename, /* remove_dot_dot */ true);
  160. Filename = StringRef(TmpFilename.data(), TmpFilename.size());
  161. #else
  162. Filename = SM.getFileManager().getCanonicalName(*File);
  163. #endif
  164. }
  165. }
  166. return Filename;
  167. }
  168. /// Print out the file/line/column information and include trace.
  169. ///
  170. /// This method handlen the emission of the diagnostic location information.
  171. /// This includes extracting as much location information as is present for
  172. /// the diagnostic and printing it, as well as any include stack or source
  173. /// ranges necessary.
  174. void SARIFDiagnostic::emitDiagnosticLoc(FullSourceLoc Loc, PresumedLoc PLoc,
  175. DiagnosticsEngine::Level Level,
  176. ArrayRef<CharSourceRange> Ranges) {
  177. assert(false && "Not implemented in SARIF mode");
  178. }
  179. void SARIFDiagnostic::emitIncludeLocation(FullSourceLoc Loc, PresumedLoc PLoc) {
  180. assert(false && "Not implemented in SARIF mode");
  181. }
  182. void SARIFDiagnostic::emitImportLocation(FullSourceLoc Loc, PresumedLoc PLoc,
  183. StringRef ModuleName) {
  184. assert(false && "Not implemented in SARIF mode");
  185. }
  186. void SARIFDiagnostic::emitBuildingModuleLocation(FullSourceLoc Loc,
  187. PresumedLoc PLoc,
  188. StringRef ModuleName) {
  189. assert(false && "Not implemented in SARIF mode");
  190. }
  191. } // namespace clang