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

source code of clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h