1 //===--- ClangTidyDiagnosticConsumer.h - clang-tidy -------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYDIAGNOSTICCONSUMER_H
10 #define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYDIAGNOSTICCONSUMER_H
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"
31 /// A detected error complete with information to display diagnostic and
34 /// This is used as an intermediate format to transport Diagnostics without a
35 /// dependency on a SourceManager.
37 /// FIXME: Make Diagnostics flexible enough to support this directly.
38 struct ClangTidyError
: tooling::Diagnostic
{
39 ClangTidyError(StringRef CheckName
, Level DiagLevel
, StringRef BuildDirectory
,
40 bool IsWarningAsError
);
42 bool IsWarningAsError
;
43 std::vector
<std::string
> EnabledDiagnosticAliases
;
46 /// Contains displayed and ignored diagnostic counters for a ClangTidy run.
47 struct ClangTidyStats
{
48 unsigned ErrorsDisplayed
= 0;
49 unsigned ErrorsIgnoredCheckFilter
= 0;
50 unsigned ErrorsIgnoredNOLINT
= 0;
51 unsigned ErrorsIgnoredNonUserCode
= 0;
52 unsigned ErrorsIgnoredLineFilter
= 0;
54 unsigned errorsIgnored() const {
55 return ErrorsIgnoredNOLINT
+ ErrorsIgnoredCheckFilter
+
56 ErrorsIgnoredNonUserCode
+ ErrorsIgnoredLineFilter
;
60 /// Every \c ClangTidyCheck reports errors through a \c DiagnosticsEngine
61 /// provided by this context.
63 /// A \c ClangTidyCheck always has access to the active context to report
66 /// Context->Diag(Loc, "Single-argument constructors must be explicit")
67 /// << FixItHint::CreateInsertion(Loc, "explicit ");
69 class ClangTidyContext
{
71 /// Initializes \c ClangTidyContext instance.
72 ClangTidyContext(std::unique_ptr
<ClangTidyOptionsProvider
> OptionsProvider
,
73 bool AllowEnablingAnalyzerAlphaCheckers
= false);
74 /// Sets the DiagnosticsEngine that diag() will emit diagnostics to.
75 // FIXME: this is required initialization, and should be a constructor param.
76 // Fix the context -> diag engine -> consumer -> context initialization cycle.
77 void setDiagnosticsEngine(DiagnosticsEngine
*DiagEngine
) {
78 this->DiagEngine
= DiagEngine
;
83 /// Report any errors detected using this method.
85 /// This is still under heavy development and will likely change towards using
86 /// tablegen'd diagnostic IDs.
87 /// FIXME: Figure out a way to manage ID spaces.
88 DiagnosticBuilder
diag(StringRef CheckName
, SourceLocation Loc
,
90 DiagnosticIDs::Level Level
= DiagnosticIDs::Warning
);
92 DiagnosticBuilder
diag(StringRef CheckName
, StringRef Message
,
93 DiagnosticIDs::Level Level
= DiagnosticIDs::Warning
);
95 DiagnosticBuilder
diag(const tooling::Diagnostic
&Error
);
97 /// Report any errors to do with reading the configuration using this method.
99 configurationDiag(StringRef Message
,
100 DiagnosticIDs::Level Level
= DiagnosticIDs::Warning
);
102 /// Check whether a given diagnostic should be suppressed due to the presence
103 /// of a "NOLINT" suppression comment.
104 /// This is exposed so that other tools that present clang-tidy diagnostics
105 /// (such as clangd) can respect the same suppression rules as clang-tidy.
106 /// This does not handle suppression of notes following a suppressed
107 /// diagnostic; that is left to the caller as it requires maintaining state in
108 /// between calls to this function.
109 /// If any NOLINT is malformed, e.g. a BEGIN without a subsequent END, output
110 /// \param NoLintErrors will return an error about it.
111 /// If \param AllowIO is false, the function does not attempt to read source
112 /// files from disk which are not already mapped into memory; such files are
113 /// treated as not containing a suppression comment.
114 /// \param EnableNoLintBlocks controls whether to honor NOLINTBEGIN/NOLINTEND
115 /// blocks; if false, only considers line-level disabling.
117 shouldSuppressDiagnostic(DiagnosticsEngine::Level DiagLevel
,
118 const Diagnostic
&Info
,
119 SmallVectorImpl
<tooling::Diagnostic
> &NoLintErrors
,
120 bool AllowIO
= true, bool EnableNoLintBlocks
= true);
122 /// Sets the \c SourceManager of the used \c DiagnosticsEngine.
124 /// This is called from the \c ClangTidyCheck base class.
125 void setSourceManager(SourceManager
*SourceMgr
);
127 /// Should be called when starting to process new translation unit.
128 void setCurrentFile(StringRef File
);
130 /// Returns the main file name of the current translation unit.
131 StringRef
getCurrentFile() const { return CurrentFile
; }
133 /// Sets ASTContext for the current translation unit.
134 void setASTContext(ASTContext
*Context
);
136 /// Gets the language options from the AST context.
137 const LangOptions
&getLangOpts() const { return LangOpts
; }
139 /// Returns the name of the clang-tidy check which produced this
141 std::string
getCheckName(unsigned DiagnosticID
) const;
143 /// Returns \c true if the check is enabled for the \c CurrentFile.
145 /// The \c CurrentFile can be changed using \c setCurrentFile.
146 bool isCheckEnabled(StringRef CheckName
) const;
148 /// Returns \c true if the check should be upgraded to error for the
150 bool treatAsError(StringRef CheckName
) const;
152 /// Returns global options.
153 const ClangTidyGlobalOptions
&getGlobalOptions() const;
155 /// Returns options for \c CurrentFile.
157 /// The \c CurrentFile can be changed using \c setCurrentFile.
158 const ClangTidyOptions
&getOptions() const;
160 /// Returns options for \c File. Does not change or depend on
162 ClangTidyOptions
getOptionsForFile(StringRef File
) const;
164 const FileExtensionsSet
&getHeaderFileExtensions() const {
165 return HeaderFileExtensions
;
168 const FileExtensionsSet
&getImplementationFileExtensions() const {
169 return ImplementationFileExtensions
;
172 /// Returns \c ClangTidyStats containing issued and ignored diagnostic
174 const ClangTidyStats
&getStats() const { return Stats
; }
176 /// Control profile collection in clang-tidy.
177 void setEnableProfiling(bool Profile
);
178 bool getEnableProfiling() const { return Profile
; }
180 /// Control storage of profile date.
181 void setProfileStoragePrefix(StringRef ProfilePrefix
);
182 std::optional
<ClangTidyProfiling::StorageParams
>
183 getProfileStorageParams() const;
185 /// Should be called when starting to process new translation unit.
186 void setCurrentBuildDirectory(StringRef BuildDirectory
) {
187 CurrentBuildDirectory
= std::string(BuildDirectory
);
190 /// Returns build directory of the current translation unit.
191 const std::string
&getCurrentBuildDirectory() const {
192 return CurrentBuildDirectory
;
195 /// If the experimental alpha checkers from the static analyzer can be
197 bool canEnableAnalyzerAlphaCheckers() const {
198 return AllowEnablingAnalyzerAlphaCheckers
;
201 void setSelfContainedDiags(bool Value
) { SelfContainedDiags
= Value
; }
203 bool areDiagsSelfContained() const { return SelfContainedDiags
; }
205 using DiagLevelAndFormatString
= std::pair
<DiagnosticIDs::Level
, std::string
>;
206 DiagLevelAndFormatString
getDiagLevelAndFormatString(unsigned DiagnosticID
,
207 SourceLocation Loc
) {
208 return DiagLevelAndFormatString(
209 static_cast<DiagnosticIDs::Level
>(
210 DiagEngine
->getDiagnosticLevel(DiagnosticID
, Loc
)),
212 DiagEngine
->getDiagnosticIDs()->getDescription(DiagnosticID
)));
215 void setOptionsCollector(llvm::StringSet
<> *Collector
) {
216 OptionsCollector
= Collector
;
218 llvm::StringSet
<> *getOptionsCollector() const { return OptionsCollector
; }
222 friend class ClangTidyDiagnosticConsumer
;
224 DiagnosticsEngine
*DiagEngine
;
225 std::unique_ptr
<ClangTidyOptionsProvider
> OptionsProvider
;
227 std::string CurrentFile
;
228 ClangTidyOptions CurrentOptions
;
230 std::unique_ptr
<CachedGlobList
> CheckFilter
;
231 std::unique_ptr
<CachedGlobList
> WarningAsErrorFilter
;
233 FileExtensionsSet HeaderFileExtensions
;
234 FileExtensionsSet ImplementationFileExtensions
;
236 LangOptions LangOpts
;
238 ClangTidyStats Stats
;
240 std::string CurrentBuildDirectory
;
242 llvm::DenseMap
<unsigned, std::string
> CheckNamesByDiagnosticID
;
245 std::string ProfilePrefix
;
247 bool AllowEnablingAnalyzerAlphaCheckers
;
249 bool SelfContainedDiags
;
251 NoLintDirectiveHandler NoLintHandler
;
252 llvm::StringSet
<> *OptionsCollector
= nullptr;
255 /// Gets the Fix attached to \p Diagnostic.
256 /// If there isn't a Fix attached to the diagnostic and \p AnyFix is true, Check
257 /// to see if exactly one note has a Fix and return it. Otherwise return
259 const llvm::StringMap
<tooling::Replacements
> *
260 getFixIt(const tooling::Diagnostic
&Diagnostic
, bool AnyFix
);
262 /// A diagnostic consumer that turns each \c Diagnostic into a
263 /// \c SourceManager-independent \c ClangTidyError.
264 // FIXME: If we move away from unit-tests, this can be moved to a private
265 // implementation file.
266 class ClangTidyDiagnosticConsumer
: public DiagnosticConsumer
{
268 /// \param EnableNolintBlocks Enables diagnostic-disabling inside blocks of
269 /// code, delimited by NOLINTBEGIN and NOLINTEND.
270 ClangTidyDiagnosticConsumer(ClangTidyContext
&Ctx
,
271 DiagnosticsEngine
*ExternalDiagEngine
= nullptr,
272 bool RemoveIncompatibleErrors
= true,
273 bool GetFixesFromNotes
= false,
274 bool EnableNolintBlocks
= true);
276 // FIXME: The concept of converting between FixItHints and Replacements is
277 // more generic and should be pulled out into a more useful Diagnostics
279 void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel
,
280 const Diagnostic
&Info
) override
;
282 // Retrieve the diagnostics that were captured.
283 std::vector
<ClangTidyError
> take();
286 void finalizeLastError();
287 void removeIncompatibleErrors();
288 void removeDuplicatedDiagnosticsOfAliasCheckers();
290 /// Returns the \c HeaderFilter constructed for the options set in the
292 llvm::Regex
*getHeaderFilter();
294 /// Updates \c LastErrorRelatesToUserCode and LastErrorPassesLineFilter
295 /// according to the diagnostic \p Location.
296 void checkFilters(SourceLocation Location
, const SourceManager
&Sources
);
297 bool passesLineFilter(StringRef FileName
, unsigned LineNumber
) const;
299 void forwardDiagnostic(const Diagnostic
&Info
);
301 ClangTidyContext
&Context
;
302 DiagnosticsEngine
*ExternalDiagEngine
;
303 bool RemoveIncompatibleErrors
;
304 bool GetFixesFromNotes
;
305 bool EnableNolintBlocks
;
306 std::vector
<ClangTidyError
> Errors
;
307 std::unique_ptr
<llvm::Regex
> HeaderFilter
;
308 bool LastErrorRelatesToUserCode
;
309 bool LastErrorPassesLineFilter
;
310 bool LastErrorWasIgnored
;
313 } // end namespace tidy
314 } // end namespace clang
316 #endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYDIAGNOSTICCONSUMER_H