Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-05-10 08:36:23

0001 //===--- ClangTidyDiagnosticConsumer.h - clang-tidy -------------*- C++ -*-===//
0002 //
0003 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
0004 // See https://llvm.org/LICENSE.txt for license information.
0005 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
0006 //
0007 //===----------------------------------------------------------------------===//
0008 
0009 #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYDIAGNOSTICCONSUMER_H
0010 #define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYDIAGNOSTICCONSUMER_H
0011 
0012 #include "ClangTidyOptions.h"
0013 #include "ClangTidyProfiling.h"
0014 #include "FileExtensionsSet.h"
0015 #include "NoLintDirectiveHandler.h"
0016 #include "clang/Basic/Diagnostic.h"
0017 #include "clang/Tooling/Core/Diagnostic.h"
0018 #include "llvm/ADT/DenseMap.h"
0019 #include "llvm/ADT/StringSet.h"
0020 #include "llvm/Support/Regex.h"
0021 #include <optional>
0022 
0023 namespace clang {
0024 
0025 class ASTContext;
0026 class SourceManager;
0027 
0028 namespace tidy {
0029 class CachedGlobList;
0030 
0031 /// A detected error complete with information to display diagnostic and
0032 /// automatic fix.
0033 ///
0034 /// This is used as an intermediate format to transport Diagnostics without a
0035 /// dependency on a SourceManager.
0036 ///
0037 /// FIXME: Make Diagnostics flexible enough to support this directly.
0038 struct ClangTidyError : tooling::Diagnostic {
0039   ClangTidyError(StringRef CheckName, Level DiagLevel, StringRef BuildDirectory,
0040                  bool IsWarningAsError);
0041 
0042   bool IsWarningAsError;
0043   std::vector<std::string> EnabledDiagnosticAliases;
0044 };
0045 
0046 /// Contains displayed and ignored diagnostic counters for a ClangTidy run.
0047 struct ClangTidyStats {
0048   unsigned ErrorsDisplayed = 0;
0049   unsigned ErrorsIgnoredCheckFilter = 0;
0050   unsigned ErrorsIgnoredNOLINT = 0;
0051   unsigned ErrorsIgnoredNonUserCode = 0;
0052   unsigned ErrorsIgnoredLineFilter = 0;
0053 
0054   unsigned errorsIgnored() const {
0055     return ErrorsIgnoredNOLINT + ErrorsIgnoredCheckFilter +
0056            ErrorsIgnoredNonUserCode + ErrorsIgnoredLineFilter;
0057   }
0058 };
0059 
0060 /// Every \c ClangTidyCheck reports errors through a \c DiagnosticsEngine
0061 /// provided by this context.
0062 ///
0063 /// A \c ClangTidyCheck always has access to the active context to report
0064 /// warnings like:
0065 /// \code
0066 /// Context->Diag(Loc, "Single-argument constructors must be explicit")
0067 ///     << FixItHint::CreateInsertion(Loc, "explicit ");
0068 /// \endcode
0069 class ClangTidyContext {
0070 public:
0071   /// Initializes \c ClangTidyContext instance.
0072   ClangTidyContext(std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider,
0073                    bool AllowEnablingAnalyzerAlphaCheckers = false,
0074                    bool EnableModuleHeadersParsing = false);
0075   /// Sets the DiagnosticsEngine that diag() will emit diagnostics to.
0076   // FIXME: this is required initialization, and should be a constructor param.
0077   // Fix the context -> diag engine -> consumer -> context initialization cycle.
0078   void setDiagnosticsEngine(DiagnosticsEngine *DiagEngine) {
0079     this->DiagEngine = DiagEngine;
0080   }
0081 
0082   ~ClangTidyContext();
0083 
0084   ClangTidyContext(const ClangTidyContext &) = delete;
0085   ClangTidyContext &operator=(const ClangTidyContext &) = delete;
0086 
0087   /// Report any errors detected using this method.
0088   ///
0089   /// This is still under heavy development and will likely change towards using
0090   /// tablegen'd diagnostic IDs.
0091   /// FIXME: Figure out a way to manage ID spaces.
0092   DiagnosticBuilder diag(StringRef CheckName, SourceLocation Loc,
0093                          StringRef Description,
0094                          DiagnosticIDs::Level Level = DiagnosticIDs::Warning);
0095 
0096   DiagnosticBuilder diag(StringRef CheckName, StringRef Description,
0097                          DiagnosticIDs::Level Level = DiagnosticIDs::Warning);
0098 
0099   DiagnosticBuilder diag(const tooling::Diagnostic &Error);
0100 
0101   /// Report any errors to do with reading the configuration using this method.
0102   DiagnosticBuilder
0103   configurationDiag(StringRef Message,
0104                     DiagnosticIDs::Level Level = DiagnosticIDs::Warning);
0105 
0106   /// Check whether a given diagnostic should be suppressed due to the presence
0107   /// of a "NOLINT" suppression comment.
0108   /// This is exposed so that other tools that present clang-tidy diagnostics
0109   /// (such as clangd) can respect the same suppression rules as clang-tidy.
0110   /// This does not handle suppression of notes following a suppressed
0111   /// diagnostic; that is left to the caller as it requires maintaining state in
0112   /// between calls to this function.
0113   /// If any NOLINT is malformed, e.g. a BEGIN without a subsequent END, output
0114   /// \param NoLintErrors will return an error about it.
0115   /// If \param AllowIO is false, the function does not attempt to read source
0116   /// files from disk which are not already mapped into memory; such files are
0117   /// treated as not containing a suppression comment.
0118   /// \param EnableNoLintBlocks controls whether to honor NOLINTBEGIN/NOLINTEND
0119   /// blocks; if false, only considers line-level disabling.
0120   bool
0121   shouldSuppressDiagnostic(DiagnosticsEngine::Level DiagLevel,
0122                            const Diagnostic &Info,
0123                            SmallVectorImpl<tooling::Diagnostic> &NoLintErrors,
0124                            bool AllowIO = true, bool EnableNoLintBlocks = true);
0125 
0126   /// Sets the \c SourceManager of the used \c DiagnosticsEngine.
0127   ///
0128   /// This is called from the \c ClangTidyCheck base class.
0129   void setSourceManager(SourceManager *SourceMgr);
0130 
0131   /// Should be called when starting to process new translation unit.
0132   void setCurrentFile(StringRef File);
0133 
0134   /// Returns the main file name of the current translation unit.
0135   StringRef getCurrentFile() const { return CurrentFile; }
0136 
0137   /// Sets ASTContext for the current translation unit.
0138   void setASTContext(ASTContext *Context);
0139 
0140   /// Gets the language options from the AST context.
0141   const LangOptions &getLangOpts() const { return LangOpts; }
0142 
0143   /// Returns the name of the clang-tidy check which produced this
0144   /// diagnostic ID.
0145   std::string getCheckName(unsigned DiagnosticID) const;
0146 
0147   /// Returns \c true if the check is enabled for the \c CurrentFile.
0148   ///
0149   /// The \c CurrentFile can be changed using \c setCurrentFile.
0150   bool isCheckEnabled(StringRef CheckName) const;
0151 
0152   /// Returns \c true if the check should be upgraded to error for the
0153   /// \c CurrentFile.
0154   bool treatAsError(StringRef CheckName) const;
0155 
0156   /// Returns global options.
0157   const ClangTidyGlobalOptions &getGlobalOptions() const;
0158 
0159   /// Returns options for \c CurrentFile.
0160   ///
0161   /// The \c CurrentFile can be changed using \c setCurrentFile.
0162   const ClangTidyOptions &getOptions() const;
0163 
0164   /// Returns options for \c File. Does not change or depend on
0165   /// \c CurrentFile.
0166   ClangTidyOptions getOptionsForFile(StringRef File) const;
0167 
0168   const FileExtensionsSet &getHeaderFileExtensions() const {
0169     return HeaderFileExtensions;
0170   }
0171 
0172   const FileExtensionsSet &getImplementationFileExtensions() const {
0173     return ImplementationFileExtensions;
0174   }
0175 
0176   /// Returns \c ClangTidyStats containing issued and ignored diagnostic
0177   /// counters.
0178   const ClangTidyStats &getStats() const { return Stats; }
0179 
0180   /// Control profile collection in clang-tidy.
0181   void setEnableProfiling(bool Profile);
0182   bool getEnableProfiling() const { return Profile; }
0183 
0184   /// Control storage of profile date.
0185   void setProfileStoragePrefix(StringRef ProfilePrefix);
0186   std::optional<ClangTidyProfiling::StorageParams>
0187   getProfileStorageParams() const;
0188 
0189   /// Should be called when starting to process new translation unit.
0190   void setCurrentBuildDirectory(StringRef BuildDirectory) {
0191     CurrentBuildDirectory = std::string(BuildDirectory);
0192   }
0193 
0194   /// Returns build directory of the current translation unit.
0195   const std::string &getCurrentBuildDirectory() const {
0196     return CurrentBuildDirectory;
0197   }
0198 
0199   /// If the experimental alpha checkers from the static analyzer can be
0200   /// enabled.
0201   bool canEnableAnalyzerAlphaCheckers() const {
0202     return AllowEnablingAnalyzerAlphaCheckers;
0203   }
0204 
0205   // This method determines whether preprocessor-level module header parsing is
0206   // enabled using the `--experimental-enable-module-headers-parsing` option.
0207   bool canEnableModuleHeadersParsing() const {
0208     return EnableModuleHeadersParsing;
0209   }
0210 
0211   void setSelfContainedDiags(bool Value) { SelfContainedDiags = Value; }
0212 
0213   bool areDiagsSelfContained() const { return SelfContainedDiags; }
0214 
0215   using DiagLevelAndFormatString = std::pair<DiagnosticIDs::Level, std::string>;
0216   DiagLevelAndFormatString getDiagLevelAndFormatString(unsigned DiagnosticID,
0217                                                        SourceLocation Loc) {
0218     return {
0219         static_cast<DiagnosticIDs::Level>(
0220             DiagEngine->getDiagnosticLevel(DiagnosticID, Loc)),
0221         std::string(
0222             DiagEngine->getDiagnosticIDs()->getDescription(DiagnosticID))};
0223   }
0224 
0225   void setOptionsCollector(llvm::StringSet<> *Collector) {
0226     OptionsCollector = Collector;
0227   }
0228   llvm::StringSet<> *getOptionsCollector() const { return OptionsCollector; }
0229 
0230 private:
0231   // Writes to Stats.
0232   friend class ClangTidyDiagnosticConsumer;
0233 
0234   DiagnosticsEngine *DiagEngine = nullptr;
0235   std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider;
0236 
0237   std::string CurrentFile;
0238   ClangTidyOptions CurrentOptions;
0239 
0240   std::unique_ptr<CachedGlobList> CheckFilter;
0241   std::unique_ptr<CachedGlobList> WarningAsErrorFilter;
0242 
0243   FileExtensionsSet HeaderFileExtensions;
0244   FileExtensionsSet ImplementationFileExtensions;
0245 
0246   LangOptions LangOpts;
0247 
0248   ClangTidyStats Stats;
0249 
0250   std::string CurrentBuildDirectory;
0251 
0252   llvm::DenseMap<unsigned, std::string> CheckNamesByDiagnosticID;
0253 
0254   bool Profile = false;
0255   std::string ProfilePrefix;
0256 
0257   bool AllowEnablingAnalyzerAlphaCheckers;
0258   bool EnableModuleHeadersParsing;
0259 
0260   bool SelfContainedDiags = false;
0261 
0262   NoLintDirectiveHandler NoLintHandler;
0263   llvm::StringSet<> *OptionsCollector = nullptr;
0264 };
0265 
0266 /// Gets the Fix attached to \p Diagnostic.
0267 /// If there isn't a Fix attached to the diagnostic and \p AnyFix is true, Check
0268 /// to see if exactly one note has a Fix and return it. Otherwise return
0269 /// nullptr.
0270 const llvm::StringMap<tooling::Replacements> *
0271 getFixIt(const tooling::Diagnostic &Diagnostic, bool AnyFix);
0272 
0273 /// A diagnostic consumer that turns each \c Diagnostic into a
0274 /// \c SourceManager-independent \c ClangTidyError.
0275 // FIXME: If we move away from unit-tests, this can be moved to a private
0276 // implementation file.
0277 class ClangTidyDiagnosticConsumer : public DiagnosticConsumer {
0278 public:
0279   /// \param EnableNolintBlocks Enables diagnostic-disabling inside blocks of
0280   /// code, delimited by NOLINTBEGIN and NOLINTEND.
0281   ClangTidyDiagnosticConsumer(ClangTidyContext &Ctx,
0282                               DiagnosticsEngine *ExternalDiagEngine = nullptr,
0283                               bool RemoveIncompatibleErrors = true,
0284                               bool GetFixesFromNotes = false,
0285                               bool EnableNolintBlocks = true);
0286 
0287   // FIXME: The concept of converting between FixItHints and Replacements is
0288   // more generic and should be pulled out into a more useful Diagnostics
0289   // library.
0290   void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
0291                         const Diagnostic &Info) override;
0292 
0293   // Retrieve the diagnostics that were captured.
0294   std::vector<ClangTidyError> take();
0295 
0296 private:
0297   void finalizeLastError();
0298   void removeIncompatibleErrors();
0299   void removeDuplicatedDiagnosticsOfAliasCheckers();
0300 
0301   /// Returns the \c HeaderFilter constructed for the options set in the
0302   /// context.
0303   llvm::Regex *getHeaderFilter();
0304 
0305   /// Returns the \c ExcludeHeaderFilter constructed for the options set in the
0306   /// context.
0307   llvm::Regex *getExcludeHeaderFilter();
0308 
0309   /// Updates \c LastErrorRelatesToUserCode and LastErrorPassesLineFilter
0310   /// according to the diagnostic \p Location.
0311   void checkFilters(SourceLocation Location, const SourceManager &Sources);
0312   bool passesLineFilter(StringRef FileName, unsigned LineNumber) const;
0313 
0314   void forwardDiagnostic(const Diagnostic &Info);
0315 
0316   ClangTidyContext &Context;
0317   DiagnosticsEngine *ExternalDiagEngine;
0318   bool RemoveIncompatibleErrors;
0319   bool GetFixesFromNotes;
0320   bool EnableNolintBlocks;
0321   std::vector<ClangTidyError> Errors;
0322   std::unique_ptr<llvm::Regex> HeaderFilter;
0323   std::unique_ptr<llvm::Regex> ExcludeHeaderFilter;
0324   bool LastErrorRelatesToUserCode = false;
0325   bool LastErrorPassesLineFilter = false;
0326   bool LastErrorWasIgnored = false;
0327 };
0328 
0329 } // end namespace tidy
0330 } // end namespace clang
0331 
0332 #endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYDIAGNOSTICCONSUMER_H