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 DiagnosticBuilder diag(StringRef CheckName, SourceLocation Loc,
95 StringRef Description,
96 DiagnosticIDs::Level Level = DiagnosticIDs::Warning);
97
98 DiagnosticBuilder diag(StringRef CheckName, StringRef Description,
99 DiagnosticIDs::Level Level = DiagnosticIDs::Warning);
100
101 DiagnosticBuilder diag(const tooling::Diagnostic &Error);
102
103 /// Report any errors to do with reading the configuration using this method.
104 DiagnosticBuilder
105 configurationDiag(StringRef Message,
106 DiagnosticIDs::Level Level = DiagnosticIDs::Warning);
107
108 /// Check whether a given diagnostic should be suppressed due to the presence
109 /// of a "NOLINT" suppression comment.
110 /// This is exposed so that other tools that present clang-tidy diagnostics
111 /// (such as clangd) can respect the same suppression rules as clang-tidy.
112 /// This does not handle suppression of notes following a suppressed
113 /// diagnostic; that is left to the caller as it requires maintaining state in
114 /// between calls to this function.
115 /// If any NOLINT is malformed, e.g. a BEGIN without a subsequent END, output
116 /// \param NoLintErrors will return an error about it.
117 /// If \param AllowIO is false, the function does not attempt to read source
118 /// files from disk which are not already mapped into memory; such files are
119 /// treated as not containing a suppression comment.
120 /// \param EnableNoLintBlocks controls whether to honor NOLINTBEGIN/NOLINTEND
121 /// blocks; if false, only considers line-level disabling.
122 bool
123 shouldSuppressDiagnostic(DiagnosticsEngine::Level DiagLevel,
124 const Diagnostic &Info,
125 SmallVectorImpl<tooling::Diagnostic> &NoLintErrors,
126 bool AllowIO = true, bool EnableNoLintBlocks = true);
127
128 /// Sets the \c SourceManager of the used \c DiagnosticsEngine.
129 ///
130 /// This is called from the \c ClangTidyCheck base class.
131 void setSourceManager(SourceManager *SourceMgr);
132
133 /// Should be called when starting to process new translation unit.
134 void setCurrentFile(StringRef File);
135
136 /// Returns the main file name of the current translation unit.
137 StringRef getCurrentFile() const { return CurrentFile; }
138
139 /// Sets ASTContext for the current translation unit.
140 void setASTContext(ASTContext *Context);
141
142 /// Gets the language options from the AST context.
143 const LangOptions &getLangOpts() const { return LangOpts; }
144
145 /// Returns the name of the clang-tidy check which produced this
146 /// diagnostic ID.
147 std::string getCheckName(unsigned DiagnosticID) const;
148
149 /// Returns \c true if the check is enabled for the \c CurrentFile.
150 ///
151 /// The \c CurrentFile can be changed using \c setCurrentFile.
152 bool isCheckEnabled(StringRef CheckName) const;
153
154 /// Returns \c true if the check should be upgraded to error for the
155 /// \c CurrentFile.
156 bool treatAsError(StringRef CheckName) const;
157
158 /// Returns global options.
160
161 /// Returns options for \c CurrentFile.
162 ///
163 /// The \c CurrentFile can be changed using \c setCurrentFile.
164 const ClangTidyOptions &getOptions() const;
165
166 /// Returns options for \c File. Does not change or depend on
167 /// \c CurrentFile.
168 ClangTidyOptions getOptionsForFile(StringRef File) const;
169
171 return HeaderFileExtensions;
172 }
173
175 return ImplementationFileExtensions;
176 }
177
178 /// Returns \c ClangTidyStats containing issued and ignored diagnostic
179 /// counters.
180 const ClangTidyStats &getStats() const { return Stats; }
181
182 /// Control profile collection in clang-tidy.
183 void setEnableProfiling(bool Profile);
184 bool getEnableProfiling() const { return Profile; }
185
186 /// Control storage of profile date.
187 void setProfileStoragePrefix(StringRef ProfilePrefix);
188 std::optional<ClangTidyProfiling::StorageParams>
190
191 /// Should be called when starting to process new translation unit.
192 void setCurrentBuildDirectory(StringRef BuildDirectory) {
193 CurrentBuildDirectory = std::string(BuildDirectory);
194 }
195
196 /// Returns build directory of the current translation unit.
197 const std::string &getCurrentBuildDirectory() const {
198 return CurrentBuildDirectory;
199 }
200
201 /// If the experimental alpha checkers from the static analyzer can be
202 /// enabled.
204 return AllowEnablingAnalyzerAlphaCheckers;
205 }
206
207 // This method determines whether preprocessor-level module header parsing is
208 // enabled using the `--experimental-enable-module-headers-parsing` option.
210 return EnableModuleHeadersParsing;
211 }
212
213 // whether experimental custom checks can be enabled.
214 // enabled with `--experimental-custom-checks`
215 bool canExperimentalCustomChecks() const { return ExperimentalCustomChecks; }
216
217 void setSelfContainedDiags(bool Value) { SelfContainedDiags = Value; }
218
219 bool areDiagsSelfContained() const { return SelfContainedDiags; }
220
221 using DiagLevelAndFormatString = std::pair<DiagnosticIDs::Level, std::string>;
223 SourceLocation Loc) {
224 return {static_cast<DiagnosticIDs::Level>(
225 DiagEngine->getDiagnosticLevel(DiagnosticID, Loc)),
226 std::string(
227 DiagEngine->getDiagnosticIDs()->getDescription(DiagnosticID))};
228 }
229
230 void setOptionsCollector(llvm::StringSet<> *Collector) {
231 OptionsCollector = Collector;
232 }
233 llvm::StringSet<> *getOptionsCollector() const { return OptionsCollector; }
234
235private:
236 // Writes to Stats.
238
239 std::unique_ptr<DiagnosticOptions> DiagOpts = nullptr;
240 DiagnosticsEngine *DiagEngine = nullptr;
241 std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider;
242
243 std::string CurrentFile;
244 ClangTidyOptions CurrentOptions;
245
246 std::unique_ptr<CachedGlobList> CheckFilter;
247 std::unique_ptr<CachedGlobList> WarningAsErrorFilter;
248
249 FileExtensionsSet HeaderFileExtensions;
250 FileExtensionsSet ImplementationFileExtensions;
251
252 LangOptions LangOpts;
253
254 ClangTidyStats Stats;
255
256 std::string CurrentBuildDirectory;
257
258 llvm::DenseMap<unsigned, std::string> CheckNamesByDiagnosticID;
259
260 bool Profile = false;
261 std::string ProfilePrefix;
262
263 bool AllowEnablingAnalyzerAlphaCheckers;
264 bool EnableModuleHeadersParsing;
265 bool ExperimentalCustomChecks;
266
267 bool SelfContainedDiags = false;
268
269 NoLintDirectiveHandler NoLintHandler;
270 llvm::StringSet<> *OptionsCollector = nullptr;
271};
272
273/// Gets the Fix attached to \p Diagnostic.
274/// If there isn't a Fix attached to the diagnostic and \p AnyFix is true, Check
275/// to see if exactly one note has a Fix and return it. Otherwise return
276/// nullptr.
277const llvm::StringMap<tooling::Replacements> *
278getFixIt(const tooling::Diagnostic &Diagnostic, bool AnyFix);
279
280/// A diagnostic consumer that turns each \c Diagnostic into a
281/// \c SourceManager-independent \c ClangTidyError.
282// FIXME: If we move away from unit-tests, this can be moved to a private
283// implementation file.
285public:
286 /// \param EnableNolintBlocks Enables diagnostic-disabling inside blocks of
287 /// code, delimited by NOLINTBEGIN and NOLINTEND.
289 DiagnosticsEngine *ExternalDiagEngine = nullptr,
290 bool RemoveIncompatibleErrors = true,
291 bool GetFixesFromNotes = false,
292 bool EnableNolintBlocks = true);
293
294 // FIXME: The concept of converting between FixItHints and Replacements is
295 // more generic and should be pulled out into a more useful Diagnostics
296 // library.
297 void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
298 const Diagnostic &Info) override;
299
300 void BeginSourceFile(const LangOptions &LangOpts,
301 const Preprocessor *PP = nullptr) override;
302
303 void EndSourceFile() override;
304
305 // Retrieve the diagnostics that were captured.
306 std::vector<ClangTidyError> take();
307
308private:
309 void finalizeLastError();
310 void removeIncompatibleErrors();
311 void removeDuplicatedDiagnosticsOfAliasCheckers();
312
313 /// Returns the \c HeaderFilter constructed for the options set in the
314 /// context.
315 llvm::Regex *getHeaderFilter();
316
317 /// Returns the \c ExcludeHeaderFilter constructed for the options set in the
318 /// context.
319 llvm::Regex *getExcludeHeaderFilter();
320
321 /// Updates \c LastErrorRelatesToUserCode and LastErrorPassesLineFilter
322 /// according to the diagnostic \p Location.
323 void checkFilters(SourceLocation Location, const SourceManager &Sources);
324 bool passesLineFilter(StringRef FileName, unsigned LineNumber) const;
325
326 void forwardDiagnostic(const Diagnostic &Info);
327
328 ClangTidyContext &Context;
329 DiagnosticsEngine *ExternalDiagEngine;
330 bool RemoveIncompatibleErrors;
331 bool GetFixesFromNotes;
332 bool EnableNolintBlocks;
333 std::vector<ClangTidyError> Errors;
334 std::unique_ptr<llvm::Regex> HeaderFilter;
335 std::unique_ptr<llvm::Regex> ExcludeHeaderFilter;
336 bool LastErrorRelatesToUserCode = false;
337 bool LastErrorPassesLineFilter = false;
338 bool LastErrorWasIgnored = false;
339 /// Tracks whether we're currently inside a
340 /// `BeginSourceFile()/EndSourceFile()` pair. Outside of a source file, we
341 /// should only receive diagnostics that have to source location, such as
342 /// command-line warnings.
343 bool InSourceFile = false;
344};
345
346} // end namespace tidy
347} // end namespace clang
348
349#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.