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