clang-tools 22.0.0git
ClangTidyDiagnosticConsumer.h
Go to the documentation of this file.
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-exception
6//
7//===----------------------------------------------------------------------===//
8
9#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYDIAGNOSTICCONSUMER_H
10#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYDIAGNOSTICCONSUMER_H
11
12#include "ClangTidyOptions.h"
13#include "ClangTidyProfiling.h"
14#include "FileExtensionsSet.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 and
33/// automatic fix.
34///
35/// This is used as an intermediate format to transport Diagnostics without a
36/// 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
44 std::vector<std::string> EnabledDiagnosticAliases;
45};
46
47/// Contains displayed and ignored diagnostic counters for a ClangTidy run.
60
61/// Every \c ClangTidyCheck reports errors through a \c DiagnosticsEngine
62/// provided by this context.
63///
64/// A \c ClangTidyCheck always has access to the active context to report
65/// warnings like:
66/// \code
67/// Context->Diag(Loc, "Single-argument constructors must be explicit")
68/// << FixItHint::CreateInsertion(Loc, "explicit ");
69/// \endcode
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,
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
89
92
93 /// Report any errors detected using this method.
94 ///
95 /// This is still under heavy development and will likely change towards using
96 /// 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 DiagnosticBuilder
109 configurationDiag(StringRef Message,
110 DiagnosticIDs::Level Level = DiagnosticIDs::Warning);
111
112 /// Check whether a given diagnostic should be suppressed due to the presence
113 /// of a "NOLINT" suppression comment.
114 /// This is exposed so that other tools that present clang-tidy diagnostics
115 /// (such as clangd) can respect the same suppression rules as clang-tidy.
116 /// This does not handle suppression of notes following a suppressed
117 /// diagnostic; that is left to the caller as it requires maintaining state in
118 /// between calls to this function.
119 /// If any NOLINT is malformed, e.g. a BEGIN without a subsequent END, output
120 /// \param NoLintErrors will return an error about it.
121 /// If \param AllowIO is false, the function does not attempt to read source
122 /// files from disk which are not already mapped into memory; such files are
123 /// treated as not containing a suppression comment.
124 /// \param EnableNoLintBlocks controls whether to honor NOLINTBEGIN/NOLINTEND
125 /// blocks; if false, only considers line-level disabling.
126 bool
127 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 this
150 /// 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 the
159 /// \c CurrentFile.
160 bool treatAsError(StringRef CheckName) const;
161
162 /// Returns global options.
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 on
171 /// \c CurrentFile.
172 ClangTidyOptions getOptionsForFile(StringRef File) const;
173
175 return HeaderFileExtensions;
176 }
177
179 return ImplementationFileExtensions;
180 }
181
182 /// Returns \c ClangTidyStats containing issued and ignored diagnostic
183 /// 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>
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 be
206 /// enabled.
208 return AllowEnablingAnalyzerAlphaCheckers;
209 }
210
211 // This method determines whether preprocessor-level module header parsing is
212 // enabled using the `--experimental-enable-module-headers-parsing` option.
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>;
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.
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, Check
279/// to see if exactly one note has a Fix and return it. Otherwise return
280/// nullptr.
281const llvm::StringMap<tooling::Replacements> *
282getFixIt(const tooling::Diagnostic &Diagnostic, bool AnyFix);
283
284/// A diagnostic consumer that turns each \c Diagnostic into a
285/// \c SourceManager-independent \c ClangTidyError.
286// FIXME: If we move away from unit-tests, this can be moved to a private
287// implementation file.
289public:
290 /// \param EnableNolintBlocks Enables diagnostic-disabling inside blocks of
291 /// code, delimited by NOLINTBEGIN and NOLINTEND.
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 is
299 // more generic and should be pulled out into a more useful Diagnostics
300 // 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 the
318 /// context.
319 llvm::Regex *getHeaderFilter();
320
321 /// Returns the \c ExcludeHeaderFilter constructed for the options set in the
322 /// context.
323 llvm::Regex *getExcludeHeaderFilter();
324
325 /// Updates \c LastErrorRelatesToUserCode and LastErrorPassesLineFilter
326 /// 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 a
344 /// `BeginSourceFile()/EndSourceFile()` pair. Outside of a source file, we
345 /// should only receive diagnostics that have to source location, such as
346 /// command-line warnings.
347 bool InSourceFile = false;
348};
349
350} // end namespace tidy
351} // end namespace clang
352
353#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYDIAGNOSTICCONSUMER_H
static cl::opt< bool > ExperimentalCustomChecks("experimental-custom-checks", desc(R"( Enable experimental clang-query based custom checks. see https://clang.llvm.org/extra/clang-tidy/QueryBasedCustomChecks.html. )"), cl::init(false), cl::cat(ClangTidyCategory))
static cl::opt< bool > AllowEnablingAnalyzerAlphaCheckers("allow-enabling-analyzer-alpha-checkers", cl::init(false), cl::Hidden, cl::cat(ClangTidyCategory))
This option allows enabling the experimental alpha checkers from the static analyzer.
static cl::opt< bool > EnableModuleHeadersParsing("enable-module-headers-parsing", desc(R"( Enables preprocessor-level module header parsing for C++20 and above, empowering specific checks to detect macro definitions within modules. This feature may cause performance and parsing issues and is therefore considered experimental. )"), cl::init(false), cl::cat(ClangTidyCategory))
A GlobList that caches search results, so that search is performed only once for the same query.
Definition GlobList.h:57
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
ClangTidyContext(const ClangTidyContext &)=delete
const ClangTidyStats & getStats() const
Returns ClangTidyStats containing issued and ignored diagnostic counters.
bool canEnableAnalyzerAlphaCheckers() const
If the experimental alpha checkers from the static analyzer can be enabled.
ClangTidyContext(std::unique_ptr< ClangTidyOptionsProvider > OptionsProvider)
bool isCheckEnabled(StringRef CheckName) const
Returns true if the check is enabled for the CurrentFile.
std::string getCheckName(unsigned DiagnosticID) const
Returns the name of the clang-tidy check which produced this diagnostic ID.
void setOptionsCollector(llvm::StringSet<> *Collector)
const ClangTidyOptions & getOptions() const
Returns options for CurrentFile.
llvm::StringSet * getOptionsCollector() const
const std::string & getCurrentBuildDirectory() const
Returns build directory of the current translation unit.
void setCurrentBuildDirectory(StringRef BuildDirectory)
Should be called when starting to process new translation unit.
const LangOptions & getLangOpts() const
Gets the language options from the AST context.
DiagnosticBuilder configurationDiag(StringRef Message, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Report any errors to do with reading the configuration using this method.
void setASTContext(ASTContext *Context)
Sets ASTContext for the current translation unit.
const FileExtensionsSet & getHeaderFileExtensions() const
void setProfileStoragePrefix(StringRef ProfilePrefix)
Control storage of profile date.
void setEnableProfiling(bool Profile)
Control profile collection in clang-tidy.
DiagLevelAndFormatString getDiagLevelAndFormatString(unsigned DiagnosticID, SourceLocation Loc)
std::pair< DiagnosticIDs::Level, std::string > DiagLevelAndFormatString
void setCurrentFile(StringRef File)
Should be called when starting to process new translation unit.
bool shouldSuppressDiagnostic(DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info, SmallVectorImpl< tooling::Diagnostic > &NoLintErrors, bool AllowIO=true, bool EnableNoLintBlocks=true)
Check whether a given diagnostic should be suppressed due to the presence of a "NOLINT" suppression c...
bool treatAsError(StringRef CheckName) const
Returns true if the check should be upgraded to error for the CurrentFile.
DiagnosticBuilder diag(StringRef CheckName, SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Report any errors detected using this method.
void setDiagnosticsEngine(std::unique_ptr< DiagnosticOptions > DiagOpts, DiagnosticsEngine *DiagEngine)
Sets the DiagnosticsEngine that diag() will emit diagnostics to.
ClangTidyContext & operator=(const ClangTidyContext &)=delete
ClangTidyOptions getOptionsForFile(StringRef File) const
Returns options for File.
std::optional< ClangTidyProfiling::StorageParams > getProfileStorageParams() const
StringRef getCurrentFile() const
Returns the main file name of the current translation unit.
const ClangTidyGlobalOptions & getGlobalOptions() const
Returns global options.
void setSourceManager(SourceManager *SourceMgr)
Sets the SourceManager of the used DiagnosticsEngine.
const FileExtensionsSet & getImplementationFileExtensions() const
ClangTidyDiagnosticConsumer(ClangTidyContext &Ctx, DiagnosticsEngine *ExternalDiagEngine=nullptr, bool RemoveIncompatibleErrors=true, bool GetFixesFromNotes=false, bool EnableNolintBlocks=true)
void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) override
void BeginSourceFile(const LangOptions &LangOpts, const Preprocessor *PP=nullptr) override
This class is used to locate NOLINT comments in the file being analyzed, to decide whether a diagnost...
const llvm::StringMap< tooling::Replacements > * getFixIt(const tooling::Diagnostic &Diagnostic, bool AnyFix)
Gets the Fix attached to Diagnostic.
llvm::SmallSet< llvm::StringRef, 5 > FileExtensionsSet
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
ClangTidyError(StringRef CheckName, Level DiagLevel, StringRef BuildDirectory, bool IsWarningAsError)
std::vector< std::string > EnabledDiagnosticAliases
Contains options for clang-tidy.
Contains displayed and ignored diagnostic counters for a ClangTidy run.