| //===--- ClangTidyDiagnosticConsumer.h - clang-tidy -------------*- C++ -*-===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYDIAGNOSTICCONSUMER_H | |
| #define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYDIAGNOSTICCONSUMER_H | |
| #include "ClangTidyOptions.h" | |
| #include "ClangTidyProfiling.h" | |
| #include "FileExtensionsSet.h" | |
| #include "NoLintDirectiveHandler.h" | |
| #include "clang/Basic/Diagnostic.h" | |
| #include "clang/Tooling/Core/Diagnostic.h" | |
| #include "llvm/ADT/DenseMap.h" | |
| #include "llvm/ADT/StringSet.h" | |
| #include "llvm/Support/Regex.h" | |
| #include <optional> | |
| namespace clang { | |
| class ASTContext; | |
| class SourceManager; | |
| namespace tidy { | |
| class CachedGlobList; | |
| /// A detected error complete with information to display diagnostic and | |
| /// automatic fix. | |
| /// | |
| /// This is used as an intermediate format to transport Diagnostics without a | |
| /// dependency on a SourceManager. | |
| /// | |
| /// FIXME: Make Diagnostics flexible enough to support this directly. | |
| struct ClangTidyError : tooling::Diagnostic { | |
| ClangTidyError(StringRef CheckName, Level DiagLevel, StringRef BuildDirectory, | |
| bool IsWarningAsError); | |
| bool IsWarningAsError; | |
| std::vector<std::string> EnabledDiagnosticAliases; | |
| }; | |
| /// Contains displayed and ignored diagnostic counters for a ClangTidy run. | |
| struct ClangTidyStats { | |
| unsigned ErrorsDisplayed = 0; | |
| unsigned ErrorsIgnoredCheckFilter = 0; | |
| unsigned ErrorsIgnoredNOLINT = 0; | |
| unsigned ErrorsIgnoredNonUserCode = 0; | |
| unsigned ErrorsIgnoredLineFilter = 0; | |
| unsigned errorsIgnored() const { | |
| return ErrorsIgnoredNOLINT + ErrorsIgnoredCheckFilter + | |
| ErrorsIgnoredNonUserCode + ErrorsIgnoredLineFilter; | |
| } | |
| }; | |
| /// Every \c ClangTidyCheck reports errors through a \c DiagnosticsEngine | |
| /// provided by this context. | |
| /// | |
| /// A \c ClangTidyCheck always has access to the active context to report | |
| /// warnings like: | |
| /// \code | |
| /// Context->Diag(Loc, "Single-argument constructors must be explicit") | |
| /// << FixItHint::CreateInsertion(Loc, "explicit "); | |
| /// \endcode | |
| class ClangTidyContext { | |
| public: | |
| /// Initializes \c ClangTidyContext instance. | |
| ClangTidyContext(std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider, | |
| bool AllowEnablingAnalyzerAlphaCheckers = false, | |
| bool EnableModuleHeadersParsing = false); | |
| /// Sets the DiagnosticsEngine that diag() will emit diagnostics to. | |
| // FIXME: this is required initialization, and should be a constructor param. | |
| // Fix the context -> diag engine -> consumer -> context initialization cycle. | |
| void setDiagnosticsEngine(DiagnosticsEngine *DiagEngine) { | |
| this->DiagEngine = DiagEngine; | |
| } | |
| ~ClangTidyContext(); | |
| /// Report any errors detected using this method. | |
| /// | |
| /// This is still under heavy development and will likely change towards using | |
| /// tablegen'd diagnostic IDs. | |
| /// FIXME: Figure out a way to manage ID spaces. | |
| DiagnosticBuilder diag(StringRef CheckName, SourceLocation Loc, | |
| StringRef Description, | |
| DiagnosticIDs::Level Level = DiagnosticIDs::Warning); | |
| DiagnosticBuilder diag(StringRef CheckName, StringRef Description, | |
| DiagnosticIDs::Level Level = DiagnosticIDs::Warning); | |
| DiagnosticBuilder diag(const tooling::Diagnostic &Error); | |
| /// Report any errors to do with reading the configuration using this method. | |
| DiagnosticBuilder | |
| configurationDiag(StringRef Message, | |
| DiagnosticIDs::Level Level = DiagnosticIDs::Warning); | |
| /// Check whether a given diagnostic should be suppressed due to the presence | |
| /// of a "NOLINT" suppression comment. | |
| /// This is exposed so that other tools that present clang-tidy diagnostics | |
| /// (such as clangd) can respect the same suppression rules as clang-tidy. | |
| /// This does not handle suppression of notes following a suppressed | |
| /// diagnostic; that is left to the caller as it requires maintaining state in | |
| /// between calls to this function. | |
| /// If any NOLINT is malformed, e.g. a BEGIN without a subsequent END, output | |
| /// \param NoLintErrors will return an error about it. | |
| /// If \param AllowIO is false, the function does not attempt to read source | |
| /// files from disk which are not already mapped into memory; such files are | |
| /// treated as not containing a suppression comment. | |
| /// \param EnableNoLintBlocks controls whether to honor NOLINTBEGIN/NOLINTEND | |
| /// blocks; if false, only considers line-level disabling. | |
| bool | |
| shouldSuppressDiagnostic(DiagnosticsEngine::Level DiagLevel, | |
| const Diagnostic &Info, | |
| SmallVectorImpl<tooling::Diagnostic> &NoLintErrors, | |
| bool AllowIO = true, bool EnableNoLintBlocks = true); | |
| /// Sets the \c SourceManager of the used \c DiagnosticsEngine. | |
| /// | |
| /// This is called from the \c ClangTidyCheck base class. | |
| void setSourceManager(SourceManager *SourceMgr); | |
| /// Should be called when starting to process new translation unit. | |
| void setCurrentFile(StringRef File); | |
| /// Returns the main file name of the current translation unit. | |
| StringRef getCurrentFile() const { return CurrentFile; } | |
| /// Sets ASTContext for the current translation unit. | |
| void setASTContext(ASTContext *Context); | |
| /// Gets the language options from the AST context. | |
| const LangOptions &getLangOpts() const { return LangOpts; } | |
| /// Returns the name of the clang-tidy check which produced this | |
| /// diagnostic ID. | |
| std::string getCheckName(unsigned DiagnosticID) const; | |
| /// Returns \c true if the check is enabled for the \c CurrentFile. | |
| /// | |
| /// The \c CurrentFile can be changed using \c setCurrentFile. | |
| bool isCheckEnabled(StringRef CheckName) const; | |
| /// Returns \c true if the check should be upgraded to error for the | |
| /// \c CurrentFile. | |
| bool treatAsError(StringRef CheckName) const; | |
| /// Returns global options. | |
| const ClangTidyGlobalOptions &getGlobalOptions() const; | |
| /// Returns options for \c CurrentFile. | |
| /// | |
| /// The \c CurrentFile can be changed using \c setCurrentFile. | |
| const ClangTidyOptions &getOptions() const; | |
| /// Returns options for \c File. Does not change or depend on | |
| /// \c CurrentFile. | |
| ClangTidyOptions getOptionsForFile(StringRef File) const; | |
| const FileExtensionsSet &getHeaderFileExtensions() const { | |
| return HeaderFileExtensions; | |
| } | |
| const FileExtensionsSet &getImplementationFileExtensions() const { | |
| return ImplementationFileExtensions; | |
| } | |
| /// Returns \c ClangTidyStats containing issued and ignored diagnostic | |
| /// counters. | |
| const ClangTidyStats &getStats() const { return Stats; } | |
| /// Control profile collection in clang-tidy. | |
| void setEnableProfiling(bool Profile); | |
| bool getEnableProfiling() const { return Profile; } | |
| /// Control storage of profile date. | |
| void setProfileStoragePrefix(StringRef ProfilePrefix); | |
| std::optional<ClangTidyProfiling::StorageParams> | |
| getProfileStorageParams() const; | |
| /// Should be called when starting to process new translation unit. | |
| void setCurrentBuildDirectory(StringRef BuildDirectory) { | |
| CurrentBuildDirectory = std::string(BuildDirectory); | |
| } | |
| /// Returns build directory of the current translation unit. | |
| const std::string &getCurrentBuildDirectory() const { | |
| return CurrentBuildDirectory; | |
| } | |
| /// If the experimental alpha checkers from the static analyzer can be | |
| /// enabled. | |
| bool canEnableAnalyzerAlphaCheckers() const { | |
| return AllowEnablingAnalyzerAlphaCheckers; | |
| } | |
| // This method determines whether preprocessor-level module header parsing is | |
| // enabled using the `--experimental-enable-module-headers-parsing` option. | |
| bool canEnableModuleHeadersParsing() const { | |
| return EnableModuleHeadersParsing; | |
| } | |
| void setSelfContainedDiags(bool Value) { SelfContainedDiags = Value; } | |
| bool areDiagsSelfContained() const { return SelfContainedDiags; } | |
| using DiagLevelAndFormatString = std::pair<DiagnosticIDs::Level, std::string>; | |
| DiagLevelAndFormatString getDiagLevelAndFormatString(unsigned DiagnosticID, | |
| SourceLocation Loc) { | |
| return { | |
| static_cast<DiagnosticIDs::Level>( | |
| DiagEngine->getDiagnosticLevel(DiagnosticID, Loc)), | |
| std::string( | |
| DiagEngine->getDiagnosticIDs()->getDescription(DiagnosticID))}; | |
| } | |
| void setOptionsCollector(llvm::StringSet<> *Collector) { | |
| OptionsCollector = Collector; | |
| } | |
| llvm::StringSet<> *getOptionsCollector() const { return OptionsCollector; } | |
| private: | |
| // Writes to Stats. | |
| friend class ClangTidyDiagnosticConsumer; | |
| DiagnosticsEngine *DiagEngine = nullptr; | |
| std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider; | |
| std::string CurrentFile; | |
| ClangTidyOptions CurrentOptions; | |
| std::unique_ptr<CachedGlobList> CheckFilter; | |
| std::unique_ptr<CachedGlobList> WarningAsErrorFilter; | |
| FileExtensionsSet HeaderFileExtensions; | |
| FileExtensionsSet ImplementationFileExtensions; | |
| LangOptions LangOpts; | |
| ClangTidyStats Stats; | |
| std::string CurrentBuildDirectory; | |
| llvm::DenseMap<unsigned, std::string> CheckNamesByDiagnosticID; | |
| bool Profile = false; | |
| std::string ProfilePrefix; | |
| bool AllowEnablingAnalyzerAlphaCheckers; | |
| bool EnableModuleHeadersParsing; | |
| bool SelfContainedDiags = false; | |
| NoLintDirectiveHandler NoLintHandler; | |
| llvm::StringSet<> *OptionsCollector = nullptr; | |
| }; | |
| /// Gets the Fix attached to \p Diagnostic. | |
| /// If there isn't a Fix attached to the diagnostic and \p AnyFix is true, Check | |
| /// to see if exactly one note has a Fix and return it. Otherwise return | |
| /// nullptr. | |
| const llvm::StringMap<tooling::Replacements> * | |
| getFixIt(const tooling::Diagnostic &Diagnostic, bool AnyFix); | |
| /// A diagnostic consumer that turns each \c Diagnostic into a | |
| /// \c SourceManager-independent \c ClangTidyError. | |
| // FIXME: If we move away from unit-tests, this can be moved to a private | |
| // implementation file. | |
| class ClangTidyDiagnosticConsumer : public DiagnosticConsumer { | |
| public: | |
| /// \param EnableNolintBlocks Enables diagnostic-disabling inside blocks of | |
| /// code, delimited by NOLINTBEGIN and NOLINTEND. | |
| ClangTidyDiagnosticConsumer(ClangTidyContext &Ctx, | |
| DiagnosticsEngine *ExternalDiagEngine = nullptr, | |
| bool RemoveIncompatibleErrors = true, | |
| bool GetFixesFromNotes = false, | |
| bool EnableNolintBlocks = true); | |
| // FIXME: The concept of converting between FixItHints and Replacements is | |
| // more generic and should be pulled out into a more useful Diagnostics | |
| // library. | |
| void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, | |
| const Diagnostic &Info) override; | |
| // Retrieve the diagnostics that were captured. | |
| std::vector<ClangTidyError> take(); | |
| private: | |
| void finalizeLastError(); | |
| void removeIncompatibleErrors(); | |
| void removeDuplicatedDiagnosticsOfAliasCheckers(); | |
| /// Returns the \c HeaderFilter constructed for the options set in the | |
| /// context. | |
| llvm::Regex *getHeaderFilter(); | |
| /// Updates \c LastErrorRelatesToUserCode and LastErrorPassesLineFilter | |
| /// according to the diagnostic \p Location. | |
| void checkFilters(SourceLocation Location, const SourceManager &Sources); | |
| bool passesLineFilter(StringRef FileName, unsigned LineNumber) const; | |
| void forwardDiagnostic(const Diagnostic &Info); | |
| ClangTidyContext &Context; | |
| DiagnosticsEngine *ExternalDiagEngine; | |
| bool RemoveIncompatibleErrors; | |
| bool GetFixesFromNotes; | |
| bool EnableNolintBlocks; | |
| std::vector<ClangTidyError> Errors; | |
| std::unique_ptr<llvm::Regex> HeaderFilter; | |
| bool LastErrorRelatesToUserCode = false; | |
| bool LastErrorPassesLineFilter = false; | |
| bool LastErrorWasIgnored = false; | |
| }; | |
| } // end namespace tidy | |
| } // end namespace clang | |
| #endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYDIAGNOSTICCONSUMER_H |