brintos

brintos / llvm-project-archived public Read only

0
0
Text · 13.1 KiB · 21ffd9d Raw
354 lines · c
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYDIAGNOSTICCONSUMER_H10#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYDIAGNOSTICCONSUMER_H11 12#include "ClangTidyOptions.h"13#include "ClangTidyProfiling.h"14#include "FileExtensionsSet.h"15#include "NoLintDirectiveHandler.h"16#include "clang/Basic/Diagnostic.h"17#include "clang/Tooling/Core/Diagnostic.h"18#include "llvm/ADT/DenseMap.h"19#include "llvm/ADT/StringSet.h"20#include "llvm/Support/Regex.h"21#include <optional>22#include <utility>23 24namespace clang {25 26class ASTContext;27class SourceManager;28 29namespace tidy {30class CachedGlobList;31 32/// A detected error complete with information to display diagnostic and33/// automatic fix.34///35/// This is used as an intermediate format to transport Diagnostics without a36/// dependency on a SourceManager.37///38/// FIXME: Make Diagnostics flexible enough to support this directly.39struct ClangTidyError : tooling::Diagnostic {40  ClangTidyError(StringRef CheckName, Level DiagLevel, StringRef BuildDirectory,41                 bool IsWarningAsError);42 43  bool IsWarningAsError;44  std::vector<std::string> EnabledDiagnosticAliases;45};46 47/// Contains displayed and ignored diagnostic counters for a ClangTidy run.48struct ClangTidyStats {49  unsigned ErrorsDisplayed = 0;50  unsigned ErrorsIgnoredCheckFilter = 0;51  unsigned ErrorsIgnoredNOLINT = 0;52  unsigned ErrorsIgnoredNonUserCode = 0;53  unsigned ErrorsIgnoredLineFilter = 0;54 55  unsigned errorsIgnored() const {56    return ErrorsIgnoredNOLINT + ErrorsIgnoredCheckFilter +57           ErrorsIgnoredNonUserCode + ErrorsIgnoredLineFilter;58  }59};60 61/// Every \c ClangTidyCheck reports errors through a \c DiagnosticsEngine62/// provided by this context.63///64/// A \c ClangTidyCheck always has access to the active context to report65/// warnings like:66/// \code67/// Context->Diag(Loc, "Single-argument constructors must be explicit")68///     << FixItHint::CreateInsertion(Loc, "explicit ");69/// \endcode70class ClangTidyContext {71public:72  ClangTidyContext(std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider)73      : ClangTidyContext(std::move(OptionsProvider), false, false, false) {}74  /// Initializes \c ClangTidyContext instance.75  ClangTidyContext(std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider,76                   bool AllowEnablingAnalyzerAlphaCheckers,77                   bool EnableModuleHeadersParsing,78                   bool ExperimentalCustomChecks);79  /// Sets the DiagnosticsEngine that diag() will emit diagnostics to.80  // FIXME: this is required initialization, and should be a constructor param.81  // Fix the context -> diag engine -> consumer -> context initialization cycle.82  void setDiagnosticsEngine(std::unique_ptr<DiagnosticOptions> DiagOpts,83                            DiagnosticsEngine *DiagEngine) {84    this->DiagOpts = std::move(DiagOpts);85    this->DiagEngine = DiagEngine;86  }87 88  ~ClangTidyContext();89 90  ClangTidyContext(const ClangTidyContext &) = delete;91  ClangTidyContext &operator=(const ClangTidyContext &) = delete;92 93  /// Report any errors detected using this method.94  ///95  /// This is still under heavy development and will likely change towards using96  /// tablegen'd diagnostic IDs.97  /// FIXME: Figure out a way to manage ID spaces.98  DiagnosticBuilder diag(StringRef CheckName, SourceLocation Loc,99                         StringRef Description,100                         DiagnosticIDs::Level Level = DiagnosticIDs::Warning);101 102  DiagnosticBuilder diag(StringRef CheckName, StringRef Description,103                         DiagnosticIDs::Level Level = DiagnosticIDs::Warning);104 105  DiagnosticBuilder diag(const tooling::Diagnostic &Error);106 107  /// Report any errors to do with reading the configuration using this method.108  DiagnosticBuilder109  configurationDiag(StringRef Message,110                    DiagnosticIDs::Level Level = DiagnosticIDs::Warning);111 112  /// Check whether a given diagnostic should be suppressed due to the presence113  /// of a "NOLINT" suppression comment.114  /// This is exposed so that other tools that present clang-tidy diagnostics115  /// (such as clangd) can respect the same suppression rules as clang-tidy.116  /// This does not handle suppression of notes following a suppressed117  /// diagnostic; that is left to the caller as it requires maintaining state in118  /// between calls to this function.119  /// If any NOLINT is malformed, e.g. a BEGIN without a subsequent END, output120  /// \param NoLintErrors will return an error about it.121  /// If \param AllowIO is false, the function does not attempt to read source122  /// files from disk which are not already mapped into memory; such files are123  /// treated as not containing a suppression comment.124  /// \param EnableNoLintBlocks controls whether to honor NOLINTBEGIN/NOLINTEND125  /// blocks; if false, only considers line-level disabling.126  bool127  shouldSuppressDiagnostic(DiagnosticsEngine::Level DiagLevel,128                           const Diagnostic &Info,129                           SmallVectorImpl<tooling::Diagnostic> &NoLintErrors,130                           bool AllowIO = true, bool EnableNoLintBlocks = true);131 132  /// Sets the \c SourceManager of the used \c DiagnosticsEngine.133  ///134  /// This is called from the \c ClangTidyCheck base class.135  void setSourceManager(SourceManager *SourceMgr);136 137  /// Should be called when starting to process new translation unit.138  void setCurrentFile(StringRef File);139 140  /// Returns the main file name of the current translation unit.141  StringRef getCurrentFile() const { return CurrentFile; }142 143  /// Sets ASTContext for the current translation unit.144  void setASTContext(ASTContext *Context);145 146  /// Gets the language options from the AST context.147  const LangOptions &getLangOpts() const { return LangOpts; }148 149  /// Returns the name of the clang-tidy check which produced this150  /// diagnostic ID.151  std::string getCheckName(unsigned DiagnosticID) const;152 153  /// Returns \c true if the check is enabled for the \c CurrentFile.154  ///155  /// The \c CurrentFile can be changed using \c setCurrentFile.156  bool isCheckEnabled(StringRef CheckName) const;157 158  /// Returns \c true if the check should be upgraded to error for the159  /// \c CurrentFile.160  bool treatAsError(StringRef CheckName) const;161 162  /// Returns global options.163  const ClangTidyGlobalOptions &getGlobalOptions() const;164 165  /// Returns options for \c CurrentFile.166  ///167  /// The \c CurrentFile can be changed using \c setCurrentFile.168  const ClangTidyOptions &getOptions() const;169 170  /// Returns options for \c File. Does not change or depend on171  /// \c CurrentFile.172  ClangTidyOptions getOptionsForFile(StringRef File) const;173 174  const FileExtensionsSet &getHeaderFileExtensions() const {175    return HeaderFileExtensions;176  }177 178  const FileExtensionsSet &getImplementationFileExtensions() const {179    return ImplementationFileExtensions;180  }181 182  /// Returns \c ClangTidyStats containing issued and ignored diagnostic183  /// counters.184  const ClangTidyStats &getStats() const { return Stats; }185 186  /// Control profile collection in clang-tidy.187  void setEnableProfiling(bool Profile);188  bool getEnableProfiling() const { return Profile; }189 190  /// Control storage of profile date.191  void setProfileStoragePrefix(StringRef ProfilePrefix);192  std::optional<ClangTidyProfiling::StorageParams>193  getProfileStorageParams() const;194 195  /// Should be called when starting to process new translation unit.196  void setCurrentBuildDirectory(StringRef BuildDirectory) {197    CurrentBuildDirectory = std::string(BuildDirectory);198  }199 200  /// Returns build directory of the current translation unit.201  const std::string &getCurrentBuildDirectory() const {202    return CurrentBuildDirectory;203  }204 205  /// If the experimental alpha checkers from the static analyzer can be206  /// enabled.207  bool canEnableAnalyzerAlphaCheckers() const {208    return AllowEnablingAnalyzerAlphaCheckers;209  }210 211  // This method determines whether preprocessor-level module header parsing is212  // enabled using the `--experimental-enable-module-headers-parsing` option.213  bool canEnableModuleHeadersParsing() const {214    return EnableModuleHeadersParsing;215  }216 217  // whether experimental custom checks can be enabled.218  // enabled with `--experimental-custom-checks`219  bool canExperimentalCustomChecks() const { return ExperimentalCustomChecks; }220 221  void setSelfContainedDiags(bool Value) { SelfContainedDiags = Value; }222 223  bool areDiagsSelfContained() const { return SelfContainedDiags; }224 225  using DiagLevelAndFormatString = std::pair<DiagnosticIDs::Level, std::string>;226  DiagLevelAndFormatString getDiagLevelAndFormatString(unsigned DiagnosticID,227                                                       SourceLocation Loc) {228    return {static_cast<DiagnosticIDs::Level>(229                DiagEngine->getDiagnosticLevel(DiagnosticID, Loc)),230            std::string(231                DiagEngine->getDiagnosticIDs()->getDescription(DiagnosticID))};232  }233 234  void setOptionsCollector(llvm::StringSet<> *Collector) {235    OptionsCollector = Collector;236  }237  llvm::StringSet<> *getOptionsCollector() const { return OptionsCollector; }238 239private:240  // Writes to Stats.241  friend class ClangTidyDiagnosticConsumer;242 243  std::unique_ptr<DiagnosticOptions> DiagOpts = nullptr;244  DiagnosticsEngine *DiagEngine = nullptr;245  std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider;246 247  std::string CurrentFile;248  ClangTidyOptions CurrentOptions;249 250  std::unique_ptr<CachedGlobList> CheckFilter;251  std::unique_ptr<CachedGlobList> WarningAsErrorFilter;252 253  FileExtensionsSet HeaderFileExtensions;254  FileExtensionsSet ImplementationFileExtensions;255 256  LangOptions LangOpts;257 258  ClangTidyStats Stats;259 260  std::string CurrentBuildDirectory;261 262  llvm::DenseMap<unsigned, std::string> CheckNamesByDiagnosticID;263 264  bool Profile = false;265  std::string ProfilePrefix;266 267  bool AllowEnablingAnalyzerAlphaCheckers;268  bool EnableModuleHeadersParsing;269  bool ExperimentalCustomChecks;270 271  bool SelfContainedDiags = false;272 273  NoLintDirectiveHandler NoLintHandler;274  llvm::StringSet<> *OptionsCollector = nullptr;275};276 277/// Gets the Fix attached to \p Diagnostic.278/// If there isn't a Fix attached to the diagnostic and \p AnyFix is true, Check279/// to see if exactly one note has a Fix and return it. Otherwise return280/// nullptr.281const llvm::StringMap<tooling::Replacements> *282getFixIt(const tooling::Diagnostic &Diagnostic, bool AnyFix);283 284/// A diagnostic consumer that turns each \c Diagnostic into a285/// \c SourceManager-independent \c ClangTidyError.286// FIXME: If we move away from unit-tests, this can be moved to a private287// implementation file.288class ClangTidyDiagnosticConsumer : public DiagnosticConsumer {289public:290  /// \param EnableNolintBlocks Enables diagnostic-disabling inside blocks of291  /// code, delimited by NOLINTBEGIN and NOLINTEND.292  ClangTidyDiagnosticConsumer(ClangTidyContext &Ctx,293                              DiagnosticsEngine *ExternalDiagEngine = nullptr,294                              bool RemoveIncompatibleErrors = true,295                              bool GetFixesFromNotes = false,296                              bool EnableNolintBlocks = true);297 298  // FIXME: The concept of converting between FixItHints and Replacements is299  // more generic and should be pulled out into a more useful Diagnostics300  // library.301  void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,302                        const Diagnostic &Info) override;303 304  void BeginSourceFile(const LangOptions &LangOpts,305                       const Preprocessor *PP = nullptr) override;306 307  void EndSourceFile() override;308 309  // Retrieve the diagnostics that were captured.310  std::vector<ClangTidyError> take();311 312private:313  void finalizeLastError();314  void removeIncompatibleErrors();315  void removeDuplicatedDiagnosticsOfAliasCheckers();316 317  /// Returns the \c HeaderFilter constructed for the options set in the318  /// context.319  llvm::Regex *getHeaderFilter();320 321  /// Returns the \c ExcludeHeaderFilter constructed for the options set in the322  /// context.323  llvm::Regex *getExcludeHeaderFilter();324 325  /// Updates \c LastErrorRelatesToUserCode and LastErrorPassesLineFilter326  /// according to the diagnostic \p Location.327  void checkFilters(SourceLocation Location, const SourceManager &Sources);328  bool passesLineFilter(StringRef FileName, unsigned LineNumber) const;329 330  void forwardDiagnostic(const Diagnostic &Info);331 332  ClangTidyContext &Context;333  DiagnosticsEngine *ExternalDiagEngine;334  bool RemoveIncompatibleErrors;335  bool GetFixesFromNotes;336  bool EnableNolintBlocks;337  std::vector<ClangTidyError> Errors;338  std::unique_ptr<llvm::Regex> HeaderFilter;339  std::unique_ptr<llvm::Regex> ExcludeHeaderFilter;340  bool LastErrorRelatesToUserCode = false;341  bool LastErrorPassesLineFilter = false;342  bool LastErrorWasIgnored = false;343  /// Tracks whether we're currently inside a344  /// `BeginSourceFile()/EndSourceFile()` pair. Outside of a source file, we345  /// should only receive diagnostics that have to source location, such as346  /// command-line warnings.347  bool InSourceFile = false;348};349 350} // end namespace tidy351} // end namespace clang352 353#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYDIAGNOSTICCONSUMER_H354