10 #include "../clang-tidy/ClangTidyDiagnosticConsumer.h"
15 #include "clang/Basic/AllDiagnostics.h"
16 #include "clang/Basic/Diagnostic.h"
17 #include "clang/Basic/DiagnosticIDs.h"
18 #include "clang/Basic/FileManager.h"
19 #include "clang/Basic/SourceLocation.h"
20 #include "clang/Basic/SourceManager.h"
21 #include "clang/Lex/Lexer.h"
22 #include "clang/Lex/Token.h"
23 #include "llvm/ADT/ArrayRef.h"
24 #include "llvm/ADT/DenseSet.h"
25 #include "llvm/ADT/Optional.h"
26 #include "llvm/ADT/STLExtras.h"
27 #include "llvm/ADT/ScopeExit.h"
28 #include "llvm/ADT/SmallString.h"
29 #include "llvm/ADT/StringRef.h"
30 #include "llvm/ADT/Twine.h"
31 #include "llvm/Support/Capacity.h"
32 #include "llvm/Support/Path.h"
33 #include "llvm/Support/ScopedPrinter.h"
34 #include "llvm/Support/Signals.h"
35 #include "llvm/Support/raw_ostream.h"
43 const char *getDiagnosticCode(
unsigned ID) {
45 #define DIAG(ENUM, CLASS, DEFAULT_MAPPING, DESC, GROPU, SFINAE, NOWERROR, \
46 SHOWINSYSHEADER, CATEGORY) \
47 case clang::diag::ENUM: \
49 #include "clang/Basic/DiagnosticASTKinds.inc"
50 #include "clang/Basic/DiagnosticAnalysisKinds.inc"
51 #include "clang/Basic/DiagnosticCommentKinds.inc"
52 #include "clang/Basic/DiagnosticCommonKinds.inc"
53 #include "clang/Basic/DiagnosticDriverKinds.inc"
54 #include "clang/Basic/DiagnosticFrontendKinds.inc"
55 #include "clang/Basic/DiagnosticLexKinds.inc"
56 #include "clang/Basic/DiagnosticParseKinds.inc"
57 #include "clang/Basic/DiagnosticRefactoringKinds.inc"
58 #include "clang/Basic/DiagnosticSemaKinds.inc"
59 #include "clang/Basic/DiagnosticSerializationKinds.inc"
66 bool mentionsMainFile(
const Diag &D) {
72 for (
auto &N : D.Notes) {
79 bool isExcluded(
const Diag &D) {
81 if (D.ID == clang::diag::err_msasm_unable_to_create_target ||
82 D.ID == clang::diag::err_msasm_unsupported_arch)
89 bool locationInRange(SourceLocation L, CharSourceRange R,
90 const SourceManager &M) {
91 assert(R.isCharRange());
92 if (!R.isValid() || M.getFileID(R.getBegin()) != M.getFileID(R.getEnd()) ||
93 M.getFileID(R.getBegin()) != M.getFileID(L))
95 return L != R.getEnd() && M.isPointWithin(L, R.getBegin(), R.getEnd());
101 auto &M = D.getSourceManager();
102 auto Loc = M.getFileLoc(D.getLocation());
103 for (
const auto &CR : D.getRanges()) {
104 auto R = Lexer::makeFileCharRange(CR, M, L);
105 if (locationInRange(
Loc, R, M))
109 for (
const auto &F : D.getFixItHints()) {
110 auto R = Lexer::makeFileCharRange(F.RemoveRange, M, L);
111 if (locationInRange(
Loc, R, M))
116 auto R = CharSourceRange::getCharRange(
Loc);
118 if (!Lexer::getRawToken(
Loc, Tok, M, L,
true) && Tok.isNot(tok::comment)) {
119 R = CharSourceRange::getTokenRange(Tok.getLocation(), Tok.getEndLoc());
126 const char *getMainFileRange(
const Diag &D,
const SourceManager &SM,
127 SourceLocation DiagLoc,
Range &R) {
129 for (
const auto &N : D.Notes) {
130 if (N.InsideMainFile) {
132 case diag::note_template_class_instantiation_was_here:
133 case diag::note_template_class_explicit_specialization_was_here:
134 case diag::note_template_class_instantiation_here:
135 case diag::note_template_member_class_here:
136 case diag::note_template_member_function_here:
137 case diag::note_function_template_spec_here:
138 case diag::note_template_static_data_member_def_here:
139 case diag::note_template_variable_def_here:
140 case diag::note_template_enum_def_here:
141 case diag::note_template_nsdmi_here:
142 case diag::note_template_type_alias_instantiation_here:
143 case diag::note_template_exception_spec_instantiation_here:
144 case diag::note_template_requirement_instantiation_here:
145 case diag::note_evaluating_exception_spec_here:
146 case diag::note_default_arg_instantiation_here:
147 case diag::note_default_function_arg_instantiation_here:
148 case diag::note_explicit_template_arg_substitution_here:
149 case diag::note_function_template_deduction_instantiation_here:
150 case diag::note_deduced_template_arg_substitution_here:
151 case diag::note_prior_template_arg_substitution:
152 case diag::note_template_default_arg_checking:
153 case diag::note_concept_specialization_here:
154 case diag::note_nested_requirement_here:
155 case diag::note_checking_constraints_for_template_id_here:
156 case diag::note_checking_constraints_for_var_spec_id_here:
157 case diag::note_checking_constraints_for_class_spec_id_here:
158 case diag::note_checking_constraints_for_function_here:
159 case diag::note_constraint_substitution_here:
160 case diag::note_constraint_normalization_here:
161 case diag::note_parameter_mapping_substitution_here:
163 return "in template";
170 auto GetIncludeLoc = [&SM](SourceLocation SLoc) {
171 return SM.getIncludeLoc(SM.getFileID(SLoc));
173 for (
auto IncludeLocation = GetIncludeLoc(SM.getExpansionLoc(DiagLoc));
174 IncludeLocation.isValid();
175 IncludeLocation = GetIncludeLoc(IncludeLocation)) {
180 Lexer::getLocForEndOfToken(IncludeLocation, 0, SM, LangOptions()));
181 return "in included file";
191 bool tryMoveToMainFile(Diag &D, FullSourceLoc DiagLoc) {
192 const SourceManager &SM = DiagLoc.getManager();
193 DiagLoc = DiagLoc.getExpansionLoc();
195 const char *Prefix = getMainFileRange(D, SM, DiagLoc, R);
200 const auto *FE = SM.getFileEntryForID(SM.getFileID(DiagLoc));
201 D.Notes.emplace(D.Notes.begin());
202 Note &N = D.Notes.front();
203 N.AbsFile = std::string(FE->tryGetRealPathName());
204 N.File = std::string(FE->getName());
205 N.Message =
"error occurred here";
209 D.File = SM.getFileEntryForID(SM.getMainFileID())->getName().str();
210 D.Range = std::move(R);
211 D.InsideMainFile =
true;
213 D.Message = llvm::formatv(
"{0}: {1}", Prefix, D.Message);
218 if (!D.hasSourceManager())
224 bool isNote(DiagnosticsEngine::Level L) {
225 return L == DiagnosticsEngine::Note || L == DiagnosticsEngine::Remark;
228 llvm::StringRef diagLeveltoString(DiagnosticsEngine::Level Lvl) {
230 case DiagnosticsEngine::Ignored:
232 case DiagnosticsEngine::Note:
234 case DiagnosticsEngine::Remark:
240 case DiagnosticsEngine::Fatal:
241 return "fatal error";
243 llvm_unreachable(
"unhandled DiagnosticsEngine::Level");
257 void printDiag(llvm::raw_string_ostream &
OS,
const DiagBase &D) {
258 if (D.InsideMainFile) {
262 OS << llvm::sys::path::filename(D.File) <<
":";
268 auto Pos = D.Range.start;
272 if (D.InsideMainFile)
276 OS << diagLeveltoString(D.Severity) <<
": " << D.Message;
280 std::string capitalize(std::string
Message) {
296 std::string mainMessage(
const Diag &D,
const ClangdDiagnosticOptions &Opts) {
298 llvm::raw_string_ostream
OS(Result);
300 if (Opts.DisplayFixesCount && !D.Fixes.empty())
301 OS <<
" (" << (D.Fixes.size() > 1 ?
"fixes" :
"fix") <<
" available)";
303 if (!Opts.EmitRelatedLocations)
304 for (
auto &Note : D.Notes) {
309 return capitalize(std::move(Result));
313 std::string noteMessage(
const Diag &Main,
const DiagBase &Note,
314 const ClangdDiagnosticOptions &Opts) {
316 llvm::raw_string_ostream
OS(Result);
320 if (!Opts.EmitRelatedLocations) {
325 return capitalize(std::move(Result));
339 OS << F.Message <<
" {";
340 const char *Sep =
"";
341 for (
const auto &
Edit : F.Edits) {
349 OS << static_cast<const DiagBase &>(D);
350 if (!D.
Notes.empty()) {
352 const char *Sep =
"";
359 if (!D.
Fixes.empty()) {
361 const char *Sep =
"";
375 Action.edit->changes.emplace();
376 (*
Action.edit->changes)[File.uri()] = {F.Edits.begin(), F.Edits.end()};
393 llvm::find_if(D.
Notes, [](
const Note &N) { return N.InsideMainFile; });
394 assert(It != D.
Notes.end() &&
395 "neither the main diagnostic nor notes are inside main file");
396 Main.
range = It->Range;
405 Main.
source =
"clang-tidy";
410 if (Opts.EmbedFixesInDiagnostics) {
415 if (Opts.SendDiagnosticCategory && !D.
Category.empty())
418 Main.
message = mainMessage(D, Opts);
419 if (Opts.EmitRelatedLocations) {
423 vlog(
"Dropping note from unknown file: {0}",
Note);
434 OutFn(std::move(Main), D.
Fixes);
438 if (!Opts.EmitRelatedLocations)
446 OutFn(std::move(Res), llvm::ArrayRef<Fix>());
452 case DiagnosticsEngine::Remark:
454 case DiagnosticsEngine::Note:
458 case DiagnosticsEngine::Fatal:
461 case DiagnosticsEngine::Ignored:
464 llvm_unreachable(
"Unknown diagnostic level!");
472 for (
auto &
Diag : Output) {
473 if (
const char *ClangDiag = getDiagnosticCode(
Diag.
ID)) {
475 StringRef
Warning = DiagnosticIDs::getWarningOptionForDiag(
Diag.
ID);
479 StringRef
Name(ClangDiag);
482 Name.consume_front(
"err_");
488 if (Tidy !=
nullptr) {
490 if (!TidyDiag.empty()) {
495 auto CleanMessage = [&](std::string &Msg) {
497 if (Rest.consume_back(
"]") && Rest.consume_back(
Diag.
Name) &&
498 Rest.consume_back(
" ["))
499 Msg.resize(Rest.size());
513 std::set<std::pair<Range, std::string>> SeenDiags;
521 const Preprocessor *) {
533 constexpr
unsigned MaxLen = 50;
536 llvm::StringRef R =
Code.split(
'\n').first;
538 R = R.take_front(MaxLen);
541 if (R.size() !=
Code.size())
556 D.
Category = DiagnosticIDs::getCategoryNameFromID(
557 DiagnosticIDs::getCategoryNumberForDiag(
Info.getID()))
563 DiagnosticConsumer::HandleDiagnostic(DiagLevel,
Info);
564 bool OriginallyError =
565 Info.getDiags()->getDiagnosticIDs()->isDefaultMappingAsError(
568 if (
Info.getLocation().isInvalid()) {
571 if (!OriginallyError) {
580 LastDiagOriginallyError = OriginallyError;
581 LastDiag->ID =
Info.getID();
583 LastDiag->InsideMainFile =
true;
585 LastDiag->Range.start =
Position{0, 0};
586 LastDiag->Range.end =
Position{0, 0};
590 if (!LangOpts || !
Info.hasSourceManager()) {
596 SourceManager &SM =
Info.getSourceManager();
598 auto FillDiagBase = [&](
DiagBase &D) {
602 D.
Range = diagnosticRange(
Info, *LangOpts);
603 D.
File = std::string(SM.getFilename(
Info.getLocation()));
605 SM.getFileEntryForID(SM.getFileID(
Info.getLocation())), SM);
610 auto AddFix = [&](
bool SyntheticMessage) ->
bool {
611 assert(!
Info.getFixItHints().empty() &&
612 "diagnostic does not have attached fix-its");
617 auto FixIts =
Info.getFixItHints().vec();
618 llvm::SmallVector<TextEdit, 1> Edits;
619 for (
auto &
FixIt : FixIts) {
623 if (
FixIt.RemoveRange.getBegin().isMacroID() &&
624 FixIt.RemoveRange.getEnd().isMacroID() &&
625 SM.getFileID(
FixIt.RemoveRange.getBegin()) ==
626 SM.getFileID(
FixIt.RemoveRange.getEnd())) {
627 FixIt.RemoveRange = CharSourceRange(
628 {SM.getTopMacroCallerLoc(
FixIt.RemoveRange.getBegin()),
629 SM.getTopMacroCallerLoc(
FixIt.RemoveRange.getEnd())},
630 FixIt.RemoveRange.isTokenRange());
633 if (
FixIt.RemoveRange.getBegin().isMacroID() ||
634 FixIt.RemoveRange.getEnd().isMacroID())
643 if (SyntheticMessage && FixIts.size() == 1) {
644 const auto &
FixIt = FixIts.front();
645 bool Invalid =
false;
646 llvm::StringRef Remove =
647 Lexer::getSourceText(
FixIt.RemoveRange, SM, *LangOpts, &Invalid);
648 llvm::StringRef Insert =
FixIt.CodeToInsert;
650 llvm::raw_svector_ostream M(
Message);
651 if (!Remove.empty() && !Insert.empty()) {
657 }
else if (!Remove.empty()) {
661 }
else if (!Insert.empty()) {
672 LastDiag->Fixes.push_back(
673 Fix{std::string(
Message.str()), std::move(Edits)});
677 if (!isNote(DiagLevel)) {
682 DiagLevel = Adjuster(DiagLevel,
Info);
683 if (DiagLevel == DiagnosticsEngine::Ignored) {
684 LastPrimaryDiagnosticWasSuppressed =
true;
688 LastPrimaryDiagnosticWasSuppressed =
false;
691 FillDiagBase(*LastDiag);
692 LastDiagLoc.emplace(
Info.getLocation(),
Info.getSourceManager());
693 LastDiagOriginallyError = OriginallyError;
695 if (!
Info.getFixItHints().empty())
698 auto ExtraFixes = Fixer(DiagLevel,
Info);
699 LastDiag->Fixes.insert(LastDiag->Fixes.end(), ExtraFixes.begin(),
707 if (LastPrimaryDiagnosticWasSuppressed) {
712 assert(
false &&
"Adding a note without main diagnostic");
717 if (!
Info.getFixItHints().empty()) {
727 LastDiag->Notes.push_back(std::move(N));
732 void StoreDiags::flushLastDiag() {
735 auto Finish = llvm::make_scope_exit([&, NDiags(Output.size())] {
736 if (Output.size() == NDiags)
737 vlog(
"Dropped diagnostic: {0}: {1}", LastDiag->File, LastDiag->Message);
741 if (isExcluded(*LastDiag))
744 if (!LastDiag->InsideMainFile && LastDiagLoc && LastDiagOriginallyError) {
745 if (tryMoveToMainFile(*LastDiag, *LastDiagLoc)) {
747 if (!IncludedErrorLocations
748 .insert({LastDiag->Range.start.line,
749 LastDiag->Range.start.character})
754 if (!mentionsMainFile(*LastDiag))
756 Output.push_back(std::move(*LastDiag));