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;
252 : EmptyFileExtensions,
253 HeaderFileExtensions) &&
258 : EmptyFileExtensions,
259 ImplementationFileExtensions) &&
265 DiagEngine->SetArgToStringFn(&FormatASTNodeDiagnosticArgument, Context);
266 LangOpts = Context->getLangOpts();
270 return OptionsProvider->getGlobalOptions();
274 return CurrentOptions;
281 OptionsProvider->getOptions(File), 0);
287 ProfilePrefix = std::string(Prefix);
290std::optional<ClangTidyProfiling::StorageParams>
292 if (ProfilePrefix.empty())
299 assert(CheckFilter !=
nullptr);
300 return CheckFilter->contains(CheckName);
304 assert(WarningAsErrorFilter !=
nullptr);
305 return WarningAsErrorFilter->contains(CheckName);
309 const std::string ClangWarningOption = std::string(
310 DiagEngine->getDiagnosticIDs()->getWarningOptionForDiag(DiagnosticID));
311 if (!ClangWarningOption.empty())
312 return "clang-diagnostic-" + ClangWarningOption;
313 const llvm::DenseMap<unsigned, std::string>::const_iterator I =
314 CheckNamesByDiagnosticID.find(DiagnosticID);
315 if (I != CheckNamesByDiagnosticID.end())
321 return !CheckNamesByDiagnosticID.contains(DiagnosticID);
326 bool RemoveIncompatibleErrors,
bool GetFixesFromNotes,
327 bool EnableNolintBlocks)
328 : Context(Ctx), ExternalDiagEngine(ExternalDiagEngine),
329 RemoveIncompatibleErrors(RemoveIncompatibleErrors),
330 GetFixesFromNotes(GetFixesFromNotes),
331 EnableNolintBlocks(EnableNolintBlocks) {}
333void ClangTidyDiagnosticConsumer::finalizeLastError() {
334 if (!Errors.empty()) {
336 if (Error.DiagnosticName ==
"clang-tidy-config") {
339 Error.DiagLevel != ClangTidyError::Error) {
342 }
else if (!LastErrorRelatesToUserCode) {
345 }
else if (!LastErrorPassesLineFilter) {
346 ++Context.Stats.ErrorsIgnoredLineFilter;
349 ++Context.Stats.ErrorsDisplayed;
352 LastErrorRelatesToUserCode =
false;
353 LastErrorPassesLineFilter =
false;
356namespace clang::tidy {
358const llvm::StringMap<tooling::Replacements> *
359getFixIt(
const tooling::Diagnostic &Diagnostic,
bool AnyFix) {
360 if (!Diagnostic.Message.Fix.empty())
361 return &Diagnostic.Message.Fix;
364 const llvm::StringMap<tooling::Replacements> *Result =
nullptr;
365 for (
const auto &Note : Diagnostic.Notes) {
366 if (!Note.Fix.empty()) {
379 const Preprocessor *PP) {
380 DiagnosticConsumer::BeginSourceFile(LangOpts, PP);
382 assert(!InSourceFile);
387 assert(InSourceFile);
388 InSourceFile =
false;
390 DiagnosticConsumer::EndSourceFile();
394 DiagnosticsEngine::Level DiagLevel,
const Diagnostic &Info) {
397 assert(InSourceFile || Info.getLocation().isInvalid());
399 if (LastErrorWasIgnored && DiagLevel == DiagnosticsEngine::Note)
403 if (Context.shouldSuppressDiagnostic(DiagLevel, Info, SuppressionErrors,
404 EnableNolintBlocks)) {
405 ++Context.Stats.ErrorsIgnoredNOLINT;
407 LastErrorWasIgnored =
true;
408 for (
const auto &Error : SuppressionErrors)
413 LastErrorWasIgnored =
false;
415 DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info);
417 if (DiagLevel == DiagnosticsEngine::Note) {
418 assert(!Errors.empty() &&
419 "A diagnostic note can only be appended to a message.");
422 std::string CheckName = Context.getCheckName(Info.getID());
423 if (CheckName.empty()) {
427 case DiagnosticsEngine::Error:
428 case DiagnosticsEngine::Fatal:
429 CheckName =
"clang-diagnostic-error";
431 case DiagnosticsEngine::Warning:
432 CheckName =
"clang-diagnostic-warning";
434 case DiagnosticsEngine::Remark:
435 CheckName =
"clang-diagnostic-remark";
438 CheckName =
"clang-diagnostic-unknown";
443 ClangTidyError::Level Level = ClangTidyError::Warning;
444 if (DiagLevel == DiagnosticsEngine::Error ||
445 DiagLevel == DiagnosticsEngine::Fatal) {
448 Level = ClangTidyError::Error;
449 LastErrorRelatesToUserCode =
true;
450 LastErrorPassesLineFilter =
true;
451 }
else if (DiagLevel == DiagnosticsEngine::Remark) {
452 Level = ClangTidyError::Remark;
455 const bool IsWarningAsError = DiagLevel == DiagnosticsEngine::Warning &&
456 Context.treatAsError(CheckName);
457 Errors.emplace_back(CheckName, Level, Context.getCurrentBuildDirectory(),
461 if (ExternalDiagEngine) {
464 forwardDiagnostic(Info);
466 ClangTidyDiagnosticRenderer Converter(
467 Context.getLangOpts(), Context.DiagEngine->getDiagnosticOptions(),
469 SmallString<100> Message;
470 Info.FormatDiagnostic(Message);
472 if (Info.hasSourceManager())
473 Loc = FullSourceLoc(Info.getLocation(), Info.getSourceManager());
474 else if (Context.DiagEngine->hasSourceManager())
475 Loc = FullSourceLoc(Info.getLocation(),
476 Context.DiagEngine->getSourceManager());
477 Converter.emitDiagnostic(Loc, DiagLevel, Message, Info.getRanges(),
478 Info.getFixItHints());
481 if (Info.hasSourceManager())
482 checkFilters(Info.getLocation(), Info.getID(), Info.getSourceManager());
484 for (
const auto &Error : SuppressionErrors)
488bool ClangTidyDiagnosticConsumer::passesLineFilter(StringRef FileName,
489 unsigned LineNumber)
const {
492 const std::string NormalizedFileName =
493 llvm::sys::path::convert_to_slash(FileName);
495 if (StringRef(NormalizedFileName).ends_with(Filter.Name)) {
496 if (Filter.LineRanges.empty())
500 return Range.first <= LineNumber && LineNumber <= Range.second;
507void ClangTidyDiagnosticConsumer::forwardDiagnostic(
const Diagnostic &Info) {
509 const auto DiagLevelAndFormatString =
510 Context.getDiagLevelAndFormatString(
Info.getID(),
Info.getLocation());
511 const unsigned ExternalID =
512 ExternalDiagEngine->getDiagnosticIDs()->getCustomDiagID(
513 DiagLevelAndFormatString.first, DiagLevelAndFormatString.second);
517 ExternalDiagEngine->Report(
Info.getLocation(), ExternalID);
518 for (
const FixItHint &Hint :
Info.getFixItHints())
520 for (
const auto Range :
Info.getRanges())
522 for (
unsigned Index = 0; Index <
Info.getNumArgs(); ++Index) {
523 const DiagnosticsEngine::ArgumentKind Kind =
Info.getArgKind(Index);
525 case DiagnosticsEngine::ak_std_string:
526 Builder <<
Info.getArgStdStr(Index);
528 case DiagnosticsEngine::ak_c_string:
529 Builder <<
Info.getArgCStr(Index);
531 case DiagnosticsEngine::ak_sint:
532 Builder <<
Info.getArgSInt(Index);
534 case DiagnosticsEngine::ak_uint:
535 Builder <<
Info.getArgUInt(Index);
537 case DiagnosticsEngine::ak_tokenkind:
538 Builder << static_cast<tok::TokenKind>(
Info.getRawArg(Index));
540 case DiagnosticsEngine::ak_identifierinfo:
541 Builder <<
Info.getArgIdentifier(Index);
543 case DiagnosticsEngine::ak_qual:
544 Builder << Qualifiers::fromOpaqueValue(
Info.getRawArg(Index));
546 case DiagnosticsEngine::ak_qualtype:
547 Builder << QualType::getFromOpaquePtr(
548 reinterpret_cast<void *
>(
Info.getRawArg(Index)));
550 case DiagnosticsEngine::ak_declarationname:
551 Builder << DeclarationName::getFromOpaqueInteger(
Info.getRawArg(Index));
553 case DiagnosticsEngine::ak_nameddecl:
554 Builder << reinterpret_cast<const NamedDecl *>(
Info.getRawArg(Index));
556 case DiagnosticsEngine::ak_nestednamespec:
557 Builder << NestedNameSpecifier::getFromVoidPointer(
558 reinterpret_cast<void *
>(
Info.getRawArg(Index)));
560 case DiagnosticsEngine::ak_declcontext:
561 Builder << reinterpret_cast<DeclContext *>(
Info.getRawArg(Index));
563 case DiagnosticsEngine::ak_qualtype_pair:
566 case DiagnosticsEngine::ak_attr:
567 Builder << reinterpret_cast<Attr *>(
Info.getRawArg(Index));
569 case DiagnosticsEngine::ak_attr_info:
570 Builder << reinterpret_cast<AttributeCommonInfo *>(
Info.getRawArg(Index));
572 case DiagnosticsEngine::ak_addrspace:
573 Builder << static_cast<LangAS>(
Info.getRawArg(Index));
575 case DiagnosticsEngine::ak_expr:
576 Builder << reinterpret_cast<const Expr *>(
Info.getRawArg(Index));
581void ClangTidyDiagnosticConsumer::checkFilters(SourceLocation Location,
582 unsigned DiagnosticID,
583 const SourceManager &Sources) {
585 if (!Location.isValid()) {
586 LastErrorRelatesToUserCode =
true;
587 LastErrorPassesLineFilter =
true;
591 if (!Context.getOptions().SystemHeaders.value_or(
false)) {
592 if (Context.isCompilerDiagnostic(DiagnosticID)) {
593 if (Context.DiagEngine->getDiagnosticIDs()->shouldSuppressAsSystemWarning(
594 DiagnosticID, Location, *Context.DiagEngine))
597 if (Sources.isInSystemHeader(Location) ||
598 Sources.isInSystemMacro(Location))
606 const FileID FID = Sources.getDecomposedExpansionLoc(Location).first;
607 OptionalFileEntryRef
File = Sources.getFileEntryRefForID(FID);
612 LastErrorRelatesToUserCode =
true;
613 LastErrorPassesLineFilter =
true;
617 const StringRef FileName(
File->getName());
618 LastErrorRelatesToUserCode = LastErrorRelatesToUserCode ||
619 Sources.isInMainFile(Location) ||
620 (getHeaderFilter()->match(FileName) &&
621 !getExcludeHeaderFilter()->match(FileName));
623 const unsigned LineNumber = Sources.getExpansionLineNumber(Location);
624 LastErrorPassesLineFilter =
625 LastErrorPassesLineFilter || passesLineFilter(FileName, LineNumber);
628llvm::Regex *ClangTidyDiagnosticConsumer::getHeaderFilter() {
630 HeaderFilter = std::make_unique<llvm::Regex>(
631 Context.getOptions().HeaderFilterRegex.value_or(
""));
632 return HeaderFilter.get();
635llvm::Regex *ClangTidyDiagnosticConsumer::getExcludeHeaderFilter() {
636 if (!ExcludeHeaderFilter)
637 ExcludeHeaderFilter = std::make_unique<llvm::Regex>(
638 Context.getOptions().ExcludeHeaderFilterRegex.value_or(
""));
639 return ExcludeHeaderFilter.get();
642void ClangTidyDiagnosticConsumer::removeIncompatibleErrors() {
659 Event(
unsigned Begin,
unsigned End, EventType Type,
unsigned ErrorId,
689 Priority = {Begin,
Type, -End, -ErrorSize, ErrorId};
692 Priority = {Begin,
Type, -End, ErrorSize, ErrorId};
695 Priority = {End,
Type, -Begin, ErrorSize, ErrorId};
700 bool operator<(
const Event &Other)
const {
701 return Priority < Other.Priority;
710 std::tuple<unsigned, EventType, int, int, unsigned> Priority;
714 std::vector<int> Sizes;
716 std::pair<ClangTidyError *, llvm::StringMap<tooling::Replacements> *>>
718 for (
auto &Error : Errors)
719 if (
const auto *
Fix =
getFixIt(Error, GetFixesFromNotes))
720 ErrorFixes.emplace_back(
721 &Error,
const_cast<llvm::StringMap<tooling::Replacements> *
>(
Fix));
722 for (
const auto &ErrorAndFix : ErrorFixes) {
724 for (
const auto &FileAndReplaces : *ErrorAndFix.second)
725 for (
const auto &Replace : FileAndReplaces.second)
726 Size += Replace.getLength();
727 Sizes.push_back(Size);
731 llvm::StringMap<std::vector<Event>> FileEvents;
732 for (
unsigned I = 0; I < ErrorFixes.size(); ++I) {
733 for (
const auto &FileAndReplace : *ErrorFixes[I].second) {
734 for (
const auto &Replace : FileAndReplace.second) {
735 const unsigned Begin = Replace.getOffset();
736 const unsigned End = Begin + Replace.getLength();
737 auto &Events = FileEvents[Replace.getFilePath()];
739 Events.emplace_back(Begin, End, Event::ET_Insert, I, Sizes[I]);
741 Events.emplace_back(Begin, End, Event::ET_Begin, I, Sizes[I]);
742 Events.emplace_back(Begin, End, Event::ET_End, I, Sizes[I]);
748 llvm::BitVector Apply(ErrorFixes.size(),
true);
749 for (
auto &FileAndEvents : FileEvents) {
750 std::vector<Event> &Events = FileAndEvents.second;
753 int OpenIntervals = 0;
754 for (
const auto &Event : Events) {
755 switch (
Event.Type) {
756 case Event::ET_Begin:
757 if (OpenIntervals++ != 0)
758 Apply[
Event.ErrorId] =
false;
760 case Event::ET_Insert:
761 if (OpenIntervals != 0)
762 Apply[
Event.ErrorId] =
false;
765 if (--OpenIntervals != 0)
766 Apply[
Event.ErrorId] =
false;
770 assert(OpenIntervals == 0 &&
"Amount of begin/end points doesn't match");
773 for (
unsigned I = 0; I < ErrorFixes.size(); ++I) {
775 ErrorFixes[I].second->clear();
776 ErrorFixes[I].first->Notes.emplace_back(
777 "this fix will not be applied because it overlaps with another fix");
783struct LessClangTidyError {
784 bool operator()(
const ClangTidyError &LHS,
const ClangTidyError &RHS)
const {
785 const tooling::DiagnosticMessage &M1 = LHS.Message;
786 const tooling::DiagnosticMessage &M2 = RHS.Message;
791 return std::tie(M1.FilePath, M1.FileOffset, M1.Message,
792 LHS.DiagnosticName) <
793 std::tie(M2.FilePath, M2.FileOffset, M2.Message, RHS.DiagnosticName);
796struct EqualClangTidyError {
797 bool operator()(
const ClangTidyError &LHS,
const ClangTidyError &RHS)
const {
798 const LessClangTidyError Less;
799 return !Less(LHS, RHS) && !Less(RHS, LHS);
807 llvm::stable_sort(Errors, LessClangTidyError());
808 Errors.erase(llvm::unique(Errors, EqualClangTidyError()), Errors.end());
809 if (RemoveIncompatibleErrors) {
810 removeDuplicatedDiagnosticsOfAliasCheckers();
811 removeIncompatibleErrors();
813 return std::move(Errors);
816void ClangTidyDiagnosticConsumer::removeDuplicatedDiagnosticsOfAliasCheckers() {
817 if (Errors.size() <= 1)
820 static constexpr auto AreDuplicates = [](
const ClangTidyError &E1,
822 const tooling::DiagnosticMessage &M1 = E1.Message;
823 const tooling::DiagnosticMessage &M2 = E2.Message;
824 return std::tie(M1.FilePath, M1.FileOffset, M1.Message) ==
825 std::tie(M2.FilePath, M2.FileOffset, M2.Message);
828 auto LastUniqueErrorIt = Errors.begin();
832 if (!AreDuplicates(Error, ExistingError)) {
834 if (&*LastUniqueErrorIt != &Error)
835 *LastUniqueErrorIt = std::move(Error);
837 const llvm::StringMap<tooling::Replacements> &CandidateFix =
839 const llvm::StringMap<tooling::Replacements> &ExistingFix =
840 ExistingError.Message.Fix;
842 if (CandidateFix != ExistingFix) {
844 ExistingError.Message.Fix.clear();
845 ExistingError.Notes.emplace_back(
846 llvm::formatv(
"cannot apply fix-it because an alias checker has "
847 "suggested a different fix-it; please remove one of "
848 "the checkers ('{0}', '{1}') or "
849 "ensure they are both configured the same",
850 ExistingError.DiagnosticName, Error.DiagnosticName)
854 if (
Error.IsWarningAsError)
861 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).