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/Path.h"
40#include "llvm/Support/Regex.h"
49class ClangTidyDiagnosticRenderer :
public DiagnosticRenderer {
51 ClangTidyDiagnosticRenderer(
const LangOptions &LangOpts,
52 DiagnosticOptions &DiagOpts,
53 ClangTidyError &Error)
54 : DiagnosticRenderer(LangOpts, DiagOpts), Error(Error) {}
57 void emitDiagnosticMessage(FullSourceLoc Loc, PresumedLoc PLoc,
58 DiagnosticsEngine::Level Level, StringRef Message,
59 ArrayRef<CharSourceRange> Ranges,
60 DiagOrStoredDiag Info)
override {
65 const std::string CheckNameInMessage =
" [" + Error.DiagnosticName +
"]";
66 Message.consume_back(CheckNameInMessage);
68 const auto TidyMessage =
70 ? tooling::DiagnosticMessage(Message, Loc.getManager(), Loc)
71 : tooling::DiagnosticMessage(Message);
77 const auto ToCharRange = [
this, &Loc](
const CharSourceRange &SourceRange) {
78 if (SourceRange.isCharRange())
80 assert(SourceRange.isTokenRange());
81 const SourceLocation End = Lexer::getLocForEndOfToken(
82 SourceRange.getEnd(), 0, Loc.getManager(), LangOpts);
83 return CharSourceRange::getCharRange(SourceRange.getBegin(), End);
87 const auto ValidRanges =
88 llvm::make_filter_range(Ranges, [](
const CharSourceRange &R) {
89 return R.getAsRange().isValid();
92 if (Level == DiagnosticsEngine::Note) {
93 Error.Notes.push_back(TidyMessage);
94 for (
const CharSourceRange &SourceRange : ValidRanges)
95 Error.Notes.back().Ranges.emplace_back(Loc.getManager(),
96 ToCharRange(SourceRange));
99 assert(Error.Message.Message.empty() &&
"Overwriting a diagnostic message");
100 Error.Message = TidyMessage;
101 for (
const CharSourceRange &SourceRange : ValidRanges)
102 Error.Message.Ranges.emplace_back(Loc.getManager(),
103 ToCharRange(SourceRange));
106 void emitDiagnosticLoc(FullSourceLoc Loc, PresumedLoc PLoc,
107 DiagnosticsEngine::Level Level,
108 ArrayRef<CharSourceRange> Ranges)
override {}
110 void emitCodeContext(FullSourceLoc Loc, DiagnosticsEngine::Level Level,
111 SmallVectorImpl<CharSourceRange> &Ranges,
112 ArrayRef<FixItHint> Hints)
override {
113 assert(Loc.isValid());
114 tooling::DiagnosticMessage *DiagWithFix =
115 Level == DiagnosticsEngine::Note ? &Error.Notes.back() : &Error.Message;
117 for (
const auto &FixIt : Hints) {
118 const CharSourceRange Range = FixIt.RemoveRange;
119 assert(Range.getBegin().isValid() && Range.getEnd().isValid() &&
120 "Invalid range in the fix-it hint.");
121 assert(Range.getBegin().isFileID() && Range.getEnd().isFileID() &&
122 "Only file locations supported in fix-it hints.");
124 const tooling::Replacement Replacement(Loc.getManager(), Range,
127 DiagWithFix->Fix[Replacement.getFilePath()].add(Replacement);
131 llvm::errs() <<
"Fix conflicts with existing fix! "
132 << llvm::toString(std::move(Err)) <<
"\n";
133 assert(
false &&
"Fix conflicts with existing fix!");
138 void emitIncludeLocation(FullSourceLoc Loc, PresumedLoc PLoc)
override {}
140 void emitImportLocation(FullSourceLoc Loc, PresumedLoc PLoc,
141 StringRef ModuleName)
override {}
143 void emitBuildingModuleLocation(FullSourceLoc Loc, PresumedLoc PLoc,
144 StringRef ModuleName)
override {}
146 void endDiagnostic(DiagOrStoredDiag D,
147 DiagnosticsEngine::Level Level)
override {
148 assert(!Error.Message.Message.empty() &&
"Message has not been set");
152 ClangTidyError &Error;
157 ClangTidyError::Level DiagLevel,
159 :
tooling::Diagnostic(CheckName, DiagLevel, BuildDirectory),
163 std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider,
164 bool AllowEnablingAnalyzerAlphaCheckers,
bool EnableModuleHeadersParsing,
165 bool ExperimentalCustomChecks)
166 : OptionsProvider(std::
move(OptionsProvider)),
167 AllowEnablingAnalyzerAlphaCheckers(AllowEnablingAnalyzerAlphaCheckers),
168 EnableModuleHeadersParsing(EnableModuleHeadersParsing),
169 ExperimentalCustomChecks(ExperimentalCustomChecks) {
178 StringRef CheckName, SourceLocation Loc, StringRef Description,
179 DiagnosticIDs::Level Level ) {
180 assert(Loc.isValid());
181 const unsigned ID = DiagEngine->getDiagnosticIDs()->getCustomDiagID(
182 Level, (Description +
" [" + CheckName +
"]").str());
183 CheckNamesByDiagnosticID.try_emplace(ID, CheckName);
184 return DiagEngine->Report(Loc, ID);
188 StringRef CheckName, StringRef Description,
189 DiagnosticIDs::Level Level ) {
190 const unsigned ID = DiagEngine->getDiagnosticIDs()->getCustomDiagID(
191 Level, (Description +
" [" + CheckName +
"]").str());
192 CheckNamesByDiagnosticID.try_emplace(ID, CheckName);
193 return DiagEngine->Report(ID);
197 SourceManager &SM = DiagEngine->getSourceManager();
198 FileManager &FM = SM.getFileManager();
199 const FileEntryRef File =
200 llvm::cantFail(FM.getFileRef(Error.Message.FilePath));
201 const FileID ID = SM.getOrCreateFileID(File, SrcMgr::C_User);
202 const SourceLocation FileStartLoc = SM.getLocForStartOfFile(ID);
203 const SourceLocation Loc = FileStartLoc.getLocWithOffset(
204 static_cast<SourceLocation::IntTy
>(Error.Message.FileOffset));
205 return diag(Error.DiagnosticName, Loc, Error.Message.Message,
206 static_cast<DiagnosticIDs::Level
>(Error.DiagLevel));
211 DiagnosticIDs::Level Level ) {
212 return diag(
"clang-tidy-config", Message, Level);
216 DiagnosticsEngine::Level DiagLevel,
const Diagnostic &Info,
217 SmallVectorImpl<tooling::Diagnostic> &NoLintErrors,
bool AllowIO,
218 bool EnableNoLintBlocks) {
219 const std::string CheckName =
getCheckName(Info.getID());
220 return NoLintHandler.shouldSuppress(DiagLevel, Info, CheckName, NoLintErrors,
221 AllowIO, EnableNoLintBlocks);
225 DiagEngine->setSourceManager(SourceMgr);
230 FileExtensions.clear();
231 for (
const StringRef Suffix : AllFileExtensions) {
232 StringRef Extension = Suffix.trim();
233 if (!llvm::all_of(Extension, isAlphanumeric))
235 FileExtensions.insert(Extension);
241 CurrentFile = std::string(File);
243 CheckFilter = std::make_unique<CachedGlobList>(
245 WarningAsErrorFilter = std::make_unique<CachedGlobList>(
247 static const std::vector<std::string> EmptyFileExtensions;
250 : EmptyFileExtensions,
251 HeaderFileExtensions))
255 : EmptyFileExtensions,
256 ImplementationFileExtensions))
261 DiagEngine->SetArgToStringFn(&FormatASTNodeDiagnosticArgument, Context);
262 LangOpts = Context->getLangOpts();
266 return OptionsProvider->getGlobalOptions();
270 return CurrentOptions;
277 OptionsProvider->getOptions(File), 0);
283 ProfilePrefix = std::string(Prefix);
286std::optional<ClangTidyProfiling::StorageParams>
288 if (ProfilePrefix.empty())
295 assert(CheckFilter !=
nullptr);
296 return CheckFilter->contains(CheckName);
300 assert(WarningAsErrorFilter !=
nullptr);
301 return WarningAsErrorFilter->contains(CheckName);
305 const std::string ClangWarningOption = std::string(
306 DiagEngine->getDiagnosticIDs()->getWarningOptionForDiag(DiagnosticID));
307 if (!ClangWarningOption.empty())
308 return "clang-diagnostic-" + ClangWarningOption;
309 const llvm::DenseMap<unsigned, std::string>::const_iterator I =
310 CheckNamesByDiagnosticID.find(DiagnosticID);
311 if (I != CheckNamesByDiagnosticID.end())
317 return !CheckNamesByDiagnosticID.contains(DiagnosticID);
322 bool RemoveIncompatibleErrors,
bool GetFixesFromNotes,
323 bool EnableNolintBlocks)
324 : Context(Ctx), ExternalDiagEngine(ExternalDiagEngine),
325 RemoveIncompatibleErrors(RemoveIncompatibleErrors),
326 GetFixesFromNotes(GetFixesFromNotes),
327 EnableNolintBlocks(EnableNolintBlocks) {}
329void ClangTidyDiagnosticConsumer::finalizeLastError() {
330 if (!Errors.empty()) {
332 if (Error.DiagnosticName ==
"clang-tidy-config") {
335 Error.DiagLevel != ClangTidyError::Error) {
338 }
else if (!LastErrorRelatesToUserCode) {
341 }
else if (!LastErrorPassesLineFilter) {
342 ++Context.Stats.ErrorsIgnoredLineFilter;
345 ++Context.Stats.ErrorsDisplayed;
348 LastErrorRelatesToUserCode =
false;
349 LastErrorPassesLineFilter =
false;
352namespace clang::tidy {
354const llvm::StringMap<tooling::Replacements> *
355getFixIt(
const tooling::Diagnostic &Diagnostic,
bool AnyFix) {
356 if (!Diagnostic.Message.Fix.empty())
357 return &Diagnostic.Message.Fix;
360 const llvm::StringMap<tooling::Replacements> *Result =
nullptr;
361 for (
const auto &Note : Diagnostic.Notes) {
362 if (!Note.Fix.empty()) {
375 const Preprocessor *PP) {
376 DiagnosticConsumer::BeginSourceFile(LangOpts, PP);
378 assert(!InSourceFile);
383 assert(InSourceFile);
384 InSourceFile =
false;
386 DiagnosticConsumer::EndSourceFile();
390 DiagnosticsEngine::Level DiagLevel,
const Diagnostic &Info) {
393 assert(InSourceFile || Info.getLocation().isInvalid());
395 if (LastErrorWasIgnored && DiagLevel == DiagnosticsEngine::Note)
399 if (Context.shouldSuppressDiagnostic(DiagLevel, Info, SuppressionErrors,
400 EnableNolintBlocks)) {
401 ++Context.Stats.ErrorsIgnoredNOLINT;
403 LastErrorWasIgnored =
true;
404 for (
const auto &Error : SuppressionErrors)
409 LastErrorWasIgnored =
false;
411 DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info);
413 if (DiagLevel == DiagnosticsEngine::Note) {
414 assert(!Errors.empty() &&
415 "A diagnostic note can only be appended to a message.");
418 std::string CheckName = Context.getCheckName(Info.getID());
419 if (CheckName.empty()) {
423 case DiagnosticsEngine::Error:
424 case DiagnosticsEngine::Fatal:
425 CheckName =
"clang-diagnostic-error";
427 case DiagnosticsEngine::Warning:
428 CheckName =
"clang-diagnostic-warning";
430 case DiagnosticsEngine::Remark:
431 CheckName =
"clang-diagnostic-remark";
434 CheckName =
"clang-diagnostic-unknown";
439 ClangTidyError::Level Level = ClangTidyError::Warning;
440 if (DiagLevel == DiagnosticsEngine::Error ||
441 DiagLevel == DiagnosticsEngine::Fatal) {
444 Level = ClangTidyError::Error;
445 LastErrorRelatesToUserCode =
true;
446 LastErrorPassesLineFilter =
true;
447 }
else if (DiagLevel == DiagnosticsEngine::Remark) {
448 Level = ClangTidyError::Remark;
451 const bool IsWarningAsError = DiagLevel == DiagnosticsEngine::Warning &&
452 Context.treatAsError(CheckName);
453 Errors.emplace_back(CheckName, Level, Context.getCurrentBuildDirectory(),
457 if (ExternalDiagEngine) {
460 forwardDiagnostic(Info);
462 ClangTidyDiagnosticRenderer Converter(
463 Context.getLangOpts(), Context.DiagEngine->getDiagnosticOptions(),
465 SmallString<100> Message;
466 Info.FormatDiagnostic(Message);
468 if (Info.hasSourceManager())
469 Loc = FullSourceLoc(Info.getLocation(), Info.getSourceManager());
470 else if (Context.DiagEngine->hasSourceManager())
471 Loc = FullSourceLoc(Info.getLocation(),
472 Context.DiagEngine->getSourceManager());
473 Converter.emitDiagnostic(Loc, DiagLevel, Message, Info.getRanges(),
474 Info.getFixItHints());
477 if (Info.hasSourceManager())
478 checkFilters(Info.getLocation(), Info.getID(), Info.getSourceManager());
480 for (
const auto &Error : SuppressionErrors)
484bool ClangTidyDiagnosticConsumer::passesLineFilter(StringRef FileName,
485 unsigned LineNumber)
const {
488 const std::string NormalizedFileName =
489 llvm::sys::path::convert_to_slash(FileName);
491 if (StringRef(NormalizedFileName).ends_with(Filter.Name)) {
492 if (Filter.LineRanges.empty())
496 return Range.first <= LineNumber && LineNumber <= Range.second;
503void ClangTidyDiagnosticConsumer::forwardDiagnostic(
const Diagnostic &Info) {
505 const auto DiagLevelAndFormatString =
506 Context.getDiagLevelAndFormatString(
Info.getID(),
Info.getLocation());
507 const unsigned ExternalID =
508 ExternalDiagEngine->getDiagnosticIDs()->getCustomDiagID(
509 DiagLevelAndFormatString.first, DiagLevelAndFormatString.second);
513 ExternalDiagEngine->Report(
Info.getLocation(), ExternalID);
514 for (
const FixItHint &Hint :
Info.getFixItHints())
516 for (
const auto Range :
Info.getRanges())
518 for (
unsigned Index = 0; Index <
Info.getNumArgs(); ++Index) {
519 const DiagnosticsEngine::ArgumentKind Kind =
Info.getArgKind(Index);
521 case DiagnosticsEngine::ak_std_string:
522 Builder <<
Info.getArgStdStr(Index);
524 case DiagnosticsEngine::ak_c_string:
525 Builder <<
Info.getArgCStr(Index);
527 case DiagnosticsEngine::ak_sint:
528 Builder <<
Info.getArgSInt(Index);
530 case DiagnosticsEngine::ak_uint:
531 Builder <<
Info.getArgUInt(Index);
533 case DiagnosticsEngine::ak_tokenkind:
534 Builder << static_cast<tok::TokenKind>(
Info.getRawArg(Index));
536 case DiagnosticsEngine::ak_identifierinfo:
537 Builder <<
Info.getArgIdentifier(Index);
539 case DiagnosticsEngine::ak_qual:
540 Builder << Qualifiers::fromOpaqueValue(
Info.getRawArg(Index));
542 case DiagnosticsEngine::ak_qualtype:
543 Builder << QualType::getFromOpaquePtr(
544 reinterpret_cast<void *
>(
Info.getRawArg(Index)));
546 case DiagnosticsEngine::ak_declarationname:
547 Builder << DeclarationName::getFromOpaqueInteger(
Info.getRawArg(Index));
549 case DiagnosticsEngine::ak_nameddecl:
550 Builder << reinterpret_cast<const NamedDecl *>(
Info.getRawArg(Index));
552 case DiagnosticsEngine::ak_nestednamespec:
553 Builder << NestedNameSpecifier::getFromVoidPointer(
554 reinterpret_cast<void *
>(
Info.getRawArg(Index)));
556 case DiagnosticsEngine::ak_declcontext:
557 Builder << reinterpret_cast<DeclContext *>(
Info.getRawArg(Index));
559 case DiagnosticsEngine::ak_qualtype_pair:
562 case DiagnosticsEngine::ak_attr:
563 Builder << reinterpret_cast<Attr *>(
Info.getRawArg(Index));
565 case DiagnosticsEngine::ak_attr_info:
566 Builder << reinterpret_cast<AttributeCommonInfo *>(
Info.getRawArg(Index));
568 case DiagnosticsEngine::ak_addrspace:
569 Builder << static_cast<LangAS>(
Info.getRawArg(Index));
571 case DiagnosticsEngine::ak_expr:
572 Builder << reinterpret_cast<const Expr *>(
Info.getRawArg(Index));
577void ClangTidyDiagnosticConsumer::checkFilters(SourceLocation Location,
578 unsigned DiagnosticID,
579 const SourceManager &Sources) {
581 if (!Location.isValid()) {
582 LastErrorRelatesToUserCode =
true;
583 LastErrorPassesLineFilter =
true;
587 if (!Context.getOptions().SystemHeaders.value_or(
false)) {
588 if (Context.isCompilerDiagnostic(DiagnosticID)) {
589 if (Context.DiagEngine->getDiagnosticIDs()->shouldSuppressAsSystemWarning(
590 DiagnosticID, Location, *Context.DiagEngine))
593 if (Sources.isInSystemHeader(Location) ||
594 Sources.isInSystemMacro(Location))
602 const FileID FID = Sources.getDecomposedExpansionLoc(Location).first;
603 OptionalFileEntryRef
File = Sources.getFileEntryRefForID(FID);
608 LastErrorRelatesToUserCode =
true;
609 LastErrorPassesLineFilter =
true;
613 const StringRef FileName(
File->getName());
614 LastErrorRelatesToUserCode = LastErrorRelatesToUserCode ||
615 Sources.isInMainFile(Location) ||
616 (getHeaderFilter()->match(FileName) &&
617 !getExcludeHeaderFilter()->match(FileName));
619 const unsigned LineNumber = Sources.getExpansionLineNumber(Location);
620 LastErrorPassesLineFilter =
621 LastErrorPassesLineFilter || passesLineFilter(FileName, LineNumber);
624llvm::Regex *ClangTidyDiagnosticConsumer::getHeaderFilter() {
626 HeaderFilter = std::make_unique<llvm::Regex>(
627 Context.getOptions().HeaderFilterRegex.value_or(
""));
628 return HeaderFilter.get();
631llvm::Regex *ClangTidyDiagnosticConsumer::getExcludeHeaderFilter() {
632 if (!ExcludeHeaderFilter)
633 ExcludeHeaderFilter = std::make_unique<llvm::Regex>(
634 Context.getOptions().ExcludeHeaderFilterRegex.value_or(
""));
635 return ExcludeHeaderFilter.get();
638void ClangTidyDiagnosticConsumer::removeIncompatibleErrors() {
655 Event(
unsigned Begin,
unsigned End, EventType Type,
unsigned ErrorId,
685 Priority = {Begin,
Type, -End, -ErrorSize, ErrorId};
688 Priority = {Begin,
Type, -End, ErrorSize, ErrorId};
691 Priority = {End,
Type, -Begin, ErrorSize, ErrorId};
696 bool operator<(
const Event &Other)
const {
697 return Priority < Other.Priority;
706 std::tuple<unsigned, EventType, int, int, unsigned> Priority;
710 std::vector<int> Sizes;
712 std::pair<ClangTidyError *, llvm::StringMap<tooling::Replacements> *>>
714 for (
auto &Error : Errors)
715 if (
const auto *
Fix =
getFixIt(Error, GetFixesFromNotes))
716 ErrorFixes.emplace_back(
717 &Error,
const_cast<llvm::StringMap<tooling::Replacements> *
>(
Fix));
718 for (
const auto &ErrorAndFix : ErrorFixes) {
720 for (
const auto &FileAndReplaces : *ErrorAndFix.second)
721 for (
const auto &Replace : FileAndReplaces.second)
722 Size += Replace.getLength();
723 Sizes.push_back(Size);
727 llvm::StringMap<std::vector<Event>> FileEvents;
728 for (
unsigned I = 0; I < ErrorFixes.size(); ++I) {
729 for (
const auto &FileAndReplace : *ErrorFixes[I].second) {
730 for (
const auto &Replace : FileAndReplace.second) {
731 const unsigned Begin = Replace.getOffset();
732 const unsigned End = Begin + Replace.getLength();
733 auto &Events = FileEvents[Replace.getFilePath()];
735 Events.emplace_back(Begin, End, Event::ET_Insert, I, Sizes[I]);
737 Events.emplace_back(Begin, End, Event::ET_Begin, I, Sizes[I]);
738 Events.emplace_back(Begin, End, Event::ET_End, I, Sizes[I]);
744 llvm::BitVector Apply(ErrorFixes.size(),
true);
745 for (
auto &FileAndEvents : FileEvents) {
746 std::vector<Event> &Events = FileAndEvents.second;
749 int OpenIntervals = 0;
750 for (
const auto &Event : Events) {
751 switch (
Event.Type) {
752 case Event::ET_Begin:
753 if (OpenIntervals++ != 0)
754 Apply[
Event.ErrorId] =
false;
756 case Event::ET_Insert:
757 if (OpenIntervals != 0)
758 Apply[
Event.ErrorId] =
false;
761 if (--OpenIntervals != 0)
762 Apply[
Event.ErrorId] =
false;
766 assert(OpenIntervals == 0 &&
"Amount of begin/end points doesn't match");
769 for (
unsigned I = 0; I < ErrorFixes.size(); ++I) {
771 ErrorFixes[I].second->clear();
772 ErrorFixes[I].first->Notes.emplace_back(
773 "this fix will not be applied because it overlaps with another fix");
779struct LessClangTidyError {
780 bool operator()(
const ClangTidyError &LHS,
const ClangTidyError &RHS)
const {
781 const tooling::DiagnosticMessage &M1 = LHS.Message;
782 const tooling::DiagnosticMessage &M2 = RHS.Message;
787 return std::tie(M1.FilePath, M1.FileOffset, M1.Message,
788 LHS.DiagnosticName) <
789 std::tie(M2.FilePath, M2.FileOffset, M2.Message, RHS.DiagnosticName);
792struct EqualClangTidyError {
793 bool operator()(
const ClangTidyError &LHS,
const ClangTidyError &RHS)
const {
794 const LessClangTidyError Less;
795 return !Less(LHS, RHS) && !Less(RHS, LHS);
803 llvm::stable_sort(Errors, LessClangTidyError());
804 Errors.erase(llvm::unique(Errors, EqualClangTidyError()), Errors.end());
805 if (RemoveIncompatibleErrors) {
806 removeDuplicatedDiagnosticsOfAliasCheckers();
807 removeIncompatibleErrors();
809 return std::move(Errors);
812void ClangTidyDiagnosticConsumer::removeDuplicatedDiagnosticsOfAliasCheckers() {
813 if (Errors.size() <= 1)
816 static constexpr auto AreDuplicates = [](
const ClangTidyError &E1,
818 const tooling::DiagnosticMessage &M1 = E1.Message;
819 const tooling::DiagnosticMessage &M2 = E2.Message;
820 return std::tie(M1.FilePath, M1.FileOffset, M1.Message) ==
821 std::tie(M2.FilePath, M2.FileOffset, M2.Message);
824 auto LastUniqueErrorIt = Errors.begin();
828 if (!AreDuplicates(Error, ExistingError)) {
830 if (&*LastUniqueErrorIt != &Error)
831 *LastUniqueErrorIt = std::move(Error);
833 const llvm::StringMap<tooling::Replacements> &CandidateFix =
835 const llvm::StringMap<tooling::Replacements> &ExistingFix =
836 ExistingError.Message.Fix;
838 if (CandidateFix != ExistingFix) {
840 ExistingError.Message.Fix.clear();
841 ExistingError.Notes.emplace_back(
842 llvm::formatv(
"cannot apply fix-it because an alias checker has "
843 "suggested a different fix-it; please remove one of "
844 "the checkers ('{0}', '{1}') or "
845 "ensure they are both configured the same",
846 ExistingError.DiagnosticName, Error.DiagnosticName)
850 if (
Error.IsWarningAsError)
857 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).