123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304 |
- //===--- 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 "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);
- /// 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 Message,
- DiagnosticIDs::Level Level = DiagnosticIDs::Warning);
- DiagnosticBuilder diag(StringRef CheckName, StringRef Message,
- 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;
- /// 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;
- }
- 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 DiagLevelAndFormatString(
- 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;
- std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider;
- std::string CurrentFile;
- ClangTidyOptions CurrentOptions;
- std::unique_ptr<CachedGlobList> CheckFilter;
- std::unique_ptr<CachedGlobList> WarningAsErrorFilter;
- LangOptions LangOpts;
- ClangTidyStats Stats;
- std::string CurrentBuildDirectory;
- llvm::DenseMap<unsigned, std::string> CheckNamesByDiagnosticID;
- bool Profile;
- std::string ProfilePrefix;
- bool AllowEnablingAnalyzerAlphaCheckers;
- bool SelfContainedDiags;
- 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;
- bool LastErrorPassesLineFilter;
- bool LastErrorWasIgnored;
- };
- } // end namespace tidy
- } // end namespace clang
- #endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYDIAGNOSTICCONSUMER_H
|