annotate clang-tools-extra/clangd/Diagnostics.cpp @ 180:680fa57a2f20

fix compile errors.
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Sat, 30 May 2020 17:44:06 +0900
parents 0572611fdcc8
children 2e18cbf3894f
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
150
anatofuz
parents:
diff changeset
1 //===--- Diagnostics.cpp -----------------------------------------*- C++-*-===//
anatofuz
parents:
diff changeset
2 //
anatofuz
parents:
diff changeset
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
anatofuz
parents:
diff changeset
4 // See https://llvm.org/LICENSE.txt for license information.
anatofuz
parents:
diff changeset
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
anatofuz
parents:
diff changeset
6 //
anatofuz
parents:
diff changeset
7 //===----------------------------------------------------------------------===//
anatofuz
parents:
diff changeset
8
anatofuz
parents:
diff changeset
9 #include "Diagnostics.h"
anatofuz
parents:
diff changeset
10 #include "../clang-tidy/ClangTidyDiagnosticConsumer.h"
anatofuz
parents:
diff changeset
11 #include "Compiler.h"
anatofuz
parents:
diff changeset
12 #include "Protocol.h"
anatofuz
parents:
diff changeset
13 #include "SourceCode.h"
173
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
14 #include "support/Logger.h"
150
anatofuz
parents:
diff changeset
15 #include "clang/Basic/AllDiagnostics.h"
anatofuz
parents:
diff changeset
16 #include "clang/Basic/Diagnostic.h"
anatofuz
parents:
diff changeset
17 #include "clang/Basic/DiagnosticIDs.h"
anatofuz
parents:
diff changeset
18 #include "clang/Basic/FileManager.h"
anatofuz
parents:
diff changeset
19 #include "clang/Basic/SourceLocation.h"
anatofuz
parents:
diff changeset
20 #include "clang/Basic/SourceManager.h"
anatofuz
parents:
diff changeset
21 #include "clang/Lex/Lexer.h"
anatofuz
parents:
diff changeset
22 #include "clang/Lex/Token.h"
anatofuz
parents:
diff changeset
23 #include "llvm/ADT/ArrayRef.h"
anatofuz
parents:
diff changeset
24 #include "llvm/ADT/DenseSet.h"
anatofuz
parents:
diff changeset
25 #include "llvm/ADT/Optional.h"
anatofuz
parents:
diff changeset
26 #include "llvm/ADT/STLExtras.h"
anatofuz
parents:
diff changeset
27 #include "llvm/ADT/SmallString.h"
anatofuz
parents:
diff changeset
28 #include "llvm/ADT/StringRef.h"
anatofuz
parents:
diff changeset
29 #include "llvm/ADT/Twine.h"
anatofuz
parents:
diff changeset
30 #include "llvm/Support/Capacity.h"
anatofuz
parents:
diff changeset
31 #include "llvm/Support/Path.h"
anatofuz
parents:
diff changeset
32 #include "llvm/Support/ScopedPrinter.h"
anatofuz
parents:
diff changeset
33 #include "llvm/Support/Signals.h"
anatofuz
parents:
diff changeset
34 #include "llvm/Support/raw_ostream.h"
anatofuz
parents:
diff changeset
35 #include <algorithm>
anatofuz
parents:
diff changeset
36 #include <cstddef>
anatofuz
parents:
diff changeset
37
anatofuz
parents:
diff changeset
38 namespace clang {
anatofuz
parents:
diff changeset
39 namespace clangd {
anatofuz
parents:
diff changeset
40 namespace {
anatofuz
parents:
diff changeset
41
anatofuz
parents:
diff changeset
42 const char *getDiagnosticCode(unsigned ID) {
anatofuz
parents:
diff changeset
43 switch (ID) {
anatofuz
parents:
diff changeset
44 #define DIAG(ENUM, CLASS, DEFAULT_MAPPING, DESC, GROPU, SFINAE, NOWERROR, \
anatofuz
parents:
diff changeset
45 SHOWINSYSHEADER, CATEGORY) \
anatofuz
parents:
diff changeset
46 case clang::diag::ENUM: \
anatofuz
parents:
diff changeset
47 return #ENUM;
anatofuz
parents:
diff changeset
48 #include "clang/Basic/DiagnosticASTKinds.inc"
anatofuz
parents:
diff changeset
49 #include "clang/Basic/DiagnosticAnalysisKinds.inc"
anatofuz
parents:
diff changeset
50 #include "clang/Basic/DiagnosticCommentKinds.inc"
anatofuz
parents:
diff changeset
51 #include "clang/Basic/DiagnosticCommonKinds.inc"
anatofuz
parents:
diff changeset
52 #include "clang/Basic/DiagnosticDriverKinds.inc"
anatofuz
parents:
diff changeset
53 #include "clang/Basic/DiagnosticFrontendKinds.inc"
anatofuz
parents:
diff changeset
54 #include "clang/Basic/DiagnosticLexKinds.inc"
anatofuz
parents:
diff changeset
55 #include "clang/Basic/DiagnosticParseKinds.inc"
anatofuz
parents:
diff changeset
56 #include "clang/Basic/DiagnosticRefactoringKinds.inc"
anatofuz
parents:
diff changeset
57 #include "clang/Basic/DiagnosticSemaKinds.inc"
anatofuz
parents:
diff changeset
58 #include "clang/Basic/DiagnosticSerializationKinds.inc"
anatofuz
parents:
diff changeset
59 #undef DIAG
anatofuz
parents:
diff changeset
60 default:
anatofuz
parents:
diff changeset
61 return nullptr;
anatofuz
parents:
diff changeset
62 }
anatofuz
parents:
diff changeset
63 }
anatofuz
parents:
diff changeset
64
anatofuz
parents:
diff changeset
65 bool mentionsMainFile(const Diag &D) {
anatofuz
parents:
diff changeset
66 if (D.InsideMainFile)
anatofuz
parents:
diff changeset
67 return true;
anatofuz
parents:
diff changeset
68 // Fixes are always in the main file.
anatofuz
parents:
diff changeset
69 if (!D.Fixes.empty())
anatofuz
parents:
diff changeset
70 return true;
anatofuz
parents:
diff changeset
71 for (auto &N : D.Notes) {
anatofuz
parents:
diff changeset
72 if (N.InsideMainFile)
anatofuz
parents:
diff changeset
73 return true;
anatofuz
parents:
diff changeset
74 }
anatofuz
parents:
diff changeset
75 return false;
anatofuz
parents:
diff changeset
76 }
anatofuz
parents:
diff changeset
77
anatofuz
parents:
diff changeset
78 bool isBlacklisted(const Diag &D) {
anatofuz
parents:
diff changeset
79 // clang will always fail parsing MS ASM, we don't link in desc + asm parser.
anatofuz
parents:
diff changeset
80 if (D.ID == clang::diag::err_msasm_unable_to_create_target ||
anatofuz
parents:
diff changeset
81 D.ID == clang::diag::err_msasm_unsupported_arch)
anatofuz
parents:
diff changeset
82 return true;
anatofuz
parents:
diff changeset
83 return false;
anatofuz
parents:
diff changeset
84 }
anatofuz
parents:
diff changeset
85
anatofuz
parents:
diff changeset
86 // Checks whether a location is within a half-open range.
anatofuz
parents:
diff changeset
87 // Note that clang also uses closed source ranges, which this can't handle!
anatofuz
parents:
diff changeset
88 bool locationInRange(SourceLocation L, CharSourceRange R,
anatofuz
parents:
diff changeset
89 const SourceManager &M) {
anatofuz
parents:
diff changeset
90 assert(R.isCharRange());
anatofuz
parents:
diff changeset
91 if (!R.isValid() || M.getFileID(R.getBegin()) != M.getFileID(R.getEnd()) ||
anatofuz
parents:
diff changeset
92 M.getFileID(R.getBegin()) != M.getFileID(L))
anatofuz
parents:
diff changeset
93 return false;
anatofuz
parents:
diff changeset
94 return L != R.getEnd() && M.isPointWithin(L, R.getBegin(), R.getEnd());
anatofuz
parents:
diff changeset
95 }
anatofuz
parents:
diff changeset
96
anatofuz
parents:
diff changeset
97 // Clang diags have a location (shown as ^) and 0 or more ranges (~~~~).
anatofuz
parents:
diff changeset
98 // LSP needs a single range.
anatofuz
parents:
diff changeset
99 Range diagnosticRange(const clang::Diagnostic &D, const LangOptions &L) {
anatofuz
parents:
diff changeset
100 auto &M = D.getSourceManager();
anatofuz
parents:
diff changeset
101 auto Loc = M.getFileLoc(D.getLocation());
anatofuz
parents:
diff changeset
102 for (const auto &CR : D.getRanges()) {
anatofuz
parents:
diff changeset
103 auto R = Lexer::makeFileCharRange(CR, M, L);
anatofuz
parents:
diff changeset
104 if (locationInRange(Loc, R, M))
anatofuz
parents:
diff changeset
105 return halfOpenToRange(M, R);
anatofuz
parents:
diff changeset
106 }
anatofuz
parents:
diff changeset
107 // The range may be given as a fixit hint instead.
anatofuz
parents:
diff changeset
108 for (const auto &F : D.getFixItHints()) {
anatofuz
parents:
diff changeset
109 auto R = Lexer::makeFileCharRange(F.RemoveRange, M, L);
anatofuz
parents:
diff changeset
110 if (locationInRange(Loc, R, M))
anatofuz
parents:
diff changeset
111 return halfOpenToRange(M, R);
anatofuz
parents:
diff changeset
112 }
anatofuz
parents:
diff changeset
113 // If the token at the location is not a comment, we use the token.
anatofuz
parents:
diff changeset
114 // If we can't get the token at the location, fall back to using the location
anatofuz
parents:
diff changeset
115 auto R = CharSourceRange::getCharRange(Loc);
anatofuz
parents:
diff changeset
116 Token Tok;
anatofuz
parents:
diff changeset
117 if (!Lexer::getRawToken(Loc, Tok, M, L, true) && Tok.isNot(tok::comment)) {
anatofuz
parents:
diff changeset
118 R = CharSourceRange::getTokenRange(Tok.getLocation(), Tok.getEndLoc());
anatofuz
parents:
diff changeset
119 }
anatofuz
parents:
diff changeset
120 return halfOpenToRange(M, R);
anatofuz
parents:
diff changeset
121 }
anatofuz
parents:
diff changeset
122
anatofuz
parents:
diff changeset
123 // Returns whether the \p D is modified.
anatofuz
parents:
diff changeset
124 bool adjustDiagFromHeader(Diag &D, const clang::Diagnostic &Info,
anatofuz
parents:
diff changeset
125 const LangOptions &LangOpts) {
anatofuz
parents:
diff changeset
126 // We only report diagnostics with at least error severity from headers.
173
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
127 // Use default severity to avoid noise with -Werror.
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
128 if (!Info.getDiags()->getDiagnosticIDs()->isDefaultMappingAsError(
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
129 Info.getID()))
150
anatofuz
parents:
diff changeset
130 return false;
anatofuz
parents:
diff changeset
131
anatofuz
parents:
diff changeset
132 const SourceManager &SM = Info.getSourceManager();
anatofuz
parents:
diff changeset
133 const SourceLocation &DiagLoc = SM.getExpansionLoc(Info.getLocation());
anatofuz
parents:
diff changeset
134 SourceLocation IncludeInMainFile;
anatofuz
parents:
diff changeset
135 auto GetIncludeLoc = [&SM](SourceLocation SLoc) {
anatofuz
parents:
diff changeset
136 return SM.getIncludeLoc(SM.getFileID(SLoc));
anatofuz
parents:
diff changeset
137 };
anatofuz
parents:
diff changeset
138 for (auto IncludeLocation = GetIncludeLoc(DiagLoc); IncludeLocation.isValid();
anatofuz
parents:
diff changeset
139 IncludeLocation = GetIncludeLoc(IncludeLocation)) {
anatofuz
parents:
diff changeset
140 if (clangd::isInsideMainFile(IncludeLocation, SM)) {
anatofuz
parents:
diff changeset
141 IncludeInMainFile = IncludeLocation;
anatofuz
parents:
diff changeset
142 break;
anatofuz
parents:
diff changeset
143 }
anatofuz
parents:
diff changeset
144 }
anatofuz
parents:
diff changeset
145 if (IncludeInMainFile.isInvalid())
anatofuz
parents:
diff changeset
146 return false;
anatofuz
parents:
diff changeset
147
anatofuz
parents:
diff changeset
148 // Update diag to point at include inside main file.
anatofuz
parents:
diff changeset
149 D.File = SM.getFileEntryForID(SM.getMainFileID())->getName().str();
anatofuz
parents:
diff changeset
150 D.Range.start = sourceLocToPosition(SM, IncludeInMainFile);
anatofuz
parents:
diff changeset
151 D.Range.end = sourceLocToPosition(
anatofuz
parents:
diff changeset
152 SM, Lexer::getLocForEndOfToken(IncludeInMainFile, 0, SM, LangOpts));
anatofuz
parents:
diff changeset
153 D.InsideMainFile = true;
anatofuz
parents:
diff changeset
154
anatofuz
parents:
diff changeset
155 // Add a note that will point to real diagnostic.
anatofuz
parents:
diff changeset
156 const auto *FE = SM.getFileEntryForID(SM.getFileID(DiagLoc));
anatofuz
parents:
diff changeset
157 D.Notes.emplace_back();
anatofuz
parents:
diff changeset
158 Note &N = D.Notes.back();
anatofuz
parents:
diff changeset
159 N.AbsFile = std::string(FE->tryGetRealPathName());
anatofuz
parents:
diff changeset
160 N.File = std::string(FE->getName());
anatofuz
parents:
diff changeset
161 N.Message = "error occurred here";
anatofuz
parents:
diff changeset
162 N.Range = diagnosticRange(Info, LangOpts);
anatofuz
parents:
diff changeset
163
anatofuz
parents:
diff changeset
164 // Update message to mention original file.
anatofuz
parents:
diff changeset
165 D.Message = llvm::Twine("in included file: ", D.Message).str();
anatofuz
parents:
diff changeset
166 return true;
anatofuz
parents:
diff changeset
167 }
anatofuz
parents:
diff changeset
168
anatofuz
parents:
diff changeset
169 bool isInsideMainFile(const clang::Diagnostic &D) {
anatofuz
parents:
diff changeset
170 if (!D.hasSourceManager())
anatofuz
parents:
diff changeset
171 return false;
anatofuz
parents:
diff changeset
172
anatofuz
parents:
diff changeset
173 return clangd::isInsideMainFile(D.getLocation(), D.getSourceManager());
anatofuz
parents:
diff changeset
174 }
anatofuz
parents:
diff changeset
175
anatofuz
parents:
diff changeset
176 bool isNote(DiagnosticsEngine::Level L) {
anatofuz
parents:
diff changeset
177 return L == DiagnosticsEngine::Note || L == DiagnosticsEngine::Remark;
anatofuz
parents:
diff changeset
178 }
anatofuz
parents:
diff changeset
179
anatofuz
parents:
diff changeset
180 llvm::StringRef diagLeveltoString(DiagnosticsEngine::Level Lvl) {
anatofuz
parents:
diff changeset
181 switch (Lvl) {
anatofuz
parents:
diff changeset
182 case DiagnosticsEngine::Ignored:
anatofuz
parents:
diff changeset
183 return "ignored";
anatofuz
parents:
diff changeset
184 case DiagnosticsEngine::Note:
anatofuz
parents:
diff changeset
185 return "note";
anatofuz
parents:
diff changeset
186 case DiagnosticsEngine::Remark:
anatofuz
parents:
diff changeset
187 return "remark";
anatofuz
parents:
diff changeset
188 case DiagnosticsEngine::Warning:
anatofuz
parents:
diff changeset
189 return "warning";
anatofuz
parents:
diff changeset
190 case DiagnosticsEngine::Error:
anatofuz
parents:
diff changeset
191 return "error";
anatofuz
parents:
diff changeset
192 case DiagnosticsEngine::Fatal:
anatofuz
parents:
diff changeset
193 return "fatal error";
anatofuz
parents:
diff changeset
194 }
anatofuz
parents:
diff changeset
195 llvm_unreachable("unhandled DiagnosticsEngine::Level");
anatofuz
parents:
diff changeset
196 }
anatofuz
parents:
diff changeset
197
anatofuz
parents:
diff changeset
198 /// Prints a single diagnostic in a clang-like manner, the output includes
anatofuz
parents:
diff changeset
199 /// location, severity and error message. An example of the output message is:
anatofuz
parents:
diff changeset
200 ///
anatofuz
parents:
diff changeset
201 /// main.cpp:12:23: error: undeclared identifier
anatofuz
parents:
diff changeset
202 ///
anatofuz
parents:
diff changeset
203 /// For main file we only print the basename and for all other files we print
anatofuz
parents:
diff changeset
204 /// the filename on a separate line to provide a slightly more readable output
anatofuz
parents:
diff changeset
205 /// in the editors:
anatofuz
parents:
diff changeset
206 ///
anatofuz
parents:
diff changeset
207 /// dir1/dir2/dir3/../../dir4/header.h:12:23
anatofuz
parents:
diff changeset
208 /// error: undeclared identifier
anatofuz
parents:
diff changeset
209 void printDiag(llvm::raw_string_ostream &OS, const DiagBase &D) {
anatofuz
parents:
diff changeset
210 if (D.InsideMainFile) {
anatofuz
parents:
diff changeset
211 // Paths to main files are often taken from compile_command.json, where they
anatofuz
parents:
diff changeset
212 // are typically absolute. To reduce noise we print only basename for them,
anatofuz
parents:
diff changeset
213 // it should not be confusing and saves space.
anatofuz
parents:
diff changeset
214 OS << llvm::sys::path::filename(D.File) << ":";
anatofuz
parents:
diff changeset
215 } else {
anatofuz
parents:
diff changeset
216 OS << D.File << ":";
anatofuz
parents:
diff changeset
217 }
anatofuz
parents:
diff changeset
218 // Note +1 to line and character. clangd::Range is zero-based, but when
anatofuz
parents:
diff changeset
219 // printing for users we want one-based indexes.
anatofuz
parents:
diff changeset
220 auto Pos = D.Range.start;
anatofuz
parents:
diff changeset
221 OS << (Pos.line + 1) << ":" << (Pos.character + 1) << ":";
anatofuz
parents:
diff changeset
222 // The non-main-file paths are often too long, putting them on a separate
anatofuz
parents:
diff changeset
223 // line improves readability.
anatofuz
parents:
diff changeset
224 if (D.InsideMainFile)
anatofuz
parents:
diff changeset
225 OS << " ";
anatofuz
parents:
diff changeset
226 else
anatofuz
parents:
diff changeset
227 OS << "\n";
anatofuz
parents:
diff changeset
228 OS << diagLeveltoString(D.Severity) << ": " << D.Message;
anatofuz
parents:
diff changeset
229 }
anatofuz
parents:
diff changeset
230
anatofuz
parents:
diff changeset
231 /// Capitalizes the first word in the diagnostic's message.
anatofuz
parents:
diff changeset
232 std::string capitalize(std::string Message) {
anatofuz
parents:
diff changeset
233 if (!Message.empty())
anatofuz
parents:
diff changeset
234 Message[0] = llvm::toUpper(Message[0]);
anatofuz
parents:
diff changeset
235 return Message;
anatofuz
parents:
diff changeset
236 }
anatofuz
parents:
diff changeset
237
anatofuz
parents:
diff changeset
238 /// Returns a message sent to LSP for the main diagnostic in \p D.
anatofuz
parents:
diff changeset
239 /// This message may include notes, if they're not emitted in some other way.
anatofuz
parents:
diff changeset
240 /// Example output:
anatofuz
parents:
diff changeset
241 ///
anatofuz
parents:
diff changeset
242 /// no matching function for call to 'foo'
anatofuz
parents:
diff changeset
243 ///
anatofuz
parents:
diff changeset
244 /// main.cpp:3:5: note: candidate function not viable: requires 2 arguments
anatofuz
parents:
diff changeset
245 ///
anatofuz
parents:
diff changeset
246 /// dir1/dir2/dir3/../../dir4/header.h:12:23
anatofuz
parents:
diff changeset
247 /// note: candidate function not viable: requires 3 arguments
anatofuz
parents:
diff changeset
248 std::string mainMessage(const Diag &D, const ClangdDiagnosticOptions &Opts) {
anatofuz
parents:
diff changeset
249 std::string Result;
anatofuz
parents:
diff changeset
250 llvm::raw_string_ostream OS(Result);
anatofuz
parents:
diff changeset
251 OS << D.Message;
anatofuz
parents:
diff changeset
252 if (Opts.DisplayFixesCount && !D.Fixes.empty())
anatofuz
parents:
diff changeset
253 OS << " (" << (D.Fixes.size() > 1 ? "fixes" : "fix") << " available)";
anatofuz
parents:
diff changeset
254 // If notes aren't emitted as structured info, add them to the message.
anatofuz
parents:
diff changeset
255 if (!Opts.EmitRelatedLocations)
anatofuz
parents:
diff changeset
256 for (auto &Note : D.Notes) {
anatofuz
parents:
diff changeset
257 OS << "\n\n";
anatofuz
parents:
diff changeset
258 printDiag(OS, Note);
anatofuz
parents:
diff changeset
259 }
anatofuz
parents:
diff changeset
260 OS.flush();
anatofuz
parents:
diff changeset
261 return capitalize(std::move(Result));
anatofuz
parents:
diff changeset
262 }
anatofuz
parents:
diff changeset
263
anatofuz
parents:
diff changeset
264 /// Returns a message sent to LSP for the note of the main diagnostic.
anatofuz
parents:
diff changeset
265 std::string noteMessage(const Diag &Main, const DiagBase &Note,
anatofuz
parents:
diff changeset
266 const ClangdDiagnosticOptions &Opts) {
anatofuz
parents:
diff changeset
267 std::string Result;
anatofuz
parents:
diff changeset
268 llvm::raw_string_ostream OS(Result);
anatofuz
parents:
diff changeset
269 OS << Note.Message;
anatofuz
parents:
diff changeset
270 // If the client doesn't support structured links between the note and the
anatofuz
parents:
diff changeset
271 // original diagnostic, then emit the main diagnostic to give context.
anatofuz
parents:
diff changeset
272 if (!Opts.EmitRelatedLocations) {
anatofuz
parents:
diff changeset
273 OS << "\n\n";
anatofuz
parents:
diff changeset
274 printDiag(OS, Main);
anatofuz
parents:
diff changeset
275 }
anatofuz
parents:
diff changeset
276 OS.flush();
anatofuz
parents:
diff changeset
277 return capitalize(std::move(Result));
anatofuz
parents:
diff changeset
278 }
anatofuz
parents:
diff changeset
279 } // namespace
anatofuz
parents:
diff changeset
280
anatofuz
parents:
diff changeset
281 llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const DiagBase &D) {
anatofuz
parents:
diff changeset
282 OS << "[";
anatofuz
parents:
diff changeset
283 if (!D.InsideMainFile)
anatofuz
parents:
diff changeset
284 OS << D.File << ":";
anatofuz
parents:
diff changeset
285 OS << D.Range.start << "-" << D.Range.end << "] ";
anatofuz
parents:
diff changeset
286
anatofuz
parents:
diff changeset
287 return OS << D.Message;
anatofuz
parents:
diff changeset
288 }
anatofuz
parents:
diff changeset
289
anatofuz
parents:
diff changeset
290 llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Fix &F) {
anatofuz
parents:
diff changeset
291 OS << F.Message << " {";
anatofuz
parents:
diff changeset
292 const char *Sep = "";
anatofuz
parents:
diff changeset
293 for (const auto &Edit : F.Edits) {
anatofuz
parents:
diff changeset
294 OS << Sep << Edit;
anatofuz
parents:
diff changeset
295 Sep = ", ";
anatofuz
parents:
diff changeset
296 }
anatofuz
parents:
diff changeset
297 return OS << "}";
anatofuz
parents:
diff changeset
298 }
anatofuz
parents:
diff changeset
299
anatofuz
parents:
diff changeset
300 llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Diag &D) {
anatofuz
parents:
diff changeset
301 OS << static_cast<const DiagBase &>(D);
anatofuz
parents:
diff changeset
302 if (!D.Notes.empty()) {
anatofuz
parents:
diff changeset
303 OS << ", notes: {";
anatofuz
parents:
diff changeset
304 const char *Sep = "";
anatofuz
parents:
diff changeset
305 for (auto &Note : D.Notes) {
anatofuz
parents:
diff changeset
306 OS << Sep << Note;
anatofuz
parents:
diff changeset
307 Sep = ", ";
anatofuz
parents:
diff changeset
308 }
anatofuz
parents:
diff changeset
309 OS << "}";
anatofuz
parents:
diff changeset
310 }
anatofuz
parents:
diff changeset
311 if (!D.Fixes.empty()) {
anatofuz
parents:
diff changeset
312 OS << ", fixes: {";
anatofuz
parents:
diff changeset
313 const char *Sep = "";
anatofuz
parents:
diff changeset
314 for (auto &Fix : D.Fixes) {
anatofuz
parents:
diff changeset
315 OS << Sep << Fix;
anatofuz
parents:
diff changeset
316 Sep = ", ";
anatofuz
parents:
diff changeset
317 }
anatofuz
parents:
diff changeset
318 }
anatofuz
parents:
diff changeset
319 return OS;
anatofuz
parents:
diff changeset
320 }
anatofuz
parents:
diff changeset
321
anatofuz
parents:
diff changeset
322 CodeAction toCodeAction(const Fix &F, const URIForFile &File) {
anatofuz
parents:
diff changeset
323 CodeAction Action;
anatofuz
parents:
diff changeset
324 Action.title = F.Message;
anatofuz
parents:
diff changeset
325 Action.kind = std::string(CodeAction::QUICKFIX_KIND);
anatofuz
parents:
diff changeset
326 Action.edit.emplace();
anatofuz
parents:
diff changeset
327 Action.edit->changes.emplace();
anatofuz
parents:
diff changeset
328 (*Action.edit->changes)[File.uri()] = {F.Edits.begin(), F.Edits.end()};
anatofuz
parents:
diff changeset
329 return Action;
anatofuz
parents:
diff changeset
330 }
anatofuz
parents:
diff changeset
331
anatofuz
parents:
diff changeset
332 void toLSPDiags(
anatofuz
parents:
diff changeset
333 const Diag &D, const URIForFile &File, const ClangdDiagnosticOptions &Opts,
anatofuz
parents:
diff changeset
334 llvm::function_ref<void(clangd::Diagnostic, llvm::ArrayRef<Fix>)> OutFn) {
anatofuz
parents:
diff changeset
335 clangd::Diagnostic Main;
anatofuz
parents:
diff changeset
336 Main.severity = getSeverity(D.Severity);
anatofuz
parents:
diff changeset
337
anatofuz
parents:
diff changeset
338 // Main diagnostic should always refer to a range inside main file. If a
anatofuz
parents:
diff changeset
339 // diagnostic made it so for, it means either itself or one of its notes is
anatofuz
parents:
diff changeset
340 // inside main file.
anatofuz
parents:
diff changeset
341 if (D.InsideMainFile) {
anatofuz
parents:
diff changeset
342 Main.range = D.Range;
anatofuz
parents:
diff changeset
343 } else {
anatofuz
parents:
diff changeset
344 auto It =
anatofuz
parents:
diff changeset
345 llvm::find_if(D.Notes, [](const Note &N) { return N.InsideMainFile; });
anatofuz
parents:
diff changeset
346 assert(It != D.Notes.end() &&
anatofuz
parents:
diff changeset
347 "neither the main diagnostic nor notes are inside main file");
anatofuz
parents:
diff changeset
348 Main.range = It->Range;
anatofuz
parents:
diff changeset
349 }
anatofuz
parents:
diff changeset
350
anatofuz
parents:
diff changeset
351 Main.code = D.Name;
anatofuz
parents:
diff changeset
352 switch (D.Source) {
anatofuz
parents:
diff changeset
353 case Diag::Clang:
anatofuz
parents:
diff changeset
354 Main.source = "clang";
anatofuz
parents:
diff changeset
355 break;
anatofuz
parents:
diff changeset
356 case Diag::ClangTidy:
anatofuz
parents:
diff changeset
357 Main.source = "clang-tidy";
anatofuz
parents:
diff changeset
358 break;
anatofuz
parents:
diff changeset
359 case Diag::Unknown:
anatofuz
parents:
diff changeset
360 break;
anatofuz
parents:
diff changeset
361 }
anatofuz
parents:
diff changeset
362 if (Opts.EmbedFixesInDiagnostics) {
anatofuz
parents:
diff changeset
363 Main.codeActions.emplace();
anatofuz
parents:
diff changeset
364 for (const auto &Fix : D.Fixes)
anatofuz
parents:
diff changeset
365 Main.codeActions->push_back(toCodeAction(Fix, File));
anatofuz
parents:
diff changeset
366 }
anatofuz
parents:
diff changeset
367 if (Opts.SendDiagnosticCategory && !D.Category.empty())
anatofuz
parents:
diff changeset
368 Main.category = D.Category;
anatofuz
parents:
diff changeset
369
anatofuz
parents:
diff changeset
370 Main.message = mainMessage(D, Opts);
anatofuz
parents:
diff changeset
371 if (Opts.EmitRelatedLocations) {
anatofuz
parents:
diff changeset
372 Main.relatedInformation.emplace();
anatofuz
parents:
diff changeset
373 for (auto &Note : D.Notes) {
anatofuz
parents:
diff changeset
374 if (!Note.AbsFile) {
anatofuz
parents:
diff changeset
375 vlog("Dropping note from unknown file: {0}", Note);
anatofuz
parents:
diff changeset
376 continue;
anatofuz
parents:
diff changeset
377 }
anatofuz
parents:
diff changeset
378 DiagnosticRelatedInformation RelInfo;
anatofuz
parents:
diff changeset
379 RelInfo.location.range = Note.Range;
anatofuz
parents:
diff changeset
380 RelInfo.location.uri =
anatofuz
parents:
diff changeset
381 URIForFile::canonicalize(*Note.AbsFile, File.file());
anatofuz
parents:
diff changeset
382 RelInfo.message = noteMessage(D, Note, Opts);
anatofuz
parents:
diff changeset
383 Main.relatedInformation->push_back(std::move(RelInfo));
anatofuz
parents:
diff changeset
384 }
anatofuz
parents:
diff changeset
385 }
anatofuz
parents:
diff changeset
386 OutFn(std::move(Main), D.Fixes);
anatofuz
parents:
diff changeset
387
anatofuz
parents:
diff changeset
388 // If we didn't emit the notes as relatedLocations, emit separate diagnostics
anatofuz
parents:
diff changeset
389 // so the user can find the locations easily.
anatofuz
parents:
diff changeset
390 if (!Opts.EmitRelatedLocations)
anatofuz
parents:
diff changeset
391 for (auto &Note : D.Notes) {
anatofuz
parents:
diff changeset
392 if (!Note.InsideMainFile)
anatofuz
parents:
diff changeset
393 continue;
anatofuz
parents:
diff changeset
394 clangd::Diagnostic Res;
anatofuz
parents:
diff changeset
395 Res.severity = getSeverity(Note.Severity);
anatofuz
parents:
diff changeset
396 Res.range = Note.Range;
anatofuz
parents:
diff changeset
397 Res.message = noteMessage(D, Note, Opts);
anatofuz
parents:
diff changeset
398 OutFn(std::move(Res), llvm::ArrayRef<Fix>());
anatofuz
parents:
diff changeset
399 }
anatofuz
parents:
diff changeset
400 }
anatofuz
parents:
diff changeset
401
anatofuz
parents:
diff changeset
402 int getSeverity(DiagnosticsEngine::Level L) {
anatofuz
parents:
diff changeset
403 switch (L) {
anatofuz
parents:
diff changeset
404 case DiagnosticsEngine::Remark:
anatofuz
parents:
diff changeset
405 return 4;
anatofuz
parents:
diff changeset
406 case DiagnosticsEngine::Note:
anatofuz
parents:
diff changeset
407 return 3;
anatofuz
parents:
diff changeset
408 case DiagnosticsEngine::Warning:
anatofuz
parents:
diff changeset
409 return 2;
anatofuz
parents:
diff changeset
410 case DiagnosticsEngine::Fatal:
anatofuz
parents:
diff changeset
411 case DiagnosticsEngine::Error:
anatofuz
parents:
diff changeset
412 return 1;
anatofuz
parents:
diff changeset
413 case DiagnosticsEngine::Ignored:
anatofuz
parents:
diff changeset
414 return 0;
anatofuz
parents:
diff changeset
415 }
anatofuz
parents:
diff changeset
416 llvm_unreachable("Unknown diagnostic level!");
anatofuz
parents:
diff changeset
417 }
anatofuz
parents:
diff changeset
418
anatofuz
parents:
diff changeset
419 std::vector<Diag> StoreDiags::take(const clang::tidy::ClangTidyContext *Tidy) {
anatofuz
parents:
diff changeset
420 // Do not forget to emit a pending diagnostic if there is one.
anatofuz
parents:
diff changeset
421 flushLastDiag();
anatofuz
parents:
diff changeset
422
anatofuz
parents:
diff changeset
423 // Fill in name/source now that we have all the context needed to map them.
anatofuz
parents:
diff changeset
424 for (auto &Diag : Output) {
anatofuz
parents:
diff changeset
425 if (const char *ClangDiag = getDiagnosticCode(Diag.ID)) {
anatofuz
parents:
diff changeset
426 // Warnings controlled by -Wfoo are better recognized by that name.
anatofuz
parents:
diff changeset
427 StringRef Warning = DiagnosticIDs::getWarningOptionForDiag(Diag.ID);
anatofuz
parents:
diff changeset
428 if (!Warning.empty()) {
anatofuz
parents:
diff changeset
429 Diag.Name = ("-W" + Warning).str();
anatofuz
parents:
diff changeset
430 } else {
anatofuz
parents:
diff changeset
431 StringRef Name(ClangDiag);
anatofuz
parents:
diff changeset
432 // Almost always an error, with a name like err_enum_class_reference.
anatofuz
parents:
diff changeset
433 // Drop the err_ prefix for brevity.
anatofuz
parents:
diff changeset
434 Name.consume_front("err_");
anatofuz
parents:
diff changeset
435 Diag.Name = std::string(Name);
anatofuz
parents:
diff changeset
436 }
anatofuz
parents:
diff changeset
437 Diag.Source = Diag::Clang;
anatofuz
parents:
diff changeset
438 continue;
anatofuz
parents:
diff changeset
439 }
anatofuz
parents:
diff changeset
440 if (Tidy != nullptr) {
anatofuz
parents:
diff changeset
441 std::string TidyDiag = Tidy->getCheckName(Diag.ID);
anatofuz
parents:
diff changeset
442 if (!TidyDiag.empty()) {
anatofuz
parents:
diff changeset
443 Diag.Name = std::move(TidyDiag);
anatofuz
parents:
diff changeset
444 Diag.Source = Diag::ClangTidy;
anatofuz
parents:
diff changeset
445 // clang-tidy bakes the name into diagnostic messages. Strip it out.
anatofuz
parents:
diff changeset
446 // It would be much nicer to make clang-tidy not do this.
anatofuz
parents:
diff changeset
447 auto CleanMessage = [&](std::string &Msg) {
anatofuz
parents:
diff changeset
448 StringRef Rest(Msg);
anatofuz
parents:
diff changeset
449 if (Rest.consume_back("]") && Rest.consume_back(Diag.Name) &&
anatofuz
parents:
diff changeset
450 Rest.consume_back(" ["))
anatofuz
parents:
diff changeset
451 Msg.resize(Rest.size());
anatofuz
parents:
diff changeset
452 };
anatofuz
parents:
diff changeset
453 CleanMessage(Diag.Message);
anatofuz
parents:
diff changeset
454 for (auto &Note : Diag.Notes)
anatofuz
parents:
diff changeset
455 CleanMessage(Note.Message);
anatofuz
parents:
diff changeset
456 for (auto &Fix : Diag.Fixes)
anatofuz
parents:
diff changeset
457 CleanMessage(Fix.Message);
anatofuz
parents:
diff changeset
458 continue;
anatofuz
parents:
diff changeset
459 }
anatofuz
parents:
diff changeset
460 }
anatofuz
parents:
diff changeset
461 }
anatofuz
parents:
diff changeset
462 // Deduplicate clang-tidy diagnostics -- some clang-tidy checks may emit
anatofuz
parents:
diff changeset
463 // duplicated messages due to various reasons (e.g. the check doesn't handle
anatofuz
parents:
diff changeset
464 // template instantiations well; clang-tidy alias checks).
anatofuz
parents:
diff changeset
465 std::set<std::pair<Range, std::string>> SeenDiags;
anatofuz
parents:
diff changeset
466 llvm::erase_if(Output, [&](const Diag& D) {
anatofuz
parents:
diff changeset
467 return !SeenDiags.emplace(D.Range, D.Message).second;
anatofuz
parents:
diff changeset
468 });
anatofuz
parents:
diff changeset
469 return std::move(Output);
anatofuz
parents:
diff changeset
470 }
anatofuz
parents:
diff changeset
471
anatofuz
parents:
diff changeset
472 void StoreDiags::BeginSourceFile(const LangOptions &Opts,
anatofuz
parents:
diff changeset
473 const Preprocessor *) {
anatofuz
parents:
diff changeset
474 LangOpts = Opts;
anatofuz
parents:
diff changeset
475 }
anatofuz
parents:
diff changeset
476
anatofuz
parents:
diff changeset
477 void StoreDiags::EndSourceFile() {
anatofuz
parents:
diff changeset
478 LangOpts = None;
anatofuz
parents:
diff changeset
479 }
anatofuz
parents:
diff changeset
480
anatofuz
parents:
diff changeset
481 /// Sanitizes a piece for presenting it in a synthesized fix message. Ensures
anatofuz
parents:
diff changeset
482 /// the result is not too large and does not contain newlines.
anatofuz
parents:
diff changeset
483 static void writeCodeToFixMessage(llvm::raw_ostream &OS, llvm::StringRef Code) {
anatofuz
parents:
diff changeset
484 constexpr unsigned MaxLen = 50;
anatofuz
parents:
diff changeset
485
anatofuz
parents:
diff changeset
486 // Only show the first line if there are many.
anatofuz
parents:
diff changeset
487 llvm::StringRef R = Code.split('\n').first;
anatofuz
parents:
diff changeset
488 // Shorten the message if it's too long.
anatofuz
parents:
diff changeset
489 R = R.take_front(MaxLen);
anatofuz
parents:
diff changeset
490
anatofuz
parents:
diff changeset
491 OS << R;
anatofuz
parents:
diff changeset
492 if (R.size() != Code.size())
anatofuz
parents:
diff changeset
493 OS << "…";
anatofuz
parents:
diff changeset
494 }
anatofuz
parents:
diff changeset
495
anatofuz
parents:
diff changeset
496 /// Fills \p D with all information, except the location-related bits.
anatofuz
parents:
diff changeset
497 /// Also note that ID and Name are not part of clangd::DiagBase and should be
anatofuz
parents:
diff changeset
498 /// set elsewhere.
anatofuz
parents:
diff changeset
499 static void fillNonLocationData(DiagnosticsEngine::Level DiagLevel,
anatofuz
parents:
diff changeset
500 const clang::Diagnostic &Info,
anatofuz
parents:
diff changeset
501 clangd::DiagBase &D) {
anatofuz
parents:
diff changeset
502 llvm::SmallString<64> Message;
anatofuz
parents:
diff changeset
503 Info.FormatDiagnostic(Message);
anatofuz
parents:
diff changeset
504
anatofuz
parents:
diff changeset
505 D.Message = std::string(Message.str());
anatofuz
parents:
diff changeset
506 D.Severity = DiagLevel;
anatofuz
parents:
diff changeset
507 D.Category = DiagnosticIDs::getCategoryNameFromID(
anatofuz
parents:
diff changeset
508 DiagnosticIDs::getCategoryNumberForDiag(Info.getID()))
anatofuz
parents:
diff changeset
509 .str();
anatofuz
parents:
diff changeset
510 }
anatofuz
parents:
diff changeset
511
anatofuz
parents:
diff changeset
512 void StoreDiags::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
anatofuz
parents:
diff changeset
513 const clang::Diagnostic &Info) {
anatofuz
parents:
diff changeset
514 DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info);
anatofuz
parents:
diff changeset
515
anatofuz
parents:
diff changeset
516 if (Info.getLocation().isInvalid()) {
anatofuz
parents:
diff changeset
517 // Handle diagnostics coming from command-line arguments. The source manager
anatofuz
parents:
diff changeset
518 // is *not* available at this point, so we cannot use it.
173
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
519 if (!Info.getDiags()->getDiagnosticIDs()->isDefaultMappingAsError(
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
520 Info.getID())) {
150
anatofuz
parents:
diff changeset
521 IgnoreDiagnostics::log(DiagLevel, Info);
anatofuz
parents:
diff changeset
522 return; // non-errors add too much noise, do not show them.
anatofuz
parents:
diff changeset
523 }
anatofuz
parents:
diff changeset
524
anatofuz
parents:
diff changeset
525 flushLastDiag();
anatofuz
parents:
diff changeset
526
anatofuz
parents:
diff changeset
527 LastDiag = Diag();
anatofuz
parents:
diff changeset
528 LastDiag->ID = Info.getID();
anatofuz
parents:
diff changeset
529 fillNonLocationData(DiagLevel, Info, *LastDiag);
anatofuz
parents:
diff changeset
530 LastDiag->InsideMainFile = true;
anatofuz
parents:
diff changeset
531 // Put it at the start of the main file, for a lack of a better place.
anatofuz
parents:
diff changeset
532 LastDiag->Range.start = Position{0, 0};
anatofuz
parents:
diff changeset
533 LastDiag->Range.end = Position{0, 0};
anatofuz
parents:
diff changeset
534 return;
anatofuz
parents:
diff changeset
535 }
anatofuz
parents:
diff changeset
536
anatofuz
parents:
diff changeset
537 if (!LangOpts || !Info.hasSourceManager()) {
anatofuz
parents:
diff changeset
538 IgnoreDiagnostics::log(DiagLevel, Info);
anatofuz
parents:
diff changeset
539 return;
anatofuz
parents:
diff changeset
540 }
anatofuz
parents:
diff changeset
541
anatofuz
parents:
diff changeset
542 bool InsideMainFile = isInsideMainFile(Info);
anatofuz
parents:
diff changeset
543 SourceManager &SM = Info.getSourceManager();
anatofuz
parents:
diff changeset
544
anatofuz
parents:
diff changeset
545 auto FillDiagBase = [&](DiagBase &D) {
anatofuz
parents:
diff changeset
546 fillNonLocationData(DiagLevel, Info, D);
anatofuz
parents:
diff changeset
547
anatofuz
parents:
diff changeset
548 D.InsideMainFile = InsideMainFile;
anatofuz
parents:
diff changeset
549 D.Range = diagnosticRange(Info, *LangOpts);
anatofuz
parents:
diff changeset
550 D.File = std::string(SM.getFilename(Info.getLocation()));
anatofuz
parents:
diff changeset
551 D.AbsFile = getCanonicalPath(
anatofuz
parents:
diff changeset
552 SM.getFileEntryForID(SM.getFileID(Info.getLocation())), SM);
anatofuz
parents:
diff changeset
553 return D;
anatofuz
parents:
diff changeset
554 };
anatofuz
parents:
diff changeset
555
anatofuz
parents:
diff changeset
556 auto AddFix = [&](bool SyntheticMessage) -> bool {
anatofuz
parents:
diff changeset
557 assert(!Info.getFixItHints().empty() &&
anatofuz
parents:
diff changeset
558 "diagnostic does not have attached fix-its");
anatofuz
parents:
diff changeset
559 if (!InsideMainFile)
anatofuz
parents:
diff changeset
560 return false;
anatofuz
parents:
diff changeset
561
173
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
562 // Copy as we may modify the ranges.
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
563 auto FixIts = Info.getFixItHints().vec();
150
anatofuz
parents:
diff changeset
564 llvm::SmallVector<TextEdit, 1> Edits;
173
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
565 for (auto &FixIt : FixIts) {
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
566 // Allow fixits within a single macro-arg expansion to be applied.
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
567 // This can be incorrect if the argument is expanded multiple times in
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
568 // different contexts. Hopefully this is rare!
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
569 if (FixIt.RemoveRange.getBegin().isMacroID() &&
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
570 FixIt.RemoveRange.getEnd().isMacroID() &&
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
571 SM.getFileID(FixIt.RemoveRange.getBegin()) ==
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
572 SM.getFileID(FixIt.RemoveRange.getEnd())) {
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
573 FixIt.RemoveRange = CharSourceRange(
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
574 {SM.getTopMacroCallerLoc(FixIt.RemoveRange.getBegin()),
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
575 SM.getTopMacroCallerLoc(FixIt.RemoveRange.getEnd())},
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
576 FixIt.RemoveRange.isTokenRange());
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
577 }
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
578 // Otherwise, follow clang's behavior: no fixits in macros.
150
anatofuz
parents:
diff changeset
579 if (FixIt.RemoveRange.getBegin().isMacroID() ||
anatofuz
parents:
diff changeset
580 FixIt.RemoveRange.getEnd().isMacroID())
anatofuz
parents:
diff changeset
581 return false;
anatofuz
parents:
diff changeset
582 if (!isInsideMainFile(FixIt.RemoveRange.getBegin(), SM))
anatofuz
parents:
diff changeset
583 return false;
anatofuz
parents:
diff changeset
584 Edits.push_back(toTextEdit(FixIt, SM, *LangOpts));
anatofuz
parents:
diff changeset
585 }
anatofuz
parents:
diff changeset
586
anatofuz
parents:
diff changeset
587 llvm::SmallString<64> Message;
anatofuz
parents:
diff changeset
588 // If requested and possible, create a message like "change 'foo' to 'bar'".
173
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
589 if (SyntheticMessage && FixIts.size() == 1) {
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
590 const auto &FixIt = FixIts.front();
150
anatofuz
parents:
diff changeset
591 bool Invalid = false;
anatofuz
parents:
diff changeset
592 llvm::StringRef Remove =
anatofuz
parents:
diff changeset
593 Lexer::getSourceText(FixIt.RemoveRange, SM, *LangOpts, &Invalid);
anatofuz
parents:
diff changeset
594 llvm::StringRef Insert = FixIt.CodeToInsert;
anatofuz
parents:
diff changeset
595 if (!Invalid) {
anatofuz
parents:
diff changeset
596 llvm::raw_svector_ostream M(Message);
anatofuz
parents:
diff changeset
597 if (!Remove.empty() && !Insert.empty()) {
anatofuz
parents:
diff changeset
598 M << "change '";
anatofuz
parents:
diff changeset
599 writeCodeToFixMessage(M, Remove);
anatofuz
parents:
diff changeset
600 M << "' to '";
anatofuz
parents:
diff changeset
601 writeCodeToFixMessage(M, Insert);
anatofuz
parents:
diff changeset
602 M << "'";
anatofuz
parents:
diff changeset
603 } else if (!Remove.empty()) {
anatofuz
parents:
diff changeset
604 M << "remove '";
anatofuz
parents:
diff changeset
605 writeCodeToFixMessage(M, Remove);
anatofuz
parents:
diff changeset
606 M << "'";
anatofuz
parents:
diff changeset
607 } else if (!Insert.empty()) {
anatofuz
parents:
diff changeset
608 M << "insert '";
anatofuz
parents:
diff changeset
609 writeCodeToFixMessage(M, Insert);
anatofuz
parents:
diff changeset
610 M << "'";
anatofuz
parents:
diff changeset
611 }
anatofuz
parents:
diff changeset
612 // Don't allow source code to inject newlines into diagnostics.
anatofuz
parents:
diff changeset
613 std::replace(Message.begin(), Message.end(), '\n', ' ');
anatofuz
parents:
diff changeset
614 }
anatofuz
parents:
diff changeset
615 }
173
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
616 if (Message.empty()) // either !SyntheticMessage, or we failed to make one.
150
anatofuz
parents:
diff changeset
617 Info.FormatDiagnostic(Message);
anatofuz
parents:
diff changeset
618 LastDiag->Fixes.push_back(
anatofuz
parents:
diff changeset
619 Fix{std::string(Message.str()), std::move(Edits)});
anatofuz
parents:
diff changeset
620 return true;
anatofuz
parents:
diff changeset
621 };
anatofuz
parents:
diff changeset
622
anatofuz
parents:
diff changeset
623 if (!isNote(DiagLevel)) {
anatofuz
parents:
diff changeset
624 // Handle the new main diagnostic.
anatofuz
parents:
diff changeset
625 flushLastDiag();
anatofuz
parents:
diff changeset
626
anatofuz
parents:
diff changeset
627 if (Adjuster) {
anatofuz
parents:
diff changeset
628 DiagLevel = Adjuster(DiagLevel, Info);
anatofuz
parents:
diff changeset
629 if (DiagLevel == DiagnosticsEngine::Ignored) {
anatofuz
parents:
diff changeset
630 LastPrimaryDiagnosticWasSuppressed = true;
anatofuz
parents:
diff changeset
631 return;
anatofuz
parents:
diff changeset
632 }
anatofuz
parents:
diff changeset
633 }
anatofuz
parents:
diff changeset
634 LastPrimaryDiagnosticWasSuppressed = false;
anatofuz
parents:
diff changeset
635
anatofuz
parents:
diff changeset
636 LastDiag = Diag();
anatofuz
parents:
diff changeset
637 LastDiag->ID = Info.getID();
anatofuz
parents:
diff changeset
638 FillDiagBase(*LastDiag);
anatofuz
parents:
diff changeset
639 if (!InsideMainFile)
anatofuz
parents:
diff changeset
640 LastDiagWasAdjusted = adjustDiagFromHeader(*LastDiag, Info, *LangOpts);
anatofuz
parents:
diff changeset
641
anatofuz
parents:
diff changeset
642 if (!Info.getFixItHints().empty())
anatofuz
parents:
diff changeset
643 AddFix(true /* try to invent a message instead of repeating the diag */);
anatofuz
parents:
diff changeset
644 if (Fixer) {
anatofuz
parents:
diff changeset
645 auto ExtraFixes = Fixer(DiagLevel, Info);
anatofuz
parents:
diff changeset
646 LastDiag->Fixes.insert(LastDiag->Fixes.end(), ExtraFixes.begin(),
anatofuz
parents:
diff changeset
647 ExtraFixes.end());
anatofuz
parents:
diff changeset
648 }
anatofuz
parents:
diff changeset
649 } else {
anatofuz
parents:
diff changeset
650 // Handle a note to an existing diagnostic.
anatofuz
parents:
diff changeset
651
anatofuz
parents:
diff changeset
652 // If a diagnostic was suppressed due to the suppression filter,
anatofuz
parents:
diff changeset
653 // also suppress notes associated with it.
anatofuz
parents:
diff changeset
654 if (LastPrimaryDiagnosticWasSuppressed) {
anatofuz
parents:
diff changeset
655 return;
anatofuz
parents:
diff changeset
656 }
anatofuz
parents:
diff changeset
657
anatofuz
parents:
diff changeset
658 if (!LastDiag) {
anatofuz
parents:
diff changeset
659 assert(false && "Adding a note without main diagnostic");
anatofuz
parents:
diff changeset
660 IgnoreDiagnostics::log(DiagLevel, Info);
anatofuz
parents:
diff changeset
661 return;
anatofuz
parents:
diff changeset
662 }
anatofuz
parents:
diff changeset
663
anatofuz
parents:
diff changeset
664 if (!Info.getFixItHints().empty()) {
anatofuz
parents:
diff changeset
665 // A clang note with fix-it is not a separate diagnostic in clangd. We
anatofuz
parents:
diff changeset
666 // attach it as a Fix to the main diagnostic instead.
anatofuz
parents:
diff changeset
667 if (!AddFix(false /* use the note as the message */))
anatofuz
parents:
diff changeset
668 IgnoreDiagnostics::log(DiagLevel, Info);
anatofuz
parents:
diff changeset
669 } else {
anatofuz
parents:
diff changeset
670 // A clang note without fix-its corresponds to clangd::Note.
anatofuz
parents:
diff changeset
671 Note N;
anatofuz
parents:
diff changeset
672 FillDiagBase(N);
anatofuz
parents:
diff changeset
673
anatofuz
parents:
diff changeset
674 LastDiag->Notes.push_back(std::move(N));
anatofuz
parents:
diff changeset
675 }
anatofuz
parents:
diff changeset
676 }
anatofuz
parents:
diff changeset
677 }
anatofuz
parents:
diff changeset
678
anatofuz
parents:
diff changeset
679 void StoreDiags::flushLastDiag() {
anatofuz
parents:
diff changeset
680 if (!LastDiag)
anatofuz
parents:
diff changeset
681 return;
anatofuz
parents:
diff changeset
682 if (!isBlacklisted(*LastDiag) && mentionsMainFile(*LastDiag) &&
anatofuz
parents:
diff changeset
683 (!LastDiagWasAdjusted ||
anatofuz
parents:
diff changeset
684 // Only report the first diagnostic coming from each particular header.
anatofuz
parents:
diff changeset
685 IncludeLinesWithErrors.insert(LastDiag->Range.start.line).second)) {
anatofuz
parents:
diff changeset
686 Output.push_back(std::move(*LastDiag));
anatofuz
parents:
diff changeset
687 } else {
anatofuz
parents:
diff changeset
688 vlog("Dropped diagnostic: {0}: {1}", LastDiag->File, LastDiag->Message);
anatofuz
parents:
diff changeset
689 }
anatofuz
parents:
diff changeset
690 LastDiag.reset();
anatofuz
parents:
diff changeset
691 LastDiagWasAdjusted = false;
anatofuz
parents:
diff changeset
692 }
anatofuz
parents:
diff changeset
693
anatofuz
parents:
diff changeset
694 } // namespace clangd
anatofuz
parents:
diff changeset
695 } // namespace clang