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