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 bool EnableModuleHeadersParsing
= false);
75 /// Sets the DiagnosticsEngine that diag() will emit diagnostics to.
76 // FIXME: this is required initialization, and should be a constructor param.
77 // Fix the context -> diag engine -> consumer -> context initialization cycle.
78 void setDiagnosticsEngine(DiagnosticsEngine
*DiagEngine
) {
79 this->DiagEngine
= DiagEngine
;
84 /// Report any errors detected using this method.
86 /// This is still under heavy development and will likely change towards using
87 /// tablegen'd diagnostic IDs.
88 /// FIXME: Figure out a way to manage ID spaces.
89 DiagnosticBuilder
diag(StringRef CheckName
, SourceLocation Loc
,
90 StringRef Description
,
91 DiagnosticIDs::Level Level
= DiagnosticIDs::Warning
);
93 DiagnosticBuilder
diag(StringRef CheckName
, StringRef Description
,
94 DiagnosticIDs::Level Level
= DiagnosticIDs::Warning
);
96 DiagnosticBuilder
diag(const tooling::Diagnostic
&Error
);
98 /// Report any errors to do with reading the configuration using this method.
100 configurationDiag(StringRef Message
,
101 DiagnosticIDs::Level Level
= DiagnosticIDs::Warning
);
103 /// Check whether a given diagnostic should be suppressed due to the presence
104 /// of a "NOLINT" suppression comment.
105 /// This is exposed so that other tools that present clang-tidy diagnostics
106 /// (such as clangd) can respect the same suppression rules as clang-tidy.
107 /// This does not handle suppression of notes following a suppressed
108 /// diagnostic; that is left to the caller as it requires maintaining state in
109 /// between calls to this function.
110 /// If any NOLINT is malformed, e.g. a BEGIN without a subsequent END, output
111 /// \param NoLintErrors will return an error about it.
112 /// If \param AllowIO is false, the function does not attempt to read source
113 /// files from disk which are not already mapped into memory; such files are
114 /// treated as not containing a suppression comment.
115 /// \param EnableNoLintBlocks controls whether to honor NOLINTBEGIN/NOLINTEND
116 /// blocks; if false, only considers line-level disabling.
118 shouldSuppressDiagnostic(DiagnosticsEngine::Level DiagLevel
,
119 const Diagnostic
&Info
,
120 SmallVectorImpl
<tooling::Diagnostic
> &NoLintErrors
,
121 bool AllowIO
= true, bool EnableNoLintBlocks
= true);
123 /// Sets the \c SourceManager of the used \c DiagnosticsEngine.
125 /// This is called from the \c ClangTidyCheck base class.
126 void setSourceManager(SourceManager
*SourceMgr
);
128 /// Should be called when starting to process new translation unit.
129 void setCurrentFile(StringRef File
);
131 /// Returns the main file name of the current translation unit.
132 StringRef
getCurrentFile() const { return CurrentFile
; }
134 /// Sets ASTContext for the current translation unit.
135 void setASTContext(ASTContext
*Context
);
137 /// Gets the language options from the AST context.
138 const LangOptions
&getLangOpts() const { return LangOpts
; }
140 /// Returns the name of the clang-tidy check which produced this
142 std::string
getCheckName(unsigned DiagnosticID
) const;
144 /// Returns \c true if the check is enabled for the \c CurrentFile.
146 /// The \c CurrentFile can be changed using \c setCurrentFile.
147 bool isCheckEnabled(StringRef CheckName
) const;
149 /// Returns \c true if the check should be upgraded to error for the
151 bool treatAsError(StringRef CheckName
) const;
153 /// Returns global options.
154 const ClangTidyGlobalOptions
&getGlobalOptions() const;
156 /// Returns options for \c CurrentFile.
158 /// The \c CurrentFile can be changed using \c setCurrentFile.
159 const ClangTidyOptions
&getOptions() const;
161 /// Returns options for \c File. Does not change or depend on
163 ClangTidyOptions
getOptionsForFile(StringRef File
) const;
165 const FileExtensionsSet
&getHeaderFileExtensions() const {
166 return HeaderFileExtensions
;
169 const FileExtensionsSet
&getImplementationFileExtensions() const {
170 return ImplementationFileExtensions
;
173 /// Returns \c ClangTidyStats containing issued and ignored diagnostic
175 const ClangTidyStats
&getStats() const { return Stats
; }
177 /// Control profile collection in clang-tidy.
178 void setEnableProfiling(bool Profile
);
179 bool getEnableProfiling() const { return Profile
; }
181 /// Control storage of profile date.
182 void setProfileStoragePrefix(StringRef ProfilePrefix
);
183 std::optional
<ClangTidyProfiling::StorageParams
>
184 getProfileStorageParams() const;
186 /// Should be called when starting to process new translation unit.
187 void setCurrentBuildDirectory(StringRef BuildDirectory
) {
188 CurrentBuildDirectory
= std::string(BuildDirectory
);
191 /// Returns build directory of the current translation unit.
192 const std::string
&getCurrentBuildDirectory() const {
193 return CurrentBuildDirectory
;
196 /// If the experimental alpha checkers from the static analyzer can be
198 bool canEnableAnalyzerAlphaCheckers() const {
199 return AllowEnablingAnalyzerAlphaCheckers
;
202 // This method determines whether preprocessor-level module header parsing is
203 // enabled using the `--experimental-enable-module-headers-parsing` option.
204 bool canEnableModuleHeadersParsing() const {
205 return EnableModuleHeadersParsing
;
208 void setSelfContainedDiags(bool Value
) { SelfContainedDiags
= Value
; }
210 bool areDiagsSelfContained() const { return SelfContainedDiags
; }
212 using DiagLevelAndFormatString
= std::pair
<DiagnosticIDs::Level
, std::string
>;
213 DiagLevelAndFormatString
getDiagLevelAndFormatString(unsigned DiagnosticID
,
214 SourceLocation Loc
) {
216 static_cast<DiagnosticIDs::Level
>(
217 DiagEngine
->getDiagnosticLevel(DiagnosticID
, Loc
)),
219 DiagEngine
->getDiagnosticIDs()->getDescription(DiagnosticID
))};
222 void setOptionsCollector(llvm::StringSet
<> *Collector
) {
223 OptionsCollector
= Collector
;
225 llvm::StringSet
<> *getOptionsCollector() const { return OptionsCollector
; }
229 friend class ClangTidyDiagnosticConsumer
;
231 DiagnosticsEngine
*DiagEngine
= nullptr;
232 std::unique_ptr
<ClangTidyOptionsProvider
> OptionsProvider
;
234 std::string CurrentFile
;
235 ClangTidyOptions CurrentOptions
;
237 std::unique_ptr
<CachedGlobList
> CheckFilter
;
238 std::unique_ptr
<CachedGlobList
> WarningAsErrorFilter
;
240 FileExtensionsSet HeaderFileExtensions
;
241 FileExtensionsSet ImplementationFileExtensions
;
243 LangOptions LangOpts
;
245 ClangTidyStats Stats
;
247 std::string CurrentBuildDirectory
;
249 llvm::DenseMap
<unsigned, std::string
> CheckNamesByDiagnosticID
;
251 bool Profile
= false;
252 std::string ProfilePrefix
;
254 bool AllowEnablingAnalyzerAlphaCheckers
;
255 bool EnableModuleHeadersParsing
;
257 bool SelfContainedDiags
= false;
259 NoLintDirectiveHandler NoLintHandler
;
260 llvm::StringSet
<> *OptionsCollector
= nullptr;
263 /// Gets the Fix attached to \p Diagnostic.
264 /// If there isn't a Fix attached to the diagnostic and \p AnyFix is true, Check
265 /// to see if exactly one note has a Fix and return it. Otherwise return
267 const llvm::StringMap
<tooling::Replacements
> *
268 getFixIt(const tooling::Diagnostic
&Diagnostic
, bool AnyFix
);
270 /// A diagnostic consumer that turns each \c Diagnostic into a
271 /// \c SourceManager-independent \c ClangTidyError.
272 // FIXME: If we move away from unit-tests, this can be moved to a private
273 // implementation file.
274 class ClangTidyDiagnosticConsumer
: public DiagnosticConsumer
{
276 /// \param EnableNolintBlocks Enables diagnostic-disabling inside blocks of
277 /// code, delimited by NOLINTBEGIN and NOLINTEND.
278 ClangTidyDiagnosticConsumer(ClangTidyContext
&Ctx
,
279 DiagnosticsEngine
*ExternalDiagEngine
= nullptr,
280 bool RemoveIncompatibleErrors
= true,
281 bool GetFixesFromNotes
= false,
282 bool EnableNolintBlocks
= true);
284 // FIXME: The concept of converting between FixItHints and Replacements is
285 // more generic and should be pulled out into a more useful Diagnostics
287 void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel
,
288 const Diagnostic
&Info
) override
;
290 // Retrieve the diagnostics that were captured.
291 std::vector
<ClangTidyError
> take();
294 void finalizeLastError();
295 void removeIncompatibleErrors();
296 void removeDuplicatedDiagnosticsOfAliasCheckers();
298 /// Returns the \c HeaderFilter constructed for the options set in the
300 llvm::Regex
*getHeaderFilter();
302 /// Updates \c LastErrorRelatesToUserCode and LastErrorPassesLineFilter
303 /// according to the diagnostic \p Location.
304 void checkFilters(SourceLocation Location
, const SourceManager
&Sources
);
305 bool passesLineFilter(StringRef FileName
, unsigned LineNumber
) const;
307 void forwardDiagnostic(const Diagnostic
&Info
);
309 ClangTidyContext
&Context
;
310 DiagnosticsEngine
*ExternalDiagEngine
;
311 bool RemoveIncompatibleErrors
;
312 bool GetFixesFromNotes
;
313 bool EnableNolintBlocks
;
314 std::vector
<ClangTidyError
> Errors
;
315 std::unique_ptr
<llvm::Regex
> HeaderFilter
;
316 std::unique_ptr
<llvm::Regex
> ExcludeHeaderFilter
;
317 bool LastErrorRelatesToUserCode
= false;
318 bool LastErrorPassesLineFilter
= false;
319 bool LastErrorWasIgnored
= false;
322 } // end namespace tidy
323 } // end namespace clang
325 #endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYDIAGNOSTICCONSUMER_H