22#include "clang/AST/ASTContext.h"
23#include "clang/AST/ASTDiagnostic.h"
24#include "clang/AST/Attr.h"
25#include "clang/AST/Expr.h"
26#include "clang/Basic/CharInfo.h"
27#include "clang/Basic/Diagnostic.h"
28#include "clang/Basic/DiagnosticOptions.h"
29#include "clang/Basic/FileManager.h"
30#include "clang/Basic/SourceManager.h"
31#include "clang/Frontend/DiagnosticRenderer.h"
32#include "clang/Lex/Lexer.h"
33#include "clang/Tooling/Core/Diagnostic.h"
34#include "clang/Tooling/Core/Replacement.h"
35#include "llvm/ADT/BitVector.h"
36#include "llvm/ADT/STLExtras.h"
37#include "llvm/ADT/StringMap.h"
38#include "llvm/Support/FormatVariadic.h"
39#include "llvm/Support/Regex.h"
48class ClangTidyDiagnosticRenderer :
public DiagnosticRenderer {
50 ClangTidyDiagnosticRenderer(
const LangOptions &LangOpts,
51 DiagnosticOptions &DiagOpts,
52 ClangTidyError &Error)
53 : DiagnosticRenderer(LangOpts, DiagOpts), Error(Error) {}
56 void emitDiagnosticMessage(FullSourceLoc Loc, PresumedLoc PLoc,
57 DiagnosticsEngine::Level Level, StringRef Message,
58 ArrayRef<CharSourceRange> Ranges,
59 DiagOrStoredDiag Info)
override {
64 const std::string CheckNameInMessage =
" [" + Error.DiagnosticName +
"]";
65 Message.consume_back(CheckNameInMessage);
67 const auto TidyMessage =
69 ? tooling::DiagnosticMessage(Message, Loc.getManager(), Loc)
70 : tooling::DiagnosticMessage(Message);
76 const auto ToCharRange = [
this, &Loc](
const CharSourceRange &SourceRange) {
77 if (SourceRange.isCharRange())
79 assert(SourceRange.isTokenRange());
80 const SourceLocation End = Lexer::getLocForEndOfToken(
81 SourceRange.getEnd(), 0, Loc.getManager(), LangOpts);
82 return CharSourceRange::getCharRange(SourceRange.getBegin(), End);
86 const auto ValidRanges =
87 llvm::make_filter_range(Ranges, [](
const CharSourceRange &R) {
88 return R.getAsRange().isValid();
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));
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));
105 void emitDiagnosticLoc(FullSourceLoc Loc, PresumedLoc PLoc,
106 DiagnosticsEngine::Level Level,
107 ArrayRef<CharSourceRange> Ranges)
override {}
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;
116 for (
const auto &FixIt : Hints) {
117 const 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.");
123 const tooling::Replacement Replacement(Loc.getManager(), Range,
126 DiagWithFix->Fix[Replacement.getFilePath()].add(Replacement);
130 llvm::errs() <<
"Fix conflicts with existing fix! "
131 << llvm::toString(std::move(Err)) <<
"\n";
132 assert(
false &&
"Fix conflicts with existing fix!");
137 void emitIncludeLocation(FullSourceLoc Loc, PresumedLoc PLoc)
override {}
139 void emitImportLocation(FullSourceLoc Loc, PresumedLoc PLoc,
140 StringRef ModuleName)
override {}
142 void emitBuildingModuleLocation(FullSourceLoc Loc, PresumedLoc PLoc,
143 StringRef ModuleName)
override {}
145 void endDiagnostic(DiagOrStoredDiag D,
146 DiagnosticsEngine::Level Level)
override {
147 assert(!Error.Message.Message.empty() &&
"Message has not been set");
151 ClangTidyError &Error;
156 ClangTidyError::Level DiagLevel,
158 :
tooling::Diagnostic(CheckName, DiagLevel, BuildDirectory),
162 std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider,
163 bool AllowEnablingAnalyzerAlphaCheckers,
bool EnableModuleHeadersParsing,
164 bool ExperimentalCustomChecks)
165 : OptionsProvider(std::
move(OptionsProvider)),
166 AllowEnablingAnalyzerAlphaCheckers(AllowEnablingAnalyzerAlphaCheckers),
167 EnableModuleHeadersParsing(EnableModuleHeadersParsing),
168 ExperimentalCustomChecks(ExperimentalCustomChecks) {
177 StringRef CheckName, SourceLocation Loc, StringRef Description,
178 DiagnosticIDs::Level Level ) {
179 assert(Loc.isValid());
180 const unsigned ID = DiagEngine->getDiagnosticIDs()->getCustomDiagID(
181 Level, (Description +
" [" + CheckName +
"]").str());
182 CheckNamesByDiagnosticID.try_emplace(ID, CheckName);
183 return DiagEngine->Report(Loc, ID);
187 StringRef CheckName, StringRef Description,
188 DiagnosticIDs::Level Level ) {
189 const unsigned ID = DiagEngine->getDiagnosticIDs()->getCustomDiagID(
190 Level, (Description +
" [" + CheckName +
"]").str());
191 CheckNamesByDiagnosticID.try_emplace(ID, CheckName);
192 return DiagEngine->Report(ID);
196 SourceManager &SM = DiagEngine->getSourceManager();
197 FileManager &FM = SM.getFileManager();
198 const FileEntryRef File =
199 llvm::cantFail(FM.getFileRef(Error.Message.FilePath));
200 const FileID ID = SM.getOrCreateFileID(File, SrcMgr::C_User);
201 const SourceLocation FileStartLoc = SM.getLocForStartOfFile(ID);
202 const SourceLocation Loc = FileStartLoc.getLocWithOffset(
203 static_cast<SourceLocation::IntTy
>(Error.Message.FileOffset));
204 return diag(Error.DiagnosticName, Loc, Error.Message.Message,
205 static_cast<DiagnosticIDs::Level
>(Error.DiagLevel));
210 DiagnosticIDs::Level Level ) {
211 return diag(
"clang-tidy-config", Message, Level);
215 DiagnosticsEngine::Level DiagLevel,
const Diagnostic &Info,
216 SmallVectorImpl<tooling::Diagnostic> &NoLintErrors,
bool AllowIO,
217 bool EnableNoLintBlocks) {
218 const std::string CheckName =
getCheckName(Info.getID());
219 return NoLintHandler.shouldSuppress(DiagLevel, Info, CheckName, NoLintErrors,
220 AllowIO, EnableNoLintBlocks);
224 DiagEngine->setSourceManager(SourceMgr);
229 FileExtensions.clear();
230 for (
const StringRef Suffix : AllFileExtensions) {
231 StringRef Extension = Suffix.trim();
232 if (!llvm::all_of(Extension, isAlphanumeric))
234 FileExtensions.insert(Extension);
240 CurrentFile = std::string(File);
242 CheckFilter = std::make_unique<CachedGlobList>(
244 WarningAsErrorFilter = std::make_unique<CachedGlobList>(
246 static const std::vector<std::string> EmptyFileExtensions;
249 : EmptyFileExtensions,
250 HeaderFileExtensions))
254 : EmptyFileExtensions,
255 ImplementationFileExtensions))
260 DiagEngine->SetArgToStringFn(&FormatASTNodeDiagnosticArgument, Context);
261 LangOpts = Context->getLangOpts();
265 return OptionsProvider->getGlobalOptions();
269 return CurrentOptions;
276 OptionsProvider->getOptions(File), 0);
282 ProfilePrefix = std::string(Prefix);
285std::optional<ClangTidyProfiling::StorageParams>
287 if (ProfilePrefix.empty())
294 assert(CheckFilter !=
nullptr);
295 return CheckFilter->contains(CheckName);
299 assert(WarningAsErrorFilter !=
nullptr);
300 return WarningAsErrorFilter->contains(CheckName);
304 const std::string ClangWarningOption = std::string(
305 DiagEngine->getDiagnosticIDs()->getWarningOptionForDiag(DiagnosticID));
306 if (!ClangWarningOption.empty())
307 return "clang-diagnostic-" + ClangWarningOption;
308 const llvm::DenseMap<unsigned, std::string>::const_iterator I =
309 CheckNamesByDiagnosticID.find(DiagnosticID);
310 if (I != CheckNamesByDiagnosticID.end())
316 return !CheckNamesByDiagnosticID.contains(DiagnosticID);
321 bool RemoveIncompatibleErrors,
bool GetFixesFromNotes,
322 bool EnableNolintBlocks)
323 : Context(Ctx), ExternalDiagEngine(ExternalDiagEngine),
324 RemoveIncompatibleErrors(RemoveIncompatibleErrors),
325 GetFixesFromNotes(GetFixesFromNotes),
326 EnableNolintBlocks(EnableNolintBlocks) {}
328void ClangTidyDiagnosticConsumer::finalizeLastError() {
329 if (!Errors.empty()) {
331 if (Error.DiagnosticName ==
"clang-tidy-config") {
334 Error.DiagLevel != ClangTidyError::Error) {
337 }
else if (!LastErrorRelatesToUserCode) {
340 }
else if (!LastErrorPassesLineFilter) {
341 ++Context.Stats.ErrorsIgnoredLineFilter;
344 ++Context.Stats.ErrorsDisplayed;
347 LastErrorRelatesToUserCode =
false;
348 LastErrorPassesLineFilter =
false;
351namespace clang::tidy {
353const llvm::StringMap<tooling::Replacements> *
354getFixIt(
const tooling::Diagnostic &Diagnostic,
bool AnyFix) {
355 if (!Diagnostic.Message.Fix.empty())
356 return &Diagnostic.Message.Fix;
359 const llvm::StringMap<tooling::Replacements> *Result =
nullptr;
360 for (
const auto &Note : Diagnostic.Notes) {
361 if (!Note.Fix.empty()) {
374 const Preprocessor *PP) {
375 DiagnosticConsumer::BeginSourceFile(LangOpts, PP);
377 assert(!InSourceFile);
382 assert(InSourceFile);
383 InSourceFile =
false;
385 DiagnosticConsumer::EndSourceFile();
389 DiagnosticsEngine::Level DiagLevel,
const Diagnostic &Info) {
392 assert(InSourceFile || Info.getLocation().isInvalid());
394 if (LastErrorWasIgnored && DiagLevel == DiagnosticsEngine::Note)
398 if (Context.shouldSuppressDiagnostic(DiagLevel, Info, SuppressionErrors,
399 EnableNolintBlocks)) {
400 ++Context.Stats.ErrorsIgnoredNOLINT;
402 LastErrorWasIgnored =
true;
403 for (
const auto &Error : SuppressionErrors)
408 LastErrorWasIgnored =
false;
410 DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info);
412 if (DiagLevel == DiagnosticsEngine::Note) {
413 assert(!Errors.empty() &&
414 "A diagnostic note can only be appended to a message.");
417 std::string CheckName = Context.getCheckName(Info.getID());
418 if (CheckName.empty()) {
422 case DiagnosticsEngine::Error:
423 case DiagnosticsEngine::Fatal:
424 CheckName =
"clang-diagnostic-error";
426 case DiagnosticsEngine::Warning:
427 CheckName =
"clang-diagnostic-warning";
429 case DiagnosticsEngine::Remark:
430 CheckName =
"clang-diagnostic-remark";
433 CheckName =
"clang-diagnostic-unknown";
438 ClangTidyError::Level Level = ClangTidyError::Warning;
439 if (DiagLevel == DiagnosticsEngine::Error ||
440 DiagLevel == DiagnosticsEngine::Fatal) {
443 Level = ClangTidyError::Error;
444 LastErrorRelatesToUserCode =
true;
445 LastErrorPassesLineFilter =
true;
446 }
else if (DiagLevel == DiagnosticsEngine::Remark) {
447 Level = ClangTidyError::Remark;
450 const bool IsWarningAsError = DiagLevel == DiagnosticsEngine::Warning &&
451 Context.treatAsError(CheckName);
452 Errors.emplace_back(CheckName, Level, Context.getCurrentBuildDirectory(),
456 if (ExternalDiagEngine) {
459 forwardDiagnostic(Info);
461 ClangTidyDiagnosticRenderer Converter(
462 Context.getLangOpts(), Context.DiagEngine->getDiagnosticOptions(),
464 SmallString<100> Message;
465 Info.FormatDiagnostic(Message);
467 if (Info.hasSourceManager())
468 Loc = FullSourceLoc(Info.getLocation(), Info.getSourceManager());
469 else if (Context.DiagEngine->hasSourceManager())
470 Loc = FullSourceLoc(Info.getLocation(),
471 Context.DiagEngine->getSourceManager());
472 Converter.emitDiagnostic(Loc, DiagLevel, Message, Info.getRanges(),
473 Info.getFixItHints());
476 if (Info.hasSourceManager())
477 checkFilters(Info.getLocation(), Info.getID(), Info.getSourceManager());
479 for (
const auto &Error : SuppressionErrors)
483bool ClangTidyDiagnosticConsumer::passesLineFilter(StringRef FileName,
484 unsigned LineNumber)
const {
488 if (FileName.ends_with(Filter.Name)) {
489 if (Filter.LineRanges.empty())
493 return Range.first <= LineNumber && LineNumber <= Range.second;
500void ClangTidyDiagnosticConsumer::forwardDiagnostic(
const Diagnostic &Info) {
502 const auto DiagLevelAndFormatString =
503 Context.getDiagLevelAndFormatString(
Info.getID(),
Info.getLocation());
504 const unsigned ExternalID =
505 ExternalDiagEngine->getDiagnosticIDs()->getCustomDiagID(
506 DiagLevelAndFormatString.first, DiagLevelAndFormatString.second);
510 ExternalDiagEngine->Report(
Info.getLocation(), ExternalID);
511 for (
const FixItHint &Hint :
Info.getFixItHints())
513 for (
const auto Range :
Info.getRanges())
515 for (
unsigned Index = 0; Index <
Info.getNumArgs(); ++Index) {
516 const DiagnosticsEngine::ArgumentKind Kind =
Info.getArgKind(Index);
518 case DiagnosticsEngine::ak_std_string:
519 Builder <<
Info.getArgStdStr(Index);
521 case DiagnosticsEngine::ak_c_string:
522 Builder <<
Info.getArgCStr(Index);
524 case DiagnosticsEngine::ak_sint:
525 Builder <<
Info.getArgSInt(Index);
527 case DiagnosticsEngine::ak_uint:
528 Builder <<
Info.getArgUInt(Index);
530 case DiagnosticsEngine::ak_tokenkind:
531 Builder << static_cast<tok::TokenKind>(
Info.getRawArg(Index));
533 case DiagnosticsEngine::ak_identifierinfo:
534 Builder <<
Info.getArgIdentifier(Index);
536 case DiagnosticsEngine::ak_qual:
537 Builder << Qualifiers::fromOpaqueValue(
Info.getRawArg(Index));
539 case DiagnosticsEngine::ak_qualtype:
540 Builder << QualType::getFromOpaquePtr(
541 reinterpret_cast<void *
>(
Info.getRawArg(Index)));
543 case DiagnosticsEngine::ak_declarationname:
544 Builder << DeclarationName::getFromOpaqueInteger(
Info.getRawArg(Index));
546 case DiagnosticsEngine::ak_nameddecl:
547 Builder << reinterpret_cast<const NamedDecl *>(
Info.getRawArg(Index));
549 case DiagnosticsEngine::ak_nestednamespec:
550 Builder << NestedNameSpecifier::getFromVoidPointer(
551 reinterpret_cast<void *
>(
Info.getRawArg(Index)));
553 case DiagnosticsEngine::ak_declcontext:
554 Builder << reinterpret_cast<DeclContext *>(
Info.getRawArg(Index));
556 case DiagnosticsEngine::ak_qualtype_pair:
559 case DiagnosticsEngine::ak_attr:
560 Builder << reinterpret_cast<Attr *>(
Info.getRawArg(Index));
562 case DiagnosticsEngine::ak_attr_info:
563 Builder << reinterpret_cast<AttributeCommonInfo *>(
Info.getRawArg(Index));
565 case DiagnosticsEngine::ak_addrspace:
566 Builder << static_cast<LangAS>(
Info.getRawArg(Index));
568 case DiagnosticsEngine::ak_expr:
569 Builder << reinterpret_cast<const Expr *>(
Info.getRawArg(Index));
574void ClangTidyDiagnosticConsumer::checkFilters(SourceLocation Location,
575 unsigned DiagnosticID,
576 const SourceManager &Sources) {
578 if (!Location.isValid()) {
579 LastErrorRelatesToUserCode =
true;
580 LastErrorPassesLineFilter =
true;
584 if (!Context.getOptions().SystemHeaders.value_or(
false)) {
585 if (Context.isCompilerDiagnostic(DiagnosticID)) {
586 if (Context.DiagEngine->getDiagnosticIDs()->shouldSuppressAsSystemWarning(
587 DiagnosticID, Location, *Context.DiagEngine))
590 if (Sources.isInSystemHeader(Location) ||
591 Sources.isInSystemMacro(Location))
599 const FileID FID = Sources.getDecomposedExpansionLoc(Location).first;
600 OptionalFileEntryRef
File = Sources.getFileEntryRefForID(FID);
605 LastErrorRelatesToUserCode =
true;
606 LastErrorPassesLineFilter =
true;
610 const StringRef FileName(
File->getName());
611 LastErrorRelatesToUserCode = LastErrorRelatesToUserCode ||
612 Sources.isInMainFile(Location) ||
613 (getHeaderFilter()->match(FileName) &&
614 !getExcludeHeaderFilter()->match(FileName));
616 const unsigned LineNumber = Sources.getExpansionLineNumber(Location);
617 LastErrorPassesLineFilter =
618 LastErrorPassesLineFilter || passesLineFilter(FileName, LineNumber);
621llvm::Regex *ClangTidyDiagnosticConsumer::getHeaderFilter() {
623 HeaderFilter = std::make_unique<llvm::Regex>(
624 Context.getOptions().HeaderFilterRegex.value_or(
""));
625 return HeaderFilter.get();
628llvm::Regex *ClangTidyDiagnosticConsumer::getExcludeHeaderFilter() {
629 if (!ExcludeHeaderFilter)
630 ExcludeHeaderFilter = std::make_unique<llvm::Regex>(
631 Context.getOptions().ExcludeHeaderFilterRegex.value_or(
""));
632 return ExcludeHeaderFilter.get();
635void ClangTidyDiagnosticConsumer::removeIncompatibleErrors() {
652 Event(
unsigned Begin,
unsigned End, EventType Type,
unsigned ErrorId,
682 Priority = {Begin,
Type, -End, -ErrorSize, ErrorId};
685 Priority = {Begin,
Type, -End, ErrorSize, ErrorId};
688 Priority = {End,
Type, -Begin, ErrorSize, ErrorId};
693 bool operator<(
const Event &Other)
const {
694 return Priority < Other.Priority;
703 std::tuple<unsigned, EventType, int, int, unsigned> Priority;
707 std::vector<int> Sizes;
709 std::pair<ClangTidyError *, llvm::StringMap<tooling::Replacements> *>>
711 for (
auto &Error : Errors)
712 if (
const auto *
Fix =
getFixIt(Error, GetFixesFromNotes))
713 ErrorFixes.emplace_back(
714 &Error,
const_cast<llvm::StringMap<tooling::Replacements> *
>(
Fix));
715 for (
const auto &ErrorAndFix : ErrorFixes) {
717 for (
const auto &FileAndReplaces : *ErrorAndFix.second)
718 for (
const auto &Replace : FileAndReplaces.second)
719 Size += Replace.getLength();
720 Sizes.push_back(Size);
724 llvm::StringMap<std::vector<Event>> FileEvents;
725 for (
unsigned I = 0; I < ErrorFixes.size(); ++I) {
726 for (
const auto &FileAndReplace : *ErrorFixes[I].second) {
727 for (
const auto &Replace : FileAndReplace.second) {
728 const unsigned Begin = Replace.getOffset();
729 const unsigned End = Begin + Replace.getLength();
730 auto &Events = FileEvents[Replace.getFilePath()];
732 Events.emplace_back(Begin, End, Event::ET_Insert, I, Sizes[I]);
734 Events.emplace_back(Begin, End, Event::ET_Begin, I, Sizes[I]);
735 Events.emplace_back(Begin, End, Event::ET_End, I, Sizes[I]);
741 llvm::BitVector Apply(ErrorFixes.size(),
true);
742 for (
auto &FileAndEvents : FileEvents) {
743 std::vector<Event> &Events = FileAndEvents.second;
746 int OpenIntervals = 0;
747 for (
const auto &Event : Events) {
748 switch (
Event.Type) {
749 case Event::ET_Begin:
750 if (OpenIntervals++ != 0)
751 Apply[
Event.ErrorId] =
false;
753 case Event::ET_Insert:
754 if (OpenIntervals != 0)
755 Apply[
Event.ErrorId] =
false;
758 if (--OpenIntervals != 0)
759 Apply[
Event.ErrorId] =
false;
763 assert(OpenIntervals == 0 &&
"Amount of begin/end points doesn't match");
766 for (
unsigned I = 0; I < ErrorFixes.size(); ++I) {
768 ErrorFixes[I].second->clear();
769 ErrorFixes[I].first->Notes.emplace_back(
770 "this fix will not be applied because it overlaps with another fix");
776struct LessClangTidyError {
777 bool operator()(
const ClangTidyError &LHS,
const ClangTidyError &RHS)
const {
778 const tooling::DiagnosticMessage &M1 = LHS.Message;
779 const tooling::DiagnosticMessage &M2 = RHS.Message;
784 return std::tie(M1.FilePath, M1.FileOffset, M1.Message,
785 LHS.DiagnosticName) <
786 std::tie(M2.FilePath, M2.FileOffset, M2.Message, RHS.DiagnosticName);
789struct EqualClangTidyError {
790 bool operator()(
const ClangTidyError &LHS,
const ClangTidyError &RHS)
const {
791 const LessClangTidyError Less;
792 return !Less(LHS, RHS) && !Less(RHS, LHS);
800 llvm::stable_sort(Errors, LessClangTidyError());
801 Errors.erase(llvm::unique(Errors, EqualClangTidyError()), Errors.end());
802 if (RemoveIncompatibleErrors) {
803 removeDuplicatedDiagnosticsOfAliasCheckers();
804 removeIncompatibleErrors();
806 return std::move(Errors);
809void ClangTidyDiagnosticConsumer::removeDuplicatedDiagnosticsOfAliasCheckers() {
810 if (Errors.size() <= 1)
813 static constexpr auto AreDuplicates = [](
const ClangTidyError &E1,
815 const tooling::DiagnosticMessage &M1 = E1.Message;
816 const tooling::DiagnosticMessage &M2 = E2.Message;
817 return std::tie(M1.FilePath, M1.FileOffset, M1.Message) ==
818 std::tie(M2.FilePath, M2.FileOffset, M2.Message);
821 auto LastUniqueErrorIt = Errors.begin();
825 if (!AreDuplicates(Error, ExistingError)) {
827 if (&*LastUniqueErrorIt != &Error)
828 *LastUniqueErrorIt = std::move(Error);
830 const llvm::StringMap<tooling::Replacements> &CandidateFix =
832 const llvm::StringMap<tooling::Replacements> &ExistingFix =
833 ExistingError.Message.Fix;
835 if (CandidateFix != ExistingFix) {
837 ExistingError.Message.Fix.clear();
838 ExistingError.Notes.emplace_back(
839 llvm::formatv(
"cannot apply fix-it because an alias checker has "
840 "suggested a different fix-it; please remove one of "
841 "the checkers ('{0}', '{1}') or "
842 "ensure they are both configured the same",
843 ExistingError.DiagnosticName, Error.DiagnosticName)
847 if (
Error.IsWarningAsError)
854 Errors.erase(std::next(LastUniqueErrorIt), Errors.end());
static bool parseFileExtensions(llvm::ArrayRef< std::string > AllFileExtensions, FileExtensionsSet &FileExtensions)
static cl::opt< bool > Fix("fix", 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))
static cl::opt< std::string > WarningsAsErrors("warnings-as-errors", desc(R"(
Upgrades warnings to errors. Same format as
'-checks'.
This option's value is appended to the value of
the 'WarningsAsErrors' option in .clang-tidy
file, if any.
)"), cl::init(""), cl::cat(ClangTidyCategory))
static cl::opt< std::string > Checks("checks", 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.
ClangTidyContext(std::unique_ptr< ClangTidyOptionsProvider > OptionsProvider)
bool isCheckEnabled(StringRef CheckName) const
Returns true if the check is enabled for the CurrentFile.
std::string getCheckName(unsigned DiagnosticID) const
Returns the name of the clang-tidy check which produced this diagnostic ID.
const ClangTidyOptions & getOptions() const
Returns options for CurrentFile.
bool isCompilerDiagnostic(unsigned DiagnosticID) const
Returns true if this clang-tidy check is in fact a compiler warning exposed as a 'clang-diagnostic-*'...
DiagnosticBuilder configurationDiag(StringRef Message, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Report any errors to do with reading the configuration using this method.
void setASTContext(ASTContext *Context)
Sets ASTContext for the current translation unit.
void setProfileStoragePrefix(StringRef ProfilePrefix)
Control storage of profile date.
void setEnableProfiling(bool Profile)
Control profile collection in clang-tidy.
void setCurrentFile(StringRef File)
Should be called when starting to process new translation unit.
bool shouldSuppressDiagnostic(DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info, SmallVectorImpl< tooling::Diagnostic > &NoLintErrors, bool AllowIO=true, bool EnableNoLintBlocks=true)
Check whether a given diagnostic should be suppressed due to the presence of a "NOLINT" suppression c...
bool treatAsError(StringRef CheckName) const
Returns true if the check should be upgraded to error for the CurrentFile.
DiagnosticBuilder diag(StringRef CheckName, SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Report any errors detected using this method.
ClangTidyOptions getOptionsForFile(StringRef File) const
Returns options for File.
std::optional< ClangTidyProfiling::StorageParams > getProfileStorageParams() const
const ClangTidyGlobalOptions & getGlobalOptions() const
Returns global options.
void setSourceManager(SourceManager *SourceMgr)
Sets the SourceManager of the used DiagnosticsEngine.
ClangTidyDiagnosticConsumer(ClangTidyContext &Ctx, DiagnosticsEngine *ExternalDiagEngine=nullptr, bool RemoveIncompatibleErrors=true, bool GetFixesFromNotes=false, bool EnableNolintBlocks=true)
void EndSourceFile() override
void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) override
void BeginSourceFile(const LangOptions &LangOpts, const Preprocessor *PP=nullptr) override
std::vector< ClangTidyError > take()
@ Info
An information message.
bool operator<(const Ref &L, const Ref &R)
@ Type
An inlay hint that for a type annotation.
const llvm::StringMap< tooling::Replacements > * getFixIt(const tooling::Diagnostic &Diagnostic, bool AnyFix)
Gets the Fix attached to Diagnostic.
llvm::SmallSet< llvm::StringRef, 5 > FileExtensionsSet
cppcoreguidelines::ProBoundsAvoidUncheckedContainerAccessCheck P
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
A detected error complete with information to display diagnostic and automatic fix.
ClangTidyError(StringRef CheckName, Level DiagLevel, StringRef BuildDirectory, bool IsWarningAsError)
std::vector< std::string > EnabledDiagnosticAliases
std::vector< FileFilter > LineFilter
Output warnings from certain line ranges of certain files only.
Contains options for clang-tidy.
ClangTidyOptions merge(const ClangTidyOptions &Other, unsigned Order) const
Creates a new ClangTidyOptions instance combined from all fields of this instance overridden by the f...
static ClangTidyOptions getDefaults()
These options are used for all settings that haven't been overridden by the OptionsProvider.
unsigned ErrorsIgnoredCheckFilter
unsigned ErrorsIgnoredNonUserCode
Contains a list of line ranges in a single file.
std::pair< unsigned int, unsigned int > LineRange
LineRange is a pair<start, end> (inclusive).