20 #include "clang/AST/ASTDiagnostic.h" 21 #include "clang/Basic/Diagnostic.h" 22 #include "clang/Basic/DiagnosticOptions.h" 23 #include "clang/Frontend/DiagnosticRenderer.h" 24 #include "clang/Tooling/Core/Diagnostic.h" 25 #include "llvm/ADT/STLExtras.h" 26 #include "llvm/ADT/SmallString.h" 29 using namespace clang;
33 class ClangTidyDiagnosticRenderer :
public DiagnosticRenderer {
35 ClangTidyDiagnosticRenderer(
const LangOptions &LangOpts,
36 DiagnosticOptions *DiagOpts,
38 : DiagnosticRenderer(LangOpts, DiagOpts), Error(Error) {}
41 void emitDiagnosticMessage(FullSourceLoc
Loc, PresumedLoc PLoc,
42 DiagnosticsEngine::Level Level, StringRef
Message,
43 ArrayRef<CharSourceRange> Ranges,
44 DiagOrStoredDiag Info)
override {
49 std::string CheckNameInMessage =
" [" +
Error.DiagnosticName +
"]";
50 if (Message.endswith(CheckNameInMessage))
51 Message = Message.substr(0, Message.size() - CheckNameInMessage.size());
55 ? tooling::DiagnosticMessage(Message, Loc.getManager(),
Loc)
56 : tooling::DiagnosticMessage(Message);
57 if (Level == DiagnosticsEngine::Note) {
58 Error.Notes.push_back(TidyMessage);
61 assert(
Error.Message.Message.empty() &&
"Overwriting a diagnostic message");
62 Error.Message = TidyMessage;
65 void emitDiagnosticLoc(FullSourceLoc Loc, PresumedLoc PLoc,
66 DiagnosticsEngine::Level Level,
67 ArrayRef<CharSourceRange> Ranges)
override {}
69 void emitCodeContext(FullSourceLoc Loc, DiagnosticsEngine::Level Level,
70 SmallVectorImpl<CharSourceRange> &Ranges,
71 ArrayRef<FixItHint>
Hints)
override {
72 assert(Loc.isValid());
73 tooling::DiagnosticMessage *DiagWithFix =
74 Level == DiagnosticsEngine::Note ? &
Error.Notes.back() : &
Error.Message;
76 for (
const auto &
FixIt : Hints) {
78 assert(Range.getBegin().isValid() && Range.getEnd().isValid() &&
79 "Invalid range in the fix-it hint.");
80 assert(Range.getBegin().isFileID() && Range.getEnd().isFileID() &&
81 "Only file locations supported in fix-it hints.");
83 tooling::Replacement Replacement(Loc.getManager(),
Range,
86 DiagWithFix->Fix[Replacement.getFilePath()].add(Replacement);
90 llvm::errs() <<
"Fix conflicts with existing fix! " 92 assert(
false &&
"Fix conflicts with existing fix!");
97 void emitIncludeLocation(FullSourceLoc Loc, PresumedLoc PLoc)
override {}
99 void emitImportLocation(FullSourceLoc Loc, PresumedLoc PLoc,
100 StringRef ModuleName)
override {}
102 void emitBuildingModuleLocation(FullSourceLoc Loc, PresumedLoc PLoc,
103 StringRef ModuleName)
override {}
105 void endDiagnostic(DiagOrStoredDiag
D,
106 DiagnosticsEngine::Level Level)
override {
107 assert(!
Error.Message.Message.empty() &&
"Message has not been set");
116 ClangTidyError::Level DiagLevel,
117 StringRef BuildDirectory,
bool IsWarningAsError)
118 : tooling::Diagnostic(CheckName, DiagLevel, BuildDirectory),
119 IsWarningAsError(IsWarningAsError) {}
124 GlobList = GlobList.trim(
" \r\n");
125 if (GlobList.startswith(
"-")) {
126 GlobList = GlobList.substr(1);
134 StringRef UntrimmedGlob = GlobList.substr(0, GlobList.find(
','));
135 StringRef Glob = UntrimmedGlob.trim(
' ');
136 GlobList = GlobList.substr(UntrimmedGlob.size() + 1);
137 SmallString<128> RegexText(
"^");
138 StringRef MetaChars(
"()^$|*+?.[]\\{}");
139 for (
char C : Glob) {
141 RegexText.push_back(
'.');
142 else if (MetaChars.find(C) != StringRef::npos)
143 RegexText.push_back(
'\\');
144 RegexText.push_back(C);
146 RegexText.push_back(
'$');
147 return llvm::Regex(RegexText);
152 NextGlob(Globs.empty() ? nullptr : new
GlobList(Globs)) {}
159 Contains = NextGlob->contains(S, Contains);
168 switch (
auto &
Result = Cache[S]) {
174 Result = Globs.contains(S) ? Yes : No;
177 llvm_unreachable(
"invalid enum");
182 enum Tristate { None, Yes, No };
183 llvm::StringMap<Tristate> Cache;
187 std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider,
189 : DiagEngine(nullptr), OptionsProvider(std::move(OptionsProvider)),
191 AllowEnablingAnalyzerAlphaCheckers(AllowEnablingAnalyzerAlphaCheckers) {
200 StringRef CheckName, SourceLocation Loc, StringRef
Description,
201 DiagnosticIDs::Level Level ) {
202 assert(Loc.isValid());
203 unsigned ID = DiagEngine->getDiagnosticIDs()->getCustomDiagID(
204 Level, (Description +
" [" + CheckName +
"]").str());
205 CheckNamesByDiagnosticID.try_emplace(ID, CheckName);
206 return DiagEngine->Report(Loc, ID);
210 DiagEngine->setSourceManager(SourceMgr);
217 WarningAsErrorFilter =
222 DiagEngine->SetArgToStringFn(&FormatASTNodeDiagnosticArgument, Context);
223 LangOpts = Context->getLangOpts();
227 return OptionsProvider->getGlobalOptions();
231 return CurrentOptions;
238 OptionsProvider->getOptions(File));
244 ProfilePrefix = Prefix;
247 llvm::Optional<ClangTidyProfiling::StorageParams>
249 if (ProfilePrefix.empty())
256 assert(CheckFilter !=
nullptr);
257 return CheckFilter->contains(CheckName);
261 assert(WarningAsErrorFilter !=
nullptr);
262 return WarningAsErrorFilter->contains(CheckName);
266 std::string ClangWarningOption =
267 DiagEngine->getDiagnosticIDs()->getWarningOptionForDiag(DiagnosticID);
268 if (!ClangWarningOption.empty())
269 return "clang-diagnostic-" + ClangWarningOption;
270 llvm::DenseMap<unsigned, std::string>::const_iterator I =
271 CheckNamesByDiagnosticID.find(DiagnosticID);
272 if (I != CheckNamesByDiagnosticID.end())
279 bool RemoveIncompatibleErrors)
280 : Context(Ctx), ExternalDiagEngine(ExternalDiagEngine),
281 RemoveIncompatibleErrors(RemoveIncompatibleErrors),
282 LastErrorRelatesToUserCode(false), LastErrorPassesLineFilter(false),
283 LastErrorWasIgnored(false) {}
285 void ClangTidyDiagnosticConsumer::finalizeLastError() {
286 if (!Errors.empty()) {
289 Error.DiagLevel != ClangTidyError::Error) {
292 }
else if (!LastErrorRelatesToUserCode) {
295 }
else if (!LastErrorPassesLineFilter) {
302 LastErrorRelatesToUserCode =
false;
303 LastErrorPassesLineFilter =
false;
308 const size_t NolintIndex = Line.find(NolintDirectiveText);
309 if (NolintIndex == StringRef::npos)
312 size_t BracketIndex = NolintIndex + NolintDirectiveText.size();
314 if (BracketIndex < Line.size() && Line[BracketIndex] ==
'(') {
316 const size_t BracketEndIndex = Line.find(
')', BracketIndex);
317 if (BracketEndIndex != StringRef::npos) {
318 StringRef ChecksStr =
319 Line.substr(BracketIndex, BracketEndIndex - BracketIndex);
321 if (ChecksStr !=
"*") {
324 SmallVector<StringRef, 1>
Checks;
325 ChecksStr.split(Checks,
',', -1,
false);
326 llvm::transform(Checks, Checks.begin(),
327 [](StringRef S) {
return S.trim(); });
328 return llvm::find(Checks, CheckName) != Checks.end();
339 const char *CharacterData = SM.getCharacterData(Loc, &Invalid);
344 const char *P = CharacterData;
345 while (*P !=
'\0' && *P !=
'\r' && *P !=
'\n')
347 StringRef RestOfLine(CharacterData, P - CharacterData + 1);
352 const char *BufBegin =
353 SM.getCharacterData(SM.getLocForStartOfFile(SM.getFileID(Loc)), &Invalid);
354 if (Invalid || P == BufBegin)
359 while (P != BufBegin && *P !=
'\n')
368 const char *LineEnd = P;
371 while (P != BufBegin && *P !=
'\n')
374 RestOfLine = StringRef(P, LineEnd - P + 1);
375 if (
IsNOLINTFound(
"NOLINTNEXTLINE", RestOfLine, DiagID, Context))
382 SourceLocation Loc,
unsigned DiagID,
387 if (!Loc.isMacroID())
389 Loc = SM.getImmediateExpansionRange(Loc).getBegin();
399 bool CheckMacroExpansion) {
400 return Info.getLocation().isValid() &&
401 DiagLevel != DiagnosticsEngine::Error &&
402 DiagLevel != DiagnosticsEngine::Fatal &&
406 Info.getID(), Context);
413 DiagnosticsEngine::Level DiagLevel,
const Diagnostic &Info) {
414 if (LastErrorWasIgnored && DiagLevel == DiagnosticsEngine::Note)
418 ++Context.Stats.ErrorsIgnoredNOLINT;
420 LastErrorWasIgnored =
true;
424 LastErrorWasIgnored =
false;
426 DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info);
428 if (DiagLevel == DiagnosticsEngine::Note) {
429 assert(!Errors.empty() &&
430 "A diagnostic note can only be appended to a message.");
433 std::string CheckName = Context.getCheckName(Info.getID());
434 if (CheckName.empty()) {
438 case DiagnosticsEngine::Error:
439 case DiagnosticsEngine::Fatal:
440 CheckName =
"clang-diagnostic-error";
442 case DiagnosticsEngine::Warning:
443 CheckName =
"clang-diagnostic-warning";
446 CheckName =
"clang-diagnostic-unknown";
451 ClangTidyError::Level Level = ClangTidyError::Warning;
452 if (DiagLevel == DiagnosticsEngine::Error ||
453 DiagLevel == DiagnosticsEngine::Fatal) {
456 Level = ClangTidyError::Error;
457 LastErrorRelatesToUserCode =
true;
458 LastErrorPassesLineFilter =
true;
460 bool IsWarningAsError = DiagLevel == DiagnosticsEngine::Warning &&
461 Context.treatAsError(CheckName);
462 Errors.emplace_back(CheckName, Level, Context.getCurrentBuildDirectory(),
466 if (ExternalDiagEngine) {
469 forwardDiagnostic(Info);
471 ClangTidyDiagnosticRenderer Converter(
472 Context.getLangOpts(), &Context.DiagEngine->getDiagnosticOptions(),
475 Info.FormatDiagnostic(Message);
477 if (Info.getLocation().isValid() && Info.hasSourceManager())
478 Loc = FullSourceLoc(Info.getLocation(), Info.getSourceManager());
479 Converter.emitDiagnostic(Loc, DiagLevel, Message, Info.getRanges(),
480 Info.getFixItHints());
483 if (Info.hasSourceManager())
484 checkFilters(Info.getLocation(), Info.getSourceManager());
487 bool ClangTidyDiagnosticConsumer::passesLineFilter(StringRef
FileName,
488 unsigned LineNumber)
const {
489 if (Context.getGlobalOptions().LineFilter.empty())
491 for (
const FileFilter &Filter : Context.getGlobalOptions().LineFilter) {
492 if (FileName.endswith(Filter.Name)) {
493 if (Filter.LineRanges.empty())
496 if (Range.first <= LineNumber && LineNumber <= Range.second)
505 void ClangTidyDiagnosticConsumer::forwardDiagnostic(
const Diagnostic &Info) {
507 auto DiagLevelAndFormatString =
508 Context.getDiagLevelAndFormatString(Info.getID(), Info.getLocation());
509 unsigned ExternalID = ExternalDiagEngine->getDiagnosticIDs()->getCustomDiagID(
510 DiagLevelAndFormatString.first, DiagLevelAndFormatString.second);
513 auto builder = ExternalDiagEngine->Report(Info.getLocation(), ExternalID);
514 for (
auto Hint : Info.getFixItHints())
516 for (
auto Range : Info.getRanges())
519 DiagnosticsEngine::ArgumentKind kind = Info.getArgKind(
Index);
521 case clang::DiagnosticsEngine::ak_std_string:
522 builder << Info.getArgStdStr(
Index);
524 case clang::DiagnosticsEngine::ak_c_string:
525 builder << Info.getArgCStr(
Index);
527 case clang::DiagnosticsEngine::ak_sint:
528 builder << Info.getArgSInt(
Index);
530 case clang::DiagnosticsEngine::ak_uint:
531 builder << Info.getArgUInt(
Index);
533 case clang::DiagnosticsEngine::ak_tokenkind:
534 builder << static_cast<tok::TokenKind>(Info.getRawArg(
Index));
536 case clang::DiagnosticsEngine::ak_identifierinfo:
537 builder << Info.getArgIdentifier(
Index);
539 case clang::DiagnosticsEngine::ak_qual:
540 builder << Qualifiers::fromOpaqueValue(Info.getRawArg(
Index));
542 case clang::DiagnosticsEngine::ak_qualtype:
543 builder << QualType::getFromOpaquePtr((
void *)Info.getRawArg(
Index));
545 case clang::DiagnosticsEngine::ak_declarationname:
546 builder << DeclarationName::getFromOpaqueInteger(Info.getRawArg(
Index));
548 case clang::DiagnosticsEngine::ak_nameddecl:
549 builder << reinterpret_cast<const NamedDecl *>(Info.getRawArg(
Index));
551 case clang::DiagnosticsEngine::ak_nestednamespec:
552 builder << reinterpret_cast<NestedNameSpecifier *>(Info.getRawArg(
Index));
554 case clang::DiagnosticsEngine::ak_declcontext:
555 builder << reinterpret_cast<DeclContext *>(Info.getRawArg(
Index));
557 case clang::DiagnosticsEngine::ak_qualtype_pair:
560 case clang::DiagnosticsEngine::ak_attr:
561 builder << reinterpret_cast<Attr *>(Info.getRawArg(
Index));
567 void ClangTidyDiagnosticConsumer::checkFilters(SourceLocation
Location,
568 const SourceManager &Sources) {
570 if (!Location.isValid()) {
571 LastErrorRelatesToUserCode =
true;
572 LastErrorPassesLineFilter =
true;
576 if (!*Context.getOptions().SystemHeaders &&
577 Sources.isInSystemHeader(Location))
583 FileID FID = Sources.getDecomposedExpansionLoc(Location).first;
584 const FileEntry *File = Sources.getFileEntryForID(FID);
589 LastErrorRelatesToUserCode =
true;
590 LastErrorPassesLineFilter =
true;
594 StringRef
FileName(File->getName());
595 LastErrorRelatesToUserCode = LastErrorRelatesToUserCode ||
596 Sources.isInMainFile(Location) ||
597 getHeaderFilter()->match(FileName);
599 unsigned LineNumber = Sources.getExpansionLineNumber(Location);
600 LastErrorPassesLineFilter =
601 LastErrorPassesLineFilter || passesLineFilter(FileName, LineNumber);
604 llvm::Regex *ClangTidyDiagnosticConsumer::getHeaderFilter() {
607 llvm::make_unique<llvm::Regex>(*Context.getOptions().HeaderFilterRegex);
608 return HeaderFilter.get();
611 void ClangTidyDiagnosticConsumer::removeIncompatibleErrors() {
627 Event(
unsigned Begin,
unsigned End, EventType
Type,
unsigned ErrorId,
629 :
Type(Type), ErrorId(ErrorId) {
655 if (Type == ET_Begin)
656 Priority = std::make_tuple(Begin, Type, -End, -ErrorSize, ErrorId);
658 Priority = std::make_tuple(End, Type, -Begin, ErrorSize, ErrorId);
661 bool operator<(
const Event &Other)
const {
662 return Priority < Other.Priority;
671 std::tuple<unsigned, EventType, int, int, unsigned> Priority;
675 std::vector<int> Sizes;
677 std::pair<ClangTidyError *, llvm::StringMap<tooling::Replacements> *>>
679 for (
auto &Error : Errors) {
680 if (
const auto *
Fix = tooling::selectFirstFix(Error))
681 ErrorFixes.emplace_back(
682 &Error,
const_cast<llvm::StringMap<tooling::Replacements> *
>(
Fix));
684 for (
const auto &ErrorAndFix : ErrorFixes) {
686 for (
const auto &FileAndReplaces : *ErrorAndFix.second) {
687 for (
const auto &Replace : FileAndReplaces.second)
688 Size += Replace.getLength();
690 Sizes.push_back(Size);
694 std::map<std::string, std::vector<Event>> FileEvents;
695 for (
unsigned I = 0; I < ErrorFixes.size(); ++I) {
696 for (
const auto &FileAndReplace : *ErrorFixes[I].second) {
697 for (
const auto &Replace : FileAndReplace.second) {
698 unsigned Begin = Replace.getOffset();
699 unsigned End = Begin + Replace.getLength();
700 const std::string &FilePath = Replace.getFilePath();
704 auto &Events = FileEvents[FilePath];
705 Events.emplace_back(Begin, End, Event::ET_Begin, I, Sizes[I]);
706 Events.emplace_back(Begin, End, Event::ET_End, I, Sizes[I]);
711 std::vector<bool> Apply(ErrorFixes.size(),
true);
712 for (
auto &FileAndEvents : FileEvents) {
713 std::vector<Event> &Events = FileAndEvents.second;
715 std::sort(Events.begin(), Events.end());
716 int OpenIntervals = 0;
717 for (
const auto &Event : Events) {
718 if (Event.Type == Event::ET_End)
722 if (OpenIntervals != 0)
723 Apply[Event.ErrorId] =
false;
724 if (Event.Type == Event::ET_Begin)
727 assert(OpenIntervals == 0 &&
"Amount of begin/end points doesn't match");
730 for (
unsigned I = 0; I < ErrorFixes.size(); ++I) {
732 ErrorFixes[I].second->clear();
733 ErrorFixes[I].first->Notes.emplace_back(
734 "this fix will not be applied because it overlaps with another fix");
740 struct LessClangTidyError {
742 const tooling::DiagnosticMessage &M1 = LHS.Message;
743 const tooling::DiagnosticMessage &M2 = RHS.Message;
745 return std::tie(M1.FilePath, M1.FileOffset, M1.Message) <
746 std::tie(M2.FilePath, M2.FileOffset, M2.Message);
749 struct EqualClangTidyError {
751 LessClangTidyError Less;
752 return !Less(LHS, RHS) && !Less(RHS, LHS);
760 std::sort(Errors.begin(), Errors.end(), LessClangTidyError());
761 Errors.erase(std::unique(Errors.begin(), Errors.end(), EqualClangTidyError()),
763 if (RemoveIncompatibleErrors)
764 removeIncompatibleErrors();
765 return std::move(Errors);
llvm::Optional< std::string > Checks
Checks filter.
SourceLocation Loc
'#' location in the include directive
ClangTidyOptions mergeWith(const ClangTidyOptions &Other) const
Creates a new ClangTidyOptions instance combined from all fields of this instance overridden by the f...
bool ShouldSuppressDiagnostic(DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info, ClangTidyContext &Context, bool CheckMacroExpansion)
Check whether a given diagnostic should be suppressed due to the presence of a "NOLINT" suppression c...
Read-only set of strings represented as a list of positive and negative globs.
GlobList(StringRef Globs)
GlobList is a comma-separated list of globs (only '*' metacharacter is supported) with optional '-' p...
const ClangTidyGlobalOptions & getGlobalOptions() const
Returns global options.
static bool LineIsMarkedWithNOLINTinMacro(const SourceManager &SM, SourceLocation Loc, unsigned DiagID, const ClangTidyContext &Context)
bool isCheckEnabled(StringRef CheckName) const
Returns true if the check is enabled for the CurrentFile.
bool contains(StringRef S)
Returns true if the pattern matches S.
constexpr llvm::StringLiteral Message
static llvm::StringRef toString(SpecialMemberFunctionsCheck::SpecialMemberFunctionKind K)
Contains options for clang-tidy.
unsigned ErrorsIgnoredCheckFilter
static bool ConsumeNegativeIndicator(StringRef &GlobList)
bool contains(StringRef S)
std::pair< unsigned, unsigned > LineRange
LineRange is a pair<start, end> (inclusive).
void setCurrentFile(StringRef File)
Should be called when starting to process new translation unit.
static cl::opt< bool > AllowEnablingAnalyzerAlphaCheckers("allow-enabling-analyzer-alpha-checkers", cl::init(false), cl::Hidden, cl::cat(ClangTidyCategory))
This option allows enabling the experimental alpha checkers from the static analyzer.
static llvm::Regex ConsumeGlob(StringRef &GlobList)
DiagnosticBuilder diag(StringRef CheckName, SourceLocation Loc, StringRef Message, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Report any errors detected using this method.
unsigned ErrorsIgnoredNonUserCode
std::vector< ClangTidyError > take()
bool operator<(const Ref &L, const Ref &R)
llvm::Optional< ClangTidyProfiling::StorageParams > getProfileStorageParams() const
ClangTidyDiagnosticConsumer(ClangTidyContext &Ctx, DiagnosticsEngine *ExternalDiagEngine=nullptr, bool RemoveIncompatibleErrors=true)
ClangTidyError(StringRef CheckName, Level DiagLevel, StringRef BuildDirectory, bool IsWarningAsError)
ClangTidyOptions getOptionsForFile(StringRef File) const
Returns options for File.
const ClangTidyOptions & getOptions() const
Returns options for CurrentFile.
void setASTContext(ASTContext *Context)
Sets ASTContext for the current translation unit.
void setProfileStoragePrefix(StringRef ProfilePrefix)
Control storage of profile date.
unsigned ErrorsIgnoredLineFilter
llvm::Optional< std::string > WarningsAsErrors
WarningsAsErrors filter.
void setSourceManager(SourceManager *SourceMgr)
Sets the SourceManager of the used DiagnosticsEngine.
CachedGlobList(StringRef Globs)
Contains a list of line ranges in a single file.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
ClangTidyContext(std::unique_ptr< ClangTidyOptionsProvider > OptionsProvider, bool AllowEnablingAnalyzerAlphaCheckers=false)
Initializes ClangTidyContext instance.
static bool IsNOLINTFound(StringRef NolintDirectiveText, StringRef Line, unsigned DiagID, const ClangTidyContext &Context)
std::vector< FixItHint > Hints
CharSourceRange Range
SourceRange for the file name.
A detected error complete with information to display diagnostic and automatic fix.
std::string getCheckName(unsigned DiagnosticID) const
Returns the name of the clang-tidy check which produced this diagnostic ID.
static cl::opt< std::string > Checks("checks", cl::desc(R"(
Comma-separated list of globs with optional '-'
prefix. Globs are processed in order of
appearance in the list. Globs without '-'
prefix add checks with matching names to the
set, globs with the '-' prefix remove checks
with matching names from the set of enabled
checks. This option's value is appended to the
value of the 'Checks' option in .clang-tidy
file, if any.
)"), cl::init(""), cl::cat(ClangTidyCategory))
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
llvm::Optional< FixItHint > FixIt
static ClangTidyOptions getDefaults()
These options are used for all settings that haven't been overridden by the OptionsProvider.
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))
void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) override
bool treatAsError(StringRef CheckName) const
Returns true if the check should be upgraded to error for the CurrentFile.
void setEnableProfiling(bool Profile)
Control profile collection in clang-tidy.
static bool LineIsMarkedWithNOLINT(const SourceManager &SM, SourceLocation Loc, unsigned DiagID, const ClangTidyContext &Context)
const SymbolIndex * Index