ClangTidyDiagnosticConsumer.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. //===--- ClangTidyDiagnosticConsumer.h - clang-tidy -------------*- C++ -*-===//
  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-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8. #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYDIAGNOSTICCONSUMER_H
  9. #define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYDIAGNOSTICCONSUMER_H
  10. #include "ClangTidyOptions.h"
  11. #include "ClangTidyProfiling.h"
  12. #include "NoLintDirectiveHandler.h"
  13. #include "clang/Basic/Diagnostic.h"
  14. #include "clang/Tooling/Core/Diagnostic.h"
  15. #include "llvm/ADT/DenseMap.h"
  16. #include "llvm/ADT/StringSet.h"
  17. #include "llvm/Support/Regex.h"
  18. #include <optional>
  19. namespace clang {
  20. class ASTContext;
  21. class SourceManager;
  22. namespace tidy {
  23. class CachedGlobList;
  24. /// A detected error complete with information to display diagnostic and
  25. /// automatic fix.
  26. ///
  27. /// This is used as an intermediate format to transport Diagnostics without a
  28. /// dependency on a SourceManager.
  29. ///
  30. /// FIXME: Make Diagnostics flexible enough to support this directly.
  31. struct ClangTidyError : tooling::Diagnostic {
  32. ClangTidyError(StringRef CheckName, Level DiagLevel, StringRef BuildDirectory,
  33. bool IsWarningAsError);
  34. bool IsWarningAsError;
  35. std::vector<std::string> EnabledDiagnosticAliases;
  36. };
  37. /// Contains displayed and ignored diagnostic counters for a ClangTidy run.
  38. struct ClangTidyStats {
  39. unsigned ErrorsDisplayed = 0;
  40. unsigned ErrorsIgnoredCheckFilter = 0;
  41. unsigned ErrorsIgnoredNOLINT = 0;
  42. unsigned ErrorsIgnoredNonUserCode = 0;
  43. unsigned ErrorsIgnoredLineFilter = 0;
  44. unsigned errorsIgnored() const {
  45. return ErrorsIgnoredNOLINT + ErrorsIgnoredCheckFilter +
  46. ErrorsIgnoredNonUserCode + ErrorsIgnoredLineFilter;
  47. }
  48. };
  49. /// Every \c ClangTidyCheck reports errors through a \c DiagnosticsEngine
  50. /// provided by this context.
  51. ///
  52. /// A \c ClangTidyCheck always has access to the active context to report
  53. /// warnings like:
  54. /// \code
  55. /// Context->Diag(Loc, "Single-argument constructors must be explicit")
  56. /// << FixItHint::CreateInsertion(Loc, "explicit ");
  57. /// \endcode
  58. class ClangTidyContext {
  59. public:
  60. /// Initializes \c ClangTidyContext instance.
  61. ClangTidyContext(std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider,
  62. bool AllowEnablingAnalyzerAlphaCheckers = false);
  63. /// Sets the DiagnosticsEngine that diag() will emit diagnostics to.
  64. // FIXME: this is required initialization, and should be a constructor param.
  65. // Fix the context -> diag engine -> consumer -> context initialization cycle.
  66. void setDiagnosticsEngine(DiagnosticsEngine *DiagEngine) {
  67. this->DiagEngine = DiagEngine;
  68. }
  69. ~ClangTidyContext();
  70. /// Report any errors detected using this method.
  71. ///
  72. /// This is still under heavy development and will likely change towards using
  73. /// tablegen'd diagnostic IDs.
  74. /// FIXME: Figure out a way to manage ID spaces.
  75. DiagnosticBuilder diag(StringRef CheckName, SourceLocation Loc,
  76. StringRef Message,
  77. DiagnosticIDs::Level Level = DiagnosticIDs::Warning);
  78. DiagnosticBuilder diag(StringRef CheckName, StringRef Message,
  79. DiagnosticIDs::Level Level = DiagnosticIDs::Warning);
  80. DiagnosticBuilder diag(const tooling::Diagnostic &Error);
  81. /// Report any errors to do with reading the configuration using this method.
  82. DiagnosticBuilder
  83. configurationDiag(StringRef Message,
  84. DiagnosticIDs::Level Level = DiagnosticIDs::Warning);
  85. /// Check whether a given diagnostic should be suppressed due to the presence
  86. /// of a "NOLINT" suppression comment.
  87. /// This is exposed so that other tools that present clang-tidy diagnostics
  88. /// (such as clangd) can respect the same suppression rules as clang-tidy.
  89. /// This does not handle suppression of notes following a suppressed
  90. /// diagnostic; that is left to the caller as it requires maintaining state in
  91. /// between calls to this function.
  92. /// If any NOLINT is malformed, e.g. a BEGIN without a subsequent END, output
  93. /// \param NoLintErrors will return an error about it.
  94. /// If \param AllowIO is false, the function does not attempt to read source
  95. /// files from disk which are not already mapped into memory; such files are
  96. /// treated as not containing a suppression comment.
  97. /// \param EnableNoLintBlocks controls whether to honor NOLINTBEGIN/NOLINTEND
  98. /// blocks; if false, only considers line-level disabling.
  99. bool
  100. shouldSuppressDiagnostic(DiagnosticsEngine::Level DiagLevel,
  101. const Diagnostic &Info,
  102. SmallVectorImpl<tooling::Diagnostic> &NoLintErrors,
  103. bool AllowIO = true, bool EnableNoLintBlocks = true);
  104. /// Sets the \c SourceManager of the used \c DiagnosticsEngine.
  105. ///
  106. /// This is called from the \c ClangTidyCheck base class.
  107. void setSourceManager(SourceManager *SourceMgr);
  108. /// Should be called when starting to process new translation unit.
  109. void setCurrentFile(StringRef File);
  110. /// Returns the main file name of the current translation unit.
  111. StringRef getCurrentFile() const { return CurrentFile; }
  112. /// Sets ASTContext for the current translation unit.
  113. void setASTContext(ASTContext *Context);
  114. /// Gets the language options from the AST context.
  115. const LangOptions &getLangOpts() const { return LangOpts; }
  116. /// Returns the name of the clang-tidy check which produced this
  117. /// diagnostic ID.
  118. std::string getCheckName(unsigned DiagnosticID) const;
  119. /// Returns \c true if the check is enabled for the \c CurrentFile.
  120. ///
  121. /// The \c CurrentFile can be changed using \c setCurrentFile.
  122. bool isCheckEnabled(StringRef CheckName) const;
  123. /// Returns \c true if the check should be upgraded to error for the
  124. /// \c CurrentFile.
  125. bool treatAsError(StringRef CheckName) const;
  126. /// Returns global options.
  127. const ClangTidyGlobalOptions &getGlobalOptions() const;
  128. /// Returns options for \c CurrentFile.
  129. ///
  130. /// The \c CurrentFile can be changed using \c setCurrentFile.
  131. const ClangTidyOptions &getOptions() const;
  132. /// Returns options for \c File. Does not change or depend on
  133. /// \c CurrentFile.
  134. ClangTidyOptions getOptionsForFile(StringRef File) const;
  135. /// Returns \c ClangTidyStats containing issued and ignored diagnostic
  136. /// counters.
  137. const ClangTidyStats &getStats() const { return Stats; }
  138. /// Control profile collection in clang-tidy.
  139. void setEnableProfiling(bool Profile);
  140. bool getEnableProfiling() const { return Profile; }
  141. /// Control storage of profile date.
  142. void setProfileStoragePrefix(StringRef ProfilePrefix);
  143. std::optional<ClangTidyProfiling::StorageParams>
  144. getProfileStorageParams() const;
  145. /// Should be called when starting to process new translation unit.
  146. void setCurrentBuildDirectory(StringRef BuildDirectory) {
  147. CurrentBuildDirectory = std::string(BuildDirectory);
  148. }
  149. /// Returns build directory of the current translation unit.
  150. const std::string &getCurrentBuildDirectory() const {
  151. return CurrentBuildDirectory;
  152. }
  153. /// If the experimental alpha checkers from the static analyzer can be
  154. /// enabled.
  155. bool canEnableAnalyzerAlphaCheckers() const {
  156. return AllowEnablingAnalyzerAlphaCheckers;
  157. }
  158. void setSelfContainedDiags(bool Value) { SelfContainedDiags = Value; }
  159. bool areDiagsSelfContained() const { return SelfContainedDiags; }
  160. using DiagLevelAndFormatString = std::pair<DiagnosticIDs::Level, std::string>;
  161. DiagLevelAndFormatString getDiagLevelAndFormatString(unsigned DiagnosticID,
  162. SourceLocation Loc) {
  163. return DiagLevelAndFormatString(
  164. static_cast<DiagnosticIDs::Level>(
  165. DiagEngine->getDiagnosticLevel(DiagnosticID, Loc)),
  166. std::string(
  167. DiagEngine->getDiagnosticIDs()->getDescription(DiagnosticID)));
  168. }
  169. void setOptionsCollector(llvm::StringSet<> *Collector) {
  170. OptionsCollector = Collector;
  171. }
  172. llvm::StringSet<> *getOptionsCollector() const { return OptionsCollector; }
  173. private:
  174. // Writes to Stats.
  175. friend class ClangTidyDiagnosticConsumer;
  176. DiagnosticsEngine *DiagEngine;
  177. std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider;
  178. std::string CurrentFile;
  179. ClangTidyOptions CurrentOptions;
  180. std::unique_ptr<CachedGlobList> CheckFilter;
  181. std::unique_ptr<CachedGlobList> WarningAsErrorFilter;
  182. LangOptions LangOpts;
  183. ClangTidyStats Stats;
  184. std::string CurrentBuildDirectory;
  185. llvm::DenseMap<unsigned, std::string> CheckNamesByDiagnosticID;
  186. bool Profile;
  187. std::string ProfilePrefix;
  188. bool AllowEnablingAnalyzerAlphaCheckers;
  189. bool SelfContainedDiags;
  190. NoLintDirectiveHandler NoLintHandler;
  191. llvm::StringSet<> *OptionsCollector = nullptr;
  192. };
  193. /// Gets the Fix attached to \p Diagnostic.
  194. /// If there isn't a Fix attached to the diagnostic and \p AnyFix is true, Check
  195. /// to see if exactly one note has a Fix and return it. Otherwise return
  196. /// nullptr.
  197. const llvm::StringMap<tooling::Replacements> *
  198. getFixIt(const tooling::Diagnostic &Diagnostic, bool AnyFix);
  199. /// A diagnostic consumer that turns each \c Diagnostic into a
  200. /// \c SourceManager-independent \c ClangTidyError.
  201. // FIXME: If we move away from unit-tests, this can be moved to a private
  202. // implementation file.
  203. class ClangTidyDiagnosticConsumer : public DiagnosticConsumer {
  204. public:
  205. /// \param EnableNolintBlocks Enables diagnostic-disabling inside blocks of
  206. /// code, delimited by NOLINTBEGIN and NOLINTEND.
  207. ClangTidyDiagnosticConsumer(ClangTidyContext &Ctx,
  208. DiagnosticsEngine *ExternalDiagEngine = nullptr,
  209. bool RemoveIncompatibleErrors = true,
  210. bool GetFixesFromNotes = false,
  211. bool EnableNolintBlocks = true);
  212. // FIXME: The concept of converting between FixItHints and Replacements is
  213. // more generic and should be pulled out into a more useful Diagnostics
  214. // library.
  215. void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
  216. const Diagnostic &Info) override;
  217. // Retrieve the diagnostics that were captured.
  218. std::vector<ClangTidyError> take();
  219. private:
  220. void finalizeLastError();
  221. void removeIncompatibleErrors();
  222. void removeDuplicatedDiagnosticsOfAliasCheckers();
  223. /// Returns the \c HeaderFilter constructed for the options set in the
  224. /// context.
  225. llvm::Regex *getHeaderFilter();
  226. /// Updates \c LastErrorRelatesToUserCode and LastErrorPassesLineFilter
  227. /// according to the diagnostic \p Location.
  228. void checkFilters(SourceLocation Location, const SourceManager &Sources);
  229. bool passesLineFilter(StringRef FileName, unsigned LineNumber) const;
  230. void forwardDiagnostic(const Diagnostic &Info);
  231. ClangTidyContext &Context;
  232. DiagnosticsEngine *ExternalDiagEngine;
  233. bool RemoveIncompatibleErrors;
  234. bool GetFixesFromNotes;
  235. bool EnableNolintBlocks;
  236. std::vector<ClangTidyError> Errors;
  237. std::unique_ptr<llvm::Regex> HeaderFilter;
  238. bool LastErrorRelatesToUserCode;
  239. bool LastErrorPassesLineFilter;
  240. bool LastErrorWasIgnored;
  241. };
  242. } // end namespace tidy
  243. } // end namespace clang
  244. #endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYDIAGNOSTICCONSUMER_H