14 #include "clang/Basic/SourceManager.h" 15 #include "clang/Lex/Lexer.h" 16 #include "llvm/Support/Capacity.h" 17 #include "llvm/Support/Path.h" 25 bool mentionsMainFile(
const Diag &
D) {
31 for (
auto &N : D.Notes) {
40 bool locationInRange(SourceLocation L, CharSourceRange R,
41 const SourceManager &M) {
42 assert(R.isCharRange());
43 if (!R.isValid() || M.getFileID(R.getBegin()) != M.getFileID(R.getEnd()) ||
44 M.getFileID(R.getBegin()) != M.getFileID(L))
46 return L != R.getEnd() && M.isPointWithin(L, R.getBegin(), R.getEnd());
51 Range diagnosticRange(
const clang::Diagnostic &D,
const LangOptions &L) {
52 auto &M = D.getSourceManager();
53 auto Loc = M.getFileLoc(D.getLocation());
54 for (
const auto &CR : D.getRanges()) {
55 auto R = Lexer::makeFileCharRange(CR, M, L);
56 if (locationInRange(
Loc, R, M))
59 llvm::Optional<Range> FallbackRange;
61 for (
const auto &F : D.getFixItHints()) {
62 auto R = Lexer::makeFileCharRange(F.RemoveRange, M, L);
63 if (locationInRange(
Loc, R, M))
68 if (R.getBegin() == R.getEnd() &&
Loc == R.getBegin())
72 return *FallbackRange;
74 auto R = Lexer::makeFileCharRange(CharSourceRange::getTokenRange(
Loc), M, L);
76 R = CharSourceRange::getCharRange(
Loc);
80 bool isInsideMainFile(
const SourceLocation
Loc,
const SourceManager &M) {
81 return Loc.isValid() && M.isWrittenInMainFile(M.getFileLoc(Loc));
84 bool isInsideMainFile(
const clang::Diagnostic &D) {
85 if (!D.hasSourceManager())
88 return isInsideMainFile(D.getLocation(), D.getSourceManager());
91 bool isNote(DiagnosticsEngine::Level L) {
92 return L == DiagnosticsEngine::Note || L == DiagnosticsEngine::Remark;
95 llvm::StringRef diagLeveltoString(DiagnosticsEngine::Level Lvl) {
97 case DiagnosticsEngine::Ignored:
99 case DiagnosticsEngine::Note:
101 case DiagnosticsEngine::Remark:
103 case DiagnosticsEngine::Warning:
105 case DiagnosticsEngine::Error:
107 case DiagnosticsEngine::Fatal:
108 return "fatal error";
110 llvm_unreachable(
"unhandled DiagnosticsEngine::Level");
124 void printDiag(llvm::raw_string_ostream &OS,
const DiagBase &D) {
125 if (D.InsideMainFile) {
129 OS << llvm::sys::path::filename(D.File) <<
":";
135 auto Pos = D.Range.start;
139 if (D.InsideMainFile)
143 OS << diagLeveltoString(D.Severity) <<
": " << D.Message;
147 std::string capitalize(std::string
Message) {
148 if (!Message.empty())
149 Message[0] = llvm::toUpper(Message[0]);
165 std::string mainMessage(
const Diag &D) {
167 llvm::raw_string_ostream OS(Result);
169 for (
auto &Note : D.Notes) {
174 return capitalize(std::move(Result));
180 std::string noteMessage(
const Diag &Main,
const DiagBase &Note) {
182 llvm::raw_string_ostream OS(Result);
187 return capitalize(std::move(Result));
202 const char *Sep =
"";
203 for (
const auto &Edit : F.
Edits) {
211 OS << static_cast<const DiagBase &>(
D);
212 if (!D.
Notes.empty()) {
214 const char *Sep =
"";
215 for (
auto &Note : D.
Notes) {
221 if (!D.
Fixes.empty()) {
223 const char *Sep =
"";
236 Action.
edit.emplace();
237 Action.
edit->changes.emplace();
263 OutFn(std::move(Main), D.
Fixes);
266 for (
auto &Note : D.
Notes) {
267 if (!Note.InsideMainFile)
270 Res.
message = noteMessage(D, Note);
271 OutFn(std::move(Res), llvm::ArrayRef<Fix>());
277 case DiagnosticsEngine::Remark:
279 case DiagnosticsEngine::Note:
281 case DiagnosticsEngine::Warning:
283 case DiagnosticsEngine::Fatal:
284 case DiagnosticsEngine::Error:
286 case DiagnosticsEngine::Ignored:
289 llvm_unreachable(
"Unknown diagnostic level!");
295 const Preprocessor *) {
305 const clang::Diagnostic &
Info) {
306 DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info);
308 if (!LangOpts || !Info.hasSourceManager()) {
313 bool InsideMainFile = isInsideMainFile(Info);
316 D.
Range = diagnosticRange(Info, *LangOpts);
318 Info.FormatDiagnostic(Message);
321 D.
File = Info.getSourceManager().getFilename(Info.getLocation());
323 D.
Category = DiagnosticIDs::getCategoryNameFromID(
324 DiagnosticIDs::getCategoryNumberForDiag(Info.getID()))
329 auto AddFix = [&](
bool SyntheticMessage) ->
bool {
330 assert(!Info.getFixItHints().empty() &&
331 "diagnostic does not have attached fix-its");
335 llvm::SmallVector<TextEdit, 1> Edits;
336 for (
auto &
FixIt : Info.getFixItHints()) {
337 if (!isInsideMainFile(
FixIt.RemoveRange.getBegin(),
338 Info.getSourceManager()))
340 Edits.push_back(
toTextEdit(
FixIt, Info.getSourceManager(), *LangOpts));
345 if (SyntheticMessage && Info.getNumFixItHints() == 1) {
346 const auto &
FixIt = Info.getFixItHint(0);
347 bool Invalid =
false;
348 llvm::StringRef Remove = Lexer::getSourceText(
349 FixIt.RemoveRange, Info.getSourceManager(), *LangOpts, &Invalid);
350 llvm::StringRef Insert =
FixIt.CodeToInsert;
352 llvm::raw_svector_ostream M(Message);
353 if (!Remove.empty() && !Insert.empty())
354 M <<
"change '" << Remove <<
"' to '" << Insert <<
"'";
355 else if (!Remove.empty())
356 M <<
"remove '" << Remove <<
"'";
357 else if (!Insert.empty())
358 M <<
"insert '" << Insert <<
"'";
360 std::replace(Message.begin(), Message.end(),
'\n',
' ');
364 Info.FormatDiagnostic(Message);
365 LastDiag->Fixes.push_back(
Fix{Message.str(), std::move(Edits)});
369 if (!isNote(DiagLevel)) {
374 FillDiagBase(*LastDiag);
376 if (!Info.getFixItHints().empty())
381 assert(
false &&
"Adding a note without main diagnostic");
386 if (!Info.getFixItHints().empty()) {
396 LastDiag->Notes.push_back(std::move(N));
401 void StoreDiags::flushLastDiag() {
404 if (mentionsMainFile(*LastDiag))
405 Output.push_back(std::move(*LastDiag));
407 log(
"Dropped diagnostic outside main file: {0}: {1}", LastDiag->File,
SourceLocation Loc
'#' location in the include directive
static void log(DiagnosticsEngine::Level DiagLevel, const clang::Diagnostic &Info)
Position start
The range's start position.
std::vector< Diag > take()
bool EmbedFixesInDiagnostics
If true, Clangd uses an LSP extension to embed the fixes with the diagnostics that are sent to the cl...
Contains basic information about a diagnostic.
CodeAction toCodeAction(const Fix &F, const URIForFile &File)
Convert from Fix to LSP CodeAction.
llvm::Optional< std::vector< CodeAction > > codeActions
Clangd extension: code actions related to this diagnostic.
llvm::Optional< std::string > kind
The kind of the code action.
std::string title
A short, human-readable, title for this code action.
A code action represents a change that can be performed in code, e.g.
llvm::Optional< WorkspaceEdit > edit
The workspace edit this code action performs.
static const StringRef Message
Documents should not be synced at all.
void BeginSourceFile(const LangOptions &Opts, const Preprocessor *) override
A top-level diagnostic that may have Notes and Fixes.
std::string Message
Message for the fix-it.
std::vector< Fix > Fixes
Alternative fixes for this diagnostic, one should be chosen.
llvm::SmallVector< TextEdit, 1 > Edits
TextEdits from clang's fix-its. Must be non-empty.
TextEdit toTextEdit(const FixItHint &FixIt, const SourceManager &M, const LangOptions &L)
void toLSPDiags(const Diag &D, const URIForFile &File, const ClangdDiagnosticOptions &Opts, llvm::function_ref< void(clangd::Diagnostic, llvm::ArrayRef< Fix >)> OutFn)
Conversion to LSP diagnostics.
int getSeverity(DiagnosticsEngine::Level L)
Convert from clang diagnostic level to LSP severity.
void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, const clang::Diagnostic &Info) override
llvm::unique_function< void()> Action
void log(const char *Fmt, Ts &&... Vals)
llvm::Optional< std::string > category
The diagnostic's category.
DiagnosticsEngine::Level Severity
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
Represents a single fix-it that editor can apply to fix the error.
int line
Line position in a document (zero-based).
int character
Character offset on a line in a document (zero-based).
std::vector< Note > Notes
Elaborate on the problem, usually pointing to a related piece of code.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
std::string message
The diagnostic's code.
CharSourceRange Range
SourceRange for the file name.
static const llvm::StringLiteral QUICKFIX_KIND
int severity
The diagnostic's severity.
bool SendDiagnosticCategory
If true, Clangd uses an LSP extension to send the diagnostic's category to the client.
void EndSourceFile() override
llvm::Optional< FixItHint > FixIt
Range range
The range at which the message applies.
static cl::opt< bool > Fix("fix", cl::desc(R"(
Apply suggested fixes. Without -fix-errors
clang-tidy will bail out if any compilation
errors were found.
)"), cl::init(false), cl::cat(ClangTidyCategory))
Position end
The range's end position.
llvm::raw_ostream & operator<<(llvm::raw_ostream &OS, const CodeCompletion &C)
Range halfOpenToRange(const SourceManager &SM, CharSourceRange R)