xref: /llvm-project/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp (revision eda72fac548f317cec997967494763e9a7bafa27)
1 //===--- tools/extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp ----------=== //
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 ///  \file This file implements ClangTidyDiagnosticConsumer, ClangTidyContext
10 ///  and ClangTidyError classes.
11 ///
12 ///  This tool uses the Clang Tooling infrastructure, see
13 ///    http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html
14 ///  for details on setting it up with LLVM source tree.
15 ///
16 //===----------------------------------------------------------------------===//
17 
18 #include "ClangTidyDiagnosticConsumer.h"
19 #include "ClangTidyOptions.h"
20 #include "GlobList.h"
21 #include "NoLintDirectiveHandler.h"
22 #include "clang/AST/ASTContext.h"
23 #include "clang/AST/ASTDiagnostic.h"
24 #include "clang/AST/Attr.h"
25 #include "clang/Basic/CharInfo.h"
26 #include "clang/Basic/Diagnostic.h"
27 #include "clang/Basic/DiagnosticOptions.h"
28 #include "clang/Basic/FileManager.h"
29 #include "clang/Basic/SourceManager.h"
30 #include "clang/Frontend/DiagnosticRenderer.h"
31 #include "clang/Lex/Lexer.h"
32 #include "clang/Tooling/Core/Diagnostic.h"
33 #include "clang/Tooling/Core/Replacement.h"
34 #include "llvm/ADT/BitVector.h"
35 #include "llvm/ADT/STLExtras.h"
36 #include "llvm/ADT/SmallString.h"
37 #include "llvm/ADT/StringMap.h"
38 #include "llvm/Support/FormatVariadic.h"
39 #include "llvm/Support/Regex.h"
40 #include <optional>
41 #include <tuple>
42 #include <utility>
43 #include <vector>
44 using namespace clang;
45 using namespace tidy;
46 
47 namespace {
48 class ClangTidyDiagnosticRenderer : public DiagnosticRenderer {
49 public:
50   ClangTidyDiagnosticRenderer(const LangOptions &LangOpts,
51                               DiagnosticOptions *DiagOpts,
52                               ClangTidyError &Error)
53       : DiagnosticRenderer(LangOpts, DiagOpts), Error(Error) {}
54 
55 protected:
56   void emitDiagnosticMessage(FullSourceLoc Loc, PresumedLoc PLoc,
57                              DiagnosticsEngine::Level Level, StringRef Message,
58                              ArrayRef<CharSourceRange> Ranges,
59                              DiagOrStoredDiag Info) override {
60     // Remove check name from the message.
61     // FIXME: Remove this once there's a better way to pass check names than
62     // appending the check name to the message in ClangTidyContext::diag and
63     // using getCustomDiagID.
64     std::string CheckNameInMessage = " [" + Error.DiagnosticName + "]";
65     Message.consume_back(CheckNameInMessage);
66 
67     auto TidyMessage =
68         Loc.isValid()
69             ? tooling::DiagnosticMessage(Message, Loc.getManager(), Loc)
70             : tooling::DiagnosticMessage(Message);
71 
72     // Make sure that if a TokenRange is received from the check it is unfurled
73     // into a real CharRange for the diagnostic printer later.
74     // Whatever we store here gets decoupled from the current SourceManager, so
75     // we **have to** know the exact position and length of the highlight.
76     auto ToCharRange = [this, &Loc](const CharSourceRange &SourceRange) {
77       if (SourceRange.isCharRange())
78         return SourceRange;
79       assert(SourceRange.isTokenRange());
80       SourceLocation End = Lexer::getLocForEndOfToken(
81           SourceRange.getEnd(), 0, Loc.getManager(), LangOpts);
82       return CharSourceRange::getCharRange(SourceRange.getBegin(), End);
83     };
84 
85     // We are only interested in valid ranges.
86     auto ValidRanges =
87         llvm::make_filter_range(Ranges, [](const CharSourceRange &R) {
88           return R.getAsRange().isValid();
89         });
90 
91     if (Level == DiagnosticsEngine::Note) {
92       Error.Notes.push_back(TidyMessage);
93       for (const CharSourceRange &SourceRange : ValidRanges)
94         Error.Notes.back().Ranges.emplace_back(Loc.getManager(),
95                                                ToCharRange(SourceRange));
96       return;
97     }
98     assert(Error.Message.Message.empty() && "Overwriting a diagnostic message");
99     Error.Message = TidyMessage;
100     for (const CharSourceRange &SourceRange : ValidRanges)
101       Error.Message.Ranges.emplace_back(Loc.getManager(),
102                                         ToCharRange(SourceRange));
103   }
104 
105   void emitDiagnosticLoc(FullSourceLoc Loc, PresumedLoc PLoc,
106                          DiagnosticsEngine::Level Level,
107                          ArrayRef<CharSourceRange> Ranges) override {}
108 
109   void emitCodeContext(FullSourceLoc Loc, DiagnosticsEngine::Level Level,
110                        SmallVectorImpl<CharSourceRange> &Ranges,
111                        ArrayRef<FixItHint> Hints) override {
112     assert(Loc.isValid());
113     tooling::DiagnosticMessage *DiagWithFix =
114         Level == DiagnosticsEngine::Note ? &Error.Notes.back() : &Error.Message;
115 
116     for (const auto &FixIt : Hints) {
117       CharSourceRange Range = FixIt.RemoveRange;
118       assert(Range.getBegin().isValid() && Range.getEnd().isValid() &&
119              "Invalid range in the fix-it hint.");
120       assert(Range.getBegin().isFileID() && Range.getEnd().isFileID() &&
121              "Only file locations supported in fix-it hints.");
122 
123       tooling::Replacement Replacement(Loc.getManager(), Range,
124                                        FixIt.CodeToInsert);
125       llvm::Error Err =
126           DiagWithFix->Fix[Replacement.getFilePath()].add(Replacement);
127       // FIXME: better error handling (at least, don't let other replacements be
128       // applied).
129       if (Err) {
130         llvm::errs() << "Fix conflicts with existing fix! "
131                      << llvm::toString(std::move(Err)) << "\n";
132         assert(false && "Fix conflicts with existing fix!");
133       }
134     }
135   }
136 
137   void emitIncludeLocation(FullSourceLoc Loc, PresumedLoc PLoc) override {}
138 
139   void emitImportLocation(FullSourceLoc Loc, PresumedLoc PLoc,
140                           StringRef ModuleName) override {}
141 
142   void emitBuildingModuleLocation(FullSourceLoc Loc, PresumedLoc PLoc,
143                                   StringRef ModuleName) override {}
144 
145   void endDiagnostic(DiagOrStoredDiag D,
146                      DiagnosticsEngine::Level Level) override {
147     assert(!Error.Message.Message.empty() && "Message has not been set");
148   }
149 
150 private:
151   ClangTidyError &Error;
152 };
153 } // end anonymous namespace
154 
155 ClangTidyError::ClangTidyError(StringRef CheckName,
156                                ClangTidyError::Level DiagLevel,
157                                StringRef BuildDirectory, bool IsWarningAsError)
158     : tooling::Diagnostic(CheckName, DiagLevel, BuildDirectory),
159       IsWarningAsError(IsWarningAsError) {}
160 
161 ClangTidyContext::ClangTidyContext(
162     std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider,
163     bool AllowEnablingAnalyzerAlphaCheckers, bool EnableModuleHeadersParsing)
164     : OptionsProvider(std::move(OptionsProvider)),
165 
166       AllowEnablingAnalyzerAlphaCheckers(AllowEnablingAnalyzerAlphaCheckers),
167       EnableModuleHeadersParsing(EnableModuleHeadersParsing) {
168   // Before the first translation unit we can get errors related to command-line
169   // parsing, use empty string for the file name in this case.
170   setCurrentFile("");
171 }
172 
173 ClangTidyContext::~ClangTidyContext() = default;
174 
175 DiagnosticBuilder ClangTidyContext::diag(
176     StringRef CheckName, SourceLocation Loc, StringRef Description,
177     DiagnosticIDs::Level Level /* = DiagnosticIDs::Warning*/) {
178   assert(Loc.isValid());
179   unsigned ID = DiagEngine->getDiagnosticIDs()->getCustomDiagID(
180       Level, (Description + " [" + CheckName + "]").str());
181   CheckNamesByDiagnosticID.try_emplace(ID, CheckName);
182   return DiagEngine->Report(Loc, ID);
183 }
184 
185 DiagnosticBuilder ClangTidyContext::diag(
186     StringRef CheckName, StringRef Description,
187     DiagnosticIDs::Level Level /* = DiagnosticIDs::Warning*/) {
188   unsigned ID = DiagEngine->getDiagnosticIDs()->getCustomDiagID(
189       Level, (Description + " [" + CheckName + "]").str());
190   CheckNamesByDiagnosticID.try_emplace(ID, CheckName);
191   return DiagEngine->Report(ID);
192 }
193 
194 DiagnosticBuilder ClangTidyContext::diag(const tooling::Diagnostic &Error) {
195   SourceManager &SM = DiagEngine->getSourceManager();
196   FileManager &FM = SM.getFileManager();
197   FileEntryRef File = llvm::cantFail(FM.getFileRef(Error.Message.FilePath));
198   FileID ID = SM.getOrCreateFileID(File, SrcMgr::C_User);
199   SourceLocation FileStartLoc = SM.getLocForStartOfFile(ID);
200   SourceLocation Loc = FileStartLoc.getLocWithOffset(
201       static_cast<SourceLocation::IntTy>(Error.Message.FileOffset));
202   return diag(Error.DiagnosticName, Loc, Error.Message.Message,
203               static_cast<DiagnosticIDs::Level>(Error.DiagLevel));
204 }
205 
206 DiagnosticBuilder ClangTidyContext::configurationDiag(
207     StringRef Message,
208     DiagnosticIDs::Level Level /* = DiagnosticIDs::Warning*/) {
209   return diag("clang-tidy-config", Message, Level);
210 }
211 
212 bool ClangTidyContext::shouldSuppressDiagnostic(
213     DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info,
214     SmallVectorImpl<tooling::Diagnostic> &NoLintErrors, bool AllowIO,
215     bool EnableNoLintBlocks) {
216   std::string CheckName = getCheckName(Info.getID());
217   return NoLintHandler.shouldSuppress(DiagLevel, Info, CheckName, NoLintErrors,
218                                       AllowIO, EnableNoLintBlocks);
219 }
220 
221 void ClangTidyContext::setSourceManager(SourceManager *SourceMgr) {
222   DiagEngine->setSourceManager(SourceMgr);
223 }
224 
225 static bool parseFileExtensions(llvm::ArrayRef<std::string> AllFileExtensions,
226                                 FileExtensionsSet &FileExtensions) {
227   FileExtensions.clear();
228   for (StringRef Suffix : AllFileExtensions) {
229     StringRef Extension = Suffix.trim();
230     if (!llvm::all_of(Extension, isAlphanumeric))
231       return false;
232     FileExtensions.insert(Extension);
233   }
234   return true;
235 }
236 
237 void ClangTidyContext::setCurrentFile(StringRef File) {
238   CurrentFile = std::string(File);
239   CurrentOptions = getOptionsForFile(CurrentFile);
240   CheckFilter = std::make_unique<CachedGlobList>(*getOptions().Checks);
241   WarningAsErrorFilter =
242       std::make_unique<CachedGlobList>(*getOptions().WarningsAsErrors);
243   if (!parseFileExtensions(*getOptions().HeaderFileExtensions,
244                            HeaderFileExtensions))
245     this->configurationDiag("Invalid header file extensions");
246   if (!parseFileExtensions(*getOptions().ImplementationFileExtensions,
247                            ImplementationFileExtensions))
248     this->configurationDiag("Invalid implementation file extensions");
249 }
250 
251 void ClangTidyContext::setASTContext(ASTContext *Context) {
252   DiagEngine->SetArgToStringFn(&FormatASTNodeDiagnosticArgument, Context);
253   LangOpts = Context->getLangOpts();
254 }
255 
256 const ClangTidyGlobalOptions &ClangTidyContext::getGlobalOptions() const {
257   return OptionsProvider->getGlobalOptions();
258 }
259 
260 const ClangTidyOptions &ClangTidyContext::getOptions() const {
261   return CurrentOptions;
262 }
263 
264 ClangTidyOptions ClangTidyContext::getOptionsForFile(StringRef File) const {
265   // Merge options on top of getDefaults() as a safeguard against options with
266   // unset values.
267   return ClangTidyOptions::getDefaults().merge(
268       OptionsProvider->getOptions(File), 0);
269 }
270 
271 void ClangTidyContext::setEnableProfiling(bool P) { Profile = P; }
272 
273 void ClangTidyContext::setProfileStoragePrefix(StringRef Prefix) {
274   ProfilePrefix = std::string(Prefix);
275 }
276 
277 std::optional<ClangTidyProfiling::StorageParams>
278 ClangTidyContext::getProfileStorageParams() const {
279   if (ProfilePrefix.empty())
280     return std::nullopt;
281 
282   return ClangTidyProfiling::StorageParams(ProfilePrefix, CurrentFile);
283 }
284 
285 bool ClangTidyContext::isCheckEnabled(StringRef CheckName) const {
286   assert(CheckFilter != nullptr);
287   return CheckFilter->contains(CheckName);
288 }
289 
290 bool ClangTidyContext::treatAsError(StringRef CheckName) const {
291   assert(WarningAsErrorFilter != nullptr);
292   return WarningAsErrorFilter->contains(CheckName);
293 }
294 
295 std::string ClangTidyContext::getCheckName(unsigned DiagnosticID) const {
296   std::string ClangWarningOption = std::string(
297       DiagEngine->getDiagnosticIDs()->getWarningOptionForDiag(DiagnosticID));
298   if (!ClangWarningOption.empty())
299     return "clang-diagnostic-" + ClangWarningOption;
300   llvm::DenseMap<unsigned, std::string>::const_iterator I =
301       CheckNamesByDiagnosticID.find(DiagnosticID);
302   if (I != CheckNamesByDiagnosticID.end())
303     return I->second;
304   return "";
305 }
306 
307 ClangTidyDiagnosticConsumer::ClangTidyDiagnosticConsumer(
308     ClangTidyContext &Ctx, DiagnosticsEngine *ExternalDiagEngine,
309     bool RemoveIncompatibleErrors, bool GetFixesFromNotes,
310     bool EnableNolintBlocks)
311     : Context(Ctx), ExternalDiagEngine(ExternalDiagEngine),
312       RemoveIncompatibleErrors(RemoveIncompatibleErrors),
313       GetFixesFromNotes(GetFixesFromNotes),
314       EnableNolintBlocks(EnableNolintBlocks) {
315 
316   if (Context.getOptions().HeaderFilterRegex &&
317       !Context.getOptions().HeaderFilterRegex->empty())
318     HeaderFilter =
319         std::make_unique<llvm::Regex>(*Context.getOptions().HeaderFilterRegex);
320 
321   if (Context.getOptions().ExcludeHeaderFilterRegex &&
322       !Context.getOptions().ExcludeHeaderFilterRegex->empty())
323     ExcludeHeaderFilter = std::make_unique<llvm::Regex>(
324         *Context.getOptions().ExcludeHeaderFilterRegex);
325 }
326 
327 void ClangTidyDiagnosticConsumer::finalizeLastError() {
328   if (!Errors.empty()) {
329     ClangTidyError &Error = Errors.back();
330     if (Error.DiagnosticName == "clang-tidy-config") {
331       // Never ignore these.
332     } else if (!Context.isCheckEnabled(Error.DiagnosticName) &&
333                Error.DiagLevel != ClangTidyError::Error) {
334       ++Context.Stats.ErrorsIgnoredCheckFilter;
335       Errors.pop_back();
336     } else if (!LastErrorRelatesToUserCode) {
337       ++Context.Stats.ErrorsIgnoredNonUserCode;
338       Errors.pop_back();
339     } else if (!LastErrorPassesLineFilter) {
340       ++Context.Stats.ErrorsIgnoredLineFilter;
341       Errors.pop_back();
342     } else {
343       ++Context.Stats.ErrorsDisplayed;
344     }
345   }
346   LastErrorRelatesToUserCode = false;
347   LastErrorPassesLineFilter = false;
348 }
349 
350 namespace clang::tidy {
351 
352 const llvm::StringMap<tooling::Replacements> *
353 getFixIt(const tooling::Diagnostic &Diagnostic, bool AnyFix) {
354   if (!Diagnostic.Message.Fix.empty())
355     return &Diagnostic.Message.Fix;
356   if (!AnyFix)
357     return nullptr;
358   const llvm::StringMap<tooling::Replacements> *Result = nullptr;
359   for (const auto &Note : Diagnostic.Notes) {
360     if (!Note.Fix.empty()) {
361       if (Result)
362         // We have 2 different fixes in notes, bail out.
363         return nullptr;
364       Result = &Note.Fix;
365     }
366   }
367   return Result;
368 }
369 
370 } // namespace clang::tidy
371 
372 void ClangTidyDiagnosticConsumer::HandleDiagnostic(
373     DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) {
374   if (LastErrorWasIgnored && DiagLevel == DiagnosticsEngine::Note)
375     return;
376 
377   SmallVector<tooling::Diagnostic, 1> SuppressionErrors;
378   if (Context.shouldSuppressDiagnostic(DiagLevel, Info, SuppressionErrors,
379                                        EnableNolintBlocks)) {
380     ++Context.Stats.ErrorsIgnoredNOLINT;
381     // Ignored a warning, should ignore related notes as well
382     LastErrorWasIgnored = true;
383     for (const auto &Error : SuppressionErrors)
384       Context.diag(Error);
385     return;
386   }
387 
388   LastErrorWasIgnored = false;
389   // Count warnings/errors.
390   DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info);
391 
392   if (DiagLevel == DiagnosticsEngine::Note) {
393     assert(!Errors.empty() &&
394            "A diagnostic note can only be appended to a message.");
395   } else {
396     finalizeLastError();
397     std::string CheckName = Context.getCheckName(Info.getID());
398     if (CheckName.empty()) {
399       // This is a compiler diagnostic without a warning option. Assign check
400       // name based on its level.
401       switch (DiagLevel) {
402       case DiagnosticsEngine::Error:
403       case DiagnosticsEngine::Fatal:
404         CheckName = "clang-diagnostic-error";
405         break;
406       case DiagnosticsEngine::Warning:
407         CheckName = "clang-diagnostic-warning";
408         break;
409       case DiagnosticsEngine::Remark:
410         CheckName = "clang-diagnostic-remark";
411         break;
412       default:
413         CheckName = "clang-diagnostic-unknown";
414         break;
415       }
416     }
417 
418     ClangTidyError::Level Level = ClangTidyError::Warning;
419     if (DiagLevel == DiagnosticsEngine::Error ||
420         DiagLevel == DiagnosticsEngine::Fatal) {
421       // Force reporting of Clang errors regardless of filters and non-user
422       // code.
423       Level = ClangTidyError::Error;
424       LastErrorRelatesToUserCode = true;
425       LastErrorPassesLineFilter = true;
426     } else if (DiagLevel == DiagnosticsEngine::Remark) {
427       Level = ClangTidyError::Remark;
428     }
429 
430     bool IsWarningAsError = DiagLevel == DiagnosticsEngine::Warning &&
431                             Context.treatAsError(CheckName);
432     Errors.emplace_back(CheckName, Level, Context.getCurrentBuildDirectory(),
433                         IsWarningAsError);
434   }
435 
436   if (ExternalDiagEngine) {
437     // If there is an external diagnostics engine, like in the
438     // ClangTidyPluginAction case, forward the diagnostics to it.
439     forwardDiagnostic(Info);
440   } else {
441     ClangTidyDiagnosticRenderer Converter(
442         Context.getLangOpts(), &Context.DiagEngine->getDiagnosticOptions(),
443         Errors.back());
444     SmallString<100> Message;
445     Info.FormatDiagnostic(Message);
446     FullSourceLoc Loc;
447     if (Info.hasSourceManager())
448       Loc = FullSourceLoc(Info.getLocation(), Info.getSourceManager());
449     else if (Context.DiagEngine->hasSourceManager())
450       Loc = FullSourceLoc(Info.getLocation(),
451                           Context.DiagEngine->getSourceManager());
452     Converter.emitDiagnostic(Loc, DiagLevel, Message, Info.getRanges(),
453                              Info.getFixItHints());
454   }
455 
456   if (Info.hasSourceManager())
457     checkFilters(Info.getLocation(), Info.getSourceManager());
458 
459   for (const auto &Error : SuppressionErrors)
460     Context.diag(Error);
461 }
462 
463 bool ClangTidyDiagnosticConsumer::passesLineFilter(StringRef FileName,
464                                                    unsigned LineNumber) const {
465   if (Context.getGlobalOptions().LineFilter.empty())
466     return true;
467   for (const FileFilter &Filter : Context.getGlobalOptions().LineFilter) {
468     if (FileName.ends_with(Filter.Name)) {
469       if (Filter.LineRanges.empty())
470         return true;
471       for (const FileFilter::LineRange &Range : Filter.LineRanges) {
472         if (Range.first <= LineNumber && LineNumber <= Range.second)
473           return true;
474       }
475       return false;
476     }
477   }
478   return false;
479 }
480 
481 void ClangTidyDiagnosticConsumer::forwardDiagnostic(const Diagnostic &Info) {
482   // Acquire a diagnostic ID also in the external diagnostics engine.
483   auto DiagLevelAndFormatString =
484       Context.getDiagLevelAndFormatString(Info.getID(), Info.getLocation());
485   unsigned ExternalID = ExternalDiagEngine->getDiagnosticIDs()->getCustomDiagID(
486       DiagLevelAndFormatString.first, DiagLevelAndFormatString.second);
487 
488   // Forward the details.
489   auto Builder = ExternalDiagEngine->Report(Info.getLocation(), ExternalID);
490   for (const FixItHint &Hint : Info.getFixItHints())
491     Builder << Hint;
492   for (auto Range : Info.getRanges())
493     Builder << Range;
494   for (unsigned Index = 0; Index < Info.getNumArgs(); ++Index) {
495     DiagnosticsEngine::ArgumentKind Kind = Info.getArgKind(Index);
496     switch (Kind) {
497     case clang::DiagnosticsEngine::ak_std_string:
498       Builder << Info.getArgStdStr(Index);
499       break;
500     case clang::DiagnosticsEngine::ak_c_string:
501       Builder << Info.getArgCStr(Index);
502       break;
503     case clang::DiagnosticsEngine::ak_sint:
504       Builder << Info.getArgSInt(Index);
505       break;
506     case clang::DiagnosticsEngine::ak_uint:
507       Builder << Info.getArgUInt(Index);
508       break;
509     case clang::DiagnosticsEngine::ak_tokenkind:
510       Builder << static_cast<tok::TokenKind>(Info.getRawArg(Index));
511       break;
512     case clang::DiagnosticsEngine::ak_identifierinfo:
513       Builder << Info.getArgIdentifier(Index);
514       break;
515     case clang::DiagnosticsEngine::ak_qual:
516       Builder << Qualifiers::fromOpaqueValue(Info.getRawArg(Index));
517       break;
518     case clang::DiagnosticsEngine::ak_qualtype:
519       Builder << QualType::getFromOpaquePtr((void *)Info.getRawArg(Index));
520       break;
521     case clang::DiagnosticsEngine::ak_declarationname:
522       Builder << DeclarationName::getFromOpaqueInteger(Info.getRawArg(Index));
523       break;
524     case clang::DiagnosticsEngine::ak_nameddecl:
525       Builder << reinterpret_cast<const NamedDecl *>(Info.getRawArg(Index));
526       break;
527     case clang::DiagnosticsEngine::ak_nestednamespec:
528       Builder << reinterpret_cast<NestedNameSpecifier *>(Info.getRawArg(Index));
529       break;
530     case clang::DiagnosticsEngine::ak_declcontext:
531       Builder << reinterpret_cast<DeclContext *>(Info.getRawArg(Index));
532       break;
533     case clang::DiagnosticsEngine::ak_qualtype_pair:
534       assert(false); // This one is not passed around.
535       break;
536     case clang::DiagnosticsEngine::ak_attr:
537       Builder << reinterpret_cast<Attr *>(Info.getRawArg(Index));
538       break;
539     case clang::DiagnosticsEngine::ak_addrspace:
540       Builder << static_cast<LangAS>(Info.getRawArg(Index));
541       break;
542     }
543   }
544 }
545 
546 void ClangTidyDiagnosticConsumer::checkFilters(SourceLocation Location,
547                                                const SourceManager &Sources) {
548   // Invalid location may mean a diagnostic in a command line, don't skip these.
549   if (!Location.isValid()) {
550     LastErrorRelatesToUserCode = true;
551     LastErrorPassesLineFilter = true;
552     return;
553   }
554 
555   if (!*Context.getOptions().SystemHeaders &&
556       (Sources.isInSystemHeader(Location) || Sources.isInSystemMacro(Location)))
557     return;
558 
559   // FIXME: We start with a conservative approach here, but the actual type of
560   // location needed depends on the check (in particular, where this check wants
561   // to apply fixes).
562   FileID FID = Sources.getDecomposedExpansionLoc(Location).first;
563   OptionalFileEntryRef File = Sources.getFileEntryRefForID(FID);
564 
565   // -DMACRO definitions on the command line have locations in a virtual buffer
566   // that doesn't have a FileEntry. Don't skip these as well.
567   if (!File) {
568     LastErrorRelatesToUserCode = true;
569     LastErrorPassesLineFilter = true;
570     return;
571   }
572 
573   StringRef FileName(File->getName());
574   LastErrorRelatesToUserCode =
575       LastErrorRelatesToUserCode || Sources.isInMainFile(Location) ||
576       (HeaderFilter &&
577        (HeaderFilter->match(FileName) &&
578         !(ExcludeHeaderFilter && ExcludeHeaderFilter->match(FileName))));
579 
580   unsigned LineNumber = Sources.getExpansionLineNumber(Location);
581   LastErrorPassesLineFilter =
582       LastErrorPassesLineFilter || passesLineFilter(FileName, LineNumber);
583 }
584 
585 void ClangTidyDiagnosticConsumer::removeIncompatibleErrors() {
586   // Each error is modelled as the set of intervals in which it applies
587   // replacements. To detect overlapping replacements, we use a sweep line
588   // algorithm over these sets of intervals.
589   // An event here consists of the opening or closing of an interval. During the
590   // process, we maintain a counter with the amount of open intervals. If we
591   // find an endpoint of an interval and this counter is different from 0, it
592   // means that this interval overlaps with another one, so we set it as
593   // inapplicable.
594   struct Event {
595     // An event can be either the begin or the end of an interval.
596     enum EventType {
597       ET_Begin = 1,
598       ET_Insert = 0,
599       ET_End = -1,
600     };
601 
602     Event(unsigned Begin, unsigned End, EventType Type, unsigned ErrorId,
603           unsigned ErrorSize)
604         : Type(Type), ErrorId(ErrorId) {
605       // The events are going to be sorted by their position. In case of draw:
606       //
607       // * If an interval ends at the same position at which other interval
608       //   begins, this is not an overlapping, so we want to remove the ending
609       //   interval before adding the starting one: end events have higher
610       //   priority than begin events.
611       //
612       // * If we have several begin points at the same position, we will mark as
613       //   inapplicable the ones that we process later, so the first one has to
614       //   be the one with the latest end point, because this one will contain
615       //   all the other intervals. For the same reason, if we have several end
616       //   points in the same position, the last one has to be the one with the
617       //   earliest begin point. In both cases, we sort non-increasingly by the
618       //   position of the complementary.
619       //
620       // * In case of two equal intervals, the one whose error is bigger can
621       //   potentially contain the other one, so we want to process its begin
622       //   points before and its end points later.
623       //
624       // * Finally, if we have two equal intervals whose errors have the same
625       //   size, none of them will be strictly contained inside the other.
626       //   Sorting by ErrorId will guarantee that the begin point of the first
627       //   one will be processed before, disallowing the second one, and the
628       //   end point of the first one will also be processed before,
629       //   disallowing the first one.
630       switch (Type) {
631       case ET_Begin:
632         Priority = std::make_tuple(Begin, Type, -End, -ErrorSize, ErrorId);
633         break;
634       case ET_Insert:
635         Priority = std::make_tuple(Begin, Type, -End, ErrorSize, ErrorId);
636         break;
637       case ET_End:
638         Priority = std::make_tuple(End, Type, -Begin, ErrorSize, ErrorId);
639         break;
640       }
641     }
642 
643     bool operator<(const Event &Other) const {
644       return Priority < Other.Priority;
645     }
646 
647     // Determines if this event is the begin or the end of an interval.
648     EventType Type;
649     // The index of the error to which the interval that generated this event
650     // belongs.
651     unsigned ErrorId;
652     // The events will be sorted based on this field.
653     std::tuple<unsigned, EventType, int, int, unsigned> Priority;
654   };
655 
656   removeDuplicatedDiagnosticsOfAliasCheckers();
657 
658   // Compute error sizes.
659   std::vector<int> Sizes;
660   std::vector<
661       std::pair<ClangTidyError *, llvm::StringMap<tooling::Replacements> *>>
662       ErrorFixes;
663   for (auto &Error : Errors) {
664     if (const auto *Fix = getFixIt(Error, GetFixesFromNotes))
665       ErrorFixes.emplace_back(
666           &Error, const_cast<llvm::StringMap<tooling::Replacements> *>(Fix));
667   }
668   for (const auto &ErrorAndFix : ErrorFixes) {
669     int Size = 0;
670     for (const auto &FileAndReplaces : *ErrorAndFix.second) {
671       for (const auto &Replace : FileAndReplaces.second)
672         Size += Replace.getLength();
673     }
674     Sizes.push_back(Size);
675   }
676 
677   // Build events from error intervals.
678   llvm::StringMap<std::vector<Event>> FileEvents;
679   for (unsigned I = 0; I < ErrorFixes.size(); ++I) {
680     for (const auto &FileAndReplace : *ErrorFixes[I].second) {
681       for (const auto &Replace : FileAndReplace.second) {
682         unsigned Begin = Replace.getOffset();
683         unsigned End = Begin + Replace.getLength();
684         auto &Events = FileEvents[Replace.getFilePath()];
685         if (Begin == End) {
686           Events.emplace_back(Begin, End, Event::ET_Insert, I, Sizes[I]);
687         } else {
688           Events.emplace_back(Begin, End, Event::ET_Begin, I, Sizes[I]);
689           Events.emplace_back(Begin, End, Event::ET_End, I, Sizes[I]);
690         }
691       }
692     }
693   }
694 
695   llvm::BitVector Apply(ErrorFixes.size(), true);
696   for (auto &FileAndEvents : FileEvents) {
697     std::vector<Event> &Events = FileAndEvents.second;
698     // Sweep.
699     llvm::sort(Events);
700     int OpenIntervals = 0;
701     for (const auto &Event : Events) {
702       switch (Event.Type) {
703       case Event::ET_Begin:
704         if (OpenIntervals++ != 0)
705           Apply[Event.ErrorId] = false;
706         break;
707       case Event::ET_Insert:
708         if (OpenIntervals != 0)
709           Apply[Event.ErrorId] = false;
710         break;
711       case Event::ET_End:
712         if (--OpenIntervals != 0)
713           Apply[Event.ErrorId] = false;
714         break;
715       }
716     }
717     assert(OpenIntervals == 0 && "Amount of begin/end points doesn't match");
718   }
719 
720   for (unsigned I = 0; I < ErrorFixes.size(); ++I) {
721     if (!Apply[I]) {
722       ErrorFixes[I].second->clear();
723       ErrorFixes[I].first->Notes.emplace_back(
724           "this fix will not be applied because it overlaps with another fix");
725     }
726   }
727 }
728 
729 namespace {
730 struct LessClangTidyError {
731   bool operator()(const ClangTidyError &LHS, const ClangTidyError &RHS) const {
732     const tooling::DiagnosticMessage &M1 = LHS.Message;
733     const tooling::DiagnosticMessage &M2 = RHS.Message;
734 
735     return std::tie(M1.FilePath, M1.FileOffset, LHS.DiagnosticName,
736                     M1.Message) <
737            std::tie(M2.FilePath, M2.FileOffset, RHS.DiagnosticName, M2.Message);
738   }
739 };
740 struct EqualClangTidyError {
741   bool operator()(const ClangTidyError &LHS, const ClangTidyError &RHS) const {
742     LessClangTidyError Less;
743     return !Less(LHS, RHS) && !Less(RHS, LHS);
744   }
745 };
746 } // end anonymous namespace
747 
748 std::vector<ClangTidyError> ClangTidyDiagnosticConsumer::take() {
749   finalizeLastError();
750 
751   llvm::stable_sort(Errors, LessClangTidyError());
752   Errors.erase(std::unique(Errors.begin(), Errors.end(), EqualClangTidyError()),
753                Errors.end());
754   if (RemoveIncompatibleErrors)
755     removeIncompatibleErrors();
756   return std::move(Errors);
757 }
758 
759 namespace {
760 struct LessClangTidyErrorWithoutDiagnosticName {
761   bool operator()(const ClangTidyError *LHS, const ClangTidyError *RHS) const {
762     const tooling::DiagnosticMessage &M1 = LHS->Message;
763     const tooling::DiagnosticMessage &M2 = RHS->Message;
764 
765     return std::tie(M1.FilePath, M1.FileOffset, M1.Message) <
766            std::tie(M2.FilePath, M2.FileOffset, M2.Message);
767   }
768 };
769 } // end anonymous namespace
770 
771 void ClangTidyDiagnosticConsumer::removeDuplicatedDiagnosticsOfAliasCheckers() {
772   using UniqueErrorSet =
773       std::set<ClangTidyError *, LessClangTidyErrorWithoutDiagnosticName>;
774   UniqueErrorSet UniqueErrors;
775 
776   auto IT = Errors.begin();
777   while (IT != Errors.end()) {
778     ClangTidyError &Error = *IT;
779     std::pair<UniqueErrorSet::iterator, bool> Inserted =
780         UniqueErrors.insert(&Error);
781 
782     // Unique error, we keep it and move along.
783     if (Inserted.second) {
784       ++IT;
785     } else {
786       ClangTidyError &ExistingError = **Inserted.first;
787       const llvm::StringMap<tooling::Replacements> &CandidateFix =
788           Error.Message.Fix;
789       const llvm::StringMap<tooling::Replacements> &ExistingFix =
790           (*Inserted.first)->Message.Fix;
791 
792       if (CandidateFix != ExistingFix) {
793 
794         // In case of a conflict, don't suggest any fix-it.
795         ExistingError.Message.Fix.clear();
796         ExistingError.Notes.emplace_back(
797             llvm::formatv("cannot apply fix-it because an alias checker has "
798                           "suggested a different fix-it; please remove one of "
799                           "the checkers ('{0}', '{1}') or "
800                           "ensure they are both configured the same",
801                           ExistingError.DiagnosticName, Error.DiagnosticName)
802                 .str());
803       }
804 
805       if (Error.IsWarningAsError)
806         ExistingError.IsWarningAsError = true;
807 
808       // Since it is the same error, we should take it as alias and remove it.
809       ExistingError.EnabledDiagnosticAliases.emplace_back(Error.DiagnosticName);
810       IT = Errors.erase(IT);
811     }
812   }
813 }
814