/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/include/clang/Frontend/VerifyDiagnosticConsumer.h
Line | Count | Source |
1 | | //===- VerifyDiagnosticConsumer.h - Verifying Diagnostic Client -*- 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_FRONTEND_VERIFYDIAGNOSTICCONSUMER_H |
10 | | #define LLVM_CLANG_FRONTEND_VERIFYDIAGNOSTICCONSUMER_H |
11 | | |
12 | | #include "clang/Basic/Diagnostic.h" |
13 | | #include "clang/Basic/FileManager.h" |
14 | | #include "clang/Basic/LLVM.h" |
15 | | #include "clang/Basic/SourceLocation.h" |
16 | | #include "clang/Lex/Preprocessor.h" |
17 | | #include "llvm/ADT/DenseMap.h" |
18 | | #include "llvm/ADT/PointerIntPair.h" |
19 | | #include "llvm/ADT/StringRef.h" |
20 | | #include <cassert> |
21 | | #include <limits> |
22 | | #include <memory> |
23 | | #include <string> |
24 | | #include <vector> |
25 | | |
26 | | namespace clang { |
27 | | |
28 | | class FileEntry; |
29 | | class LangOptions; |
30 | | class SourceManager; |
31 | | class TextDiagnosticBuffer; |
32 | | |
33 | | /// VerifyDiagnosticConsumer - Create a diagnostic client which will use |
34 | | /// markers in the input source to check that all the emitted diagnostics match |
35 | | /// those expected. |
36 | | /// |
37 | | /// INVOKING THE DIAGNOSTIC CHECKER: |
38 | | /// |
39 | | /// VerifyDiagnosticConsumer is typically invoked via the "-verify" option to |
40 | | /// "clang -cc1". "-verify" is equivalent to "-verify=expected", so all |
41 | | /// diagnostics are typically specified with the prefix "expected". For |
42 | | /// example: |
43 | | /// |
44 | | /// \code |
45 | | /// int A = B; // expected-error {{use of undeclared identifier 'B'}} |
46 | | /// \endcode |
47 | | /// |
48 | | /// Custom prefixes can be specified as a comma-separated sequence. Each |
49 | | /// prefix must start with a letter and contain only alphanumeric characters, |
50 | | /// hyphens, and underscores. For example, given just "-verify=foo,bar", |
51 | | /// the above diagnostic would be ignored, but the following diagnostics would |
52 | | /// be recognized: |
53 | | /// |
54 | | /// \code |
55 | | /// int A = B; // foo-error {{use of undeclared identifier 'B'}} |
56 | | /// int C = D; // bar-error {{use of undeclared identifier 'D'}} |
57 | | /// \endcode |
58 | | /// |
59 | | /// Multiple occurrences accumulate prefixes. For example, |
60 | | /// "-verify -verify=foo,bar -verify=baz" is equivalent to |
61 | | /// "-verify=expected,foo,bar,baz". |
62 | | /// |
63 | | /// SPECIFYING DIAGNOSTICS: |
64 | | /// |
65 | | /// Indicating that a line expects an error or a warning is simple. Put a |
66 | | /// comment on the line that has the diagnostic, use: |
67 | | /// |
68 | | /// \code |
69 | | /// expected-{error,warning,remark,note} |
70 | | /// \endcode |
71 | | /// |
72 | | /// to tag if it's an expected error, remark or warning, and place the expected |
73 | | /// text between {{ and }} markers. The full text doesn't have to be included, |
74 | | /// only enough to ensure that the correct diagnostic was emitted. |
75 | | /// |
76 | | /// Here's an example: |
77 | | /// |
78 | | /// \code |
79 | | /// int A = B; // expected-error {{use of undeclared identifier 'B'}} |
80 | | /// \endcode |
81 | | /// |
82 | | /// You can place as many diagnostics on one line as you wish. To make the code |
83 | | /// more readable, you can use slash-newline to separate out the diagnostics. |
84 | | /// |
85 | | /// Alternatively, it is possible to specify the line on which the diagnostic |
86 | | /// should appear by appending "@<line>" to "expected-<type>", for example: |
87 | | /// |
88 | | /// \code |
89 | | /// #warning some text |
90 | | /// // expected-warning@10 {{some text}} |
91 | | /// \endcode |
92 | | /// |
93 | | /// The line number may be absolute (as above), or relative to the current |
94 | | /// line by prefixing the number with either '+' or '-'. |
95 | | /// |
96 | | /// If the diagnostic is generated in a separate file, for example in a shared |
97 | | /// header file, it may be beneficial to be able to declare the file in which |
98 | | /// the diagnostic will appear, rather than placing the expected-* directive in |
99 | | /// the actual file itself. This can be done using the following syntax: |
100 | | /// |
101 | | /// \code |
102 | | /// // expected-error@path/include.h:15 {{error message}} |
103 | | /// \endcode |
104 | | /// |
105 | | /// The path can be absolute or relative and the same search paths will be used |
106 | | /// as for #include directives. The line number in an external file may be |
107 | | /// substituted with '*' meaning that any line number will match (useful where |
108 | | /// the included file is, for example, a system header where the actual line |
109 | | /// number may change and is not critical). |
110 | | /// |
111 | | /// As an alternative to specifying a fixed line number, the location of a |
112 | | /// diagnostic can instead be indicated by a marker of the form "#<marker>". |
113 | | /// Markers are specified by including them in a comment, and then referenced |
114 | | /// by appending the marker to the diagnostic with "@#<marker>": |
115 | | /// |
116 | | /// \code |
117 | | /// #warning some text // #1 |
118 | | /// // expected-warning@#1 {{some text}} |
119 | | /// \endcode |
120 | | /// |
121 | | /// The name of a marker used in a directive must be unique within the |
122 | | /// compilation. |
123 | | /// |
124 | | /// The simple syntax above allows each specification to match exactly one |
125 | | /// error. You can use the extended syntax to customize this. The extended |
126 | | /// syntax is "expected-<type> <n> {{diag text}}", where \<type> is one of |
127 | | /// "error", "warning" or "note", and \<n> is a positive integer. This allows |
128 | | /// the diagnostic to appear as many times as specified. Example: |
129 | | /// |
130 | | /// \code |
131 | | /// void f(); // expected-note 2 {{previous declaration is here}} |
132 | | /// \endcode |
133 | | /// |
134 | | /// Where the diagnostic is expected to occur a minimum number of times, this |
135 | | /// can be specified by appending a '+' to the number. Example: |
136 | | /// |
137 | | /// \code |
138 | | /// void f(); // expected-note 0+ {{previous declaration is here}} |
139 | | /// void g(); // expected-note 1+ {{previous declaration is here}} |
140 | | /// \endcode |
141 | | /// |
142 | | /// In the first example, the diagnostic becomes optional, i.e. it will be |
143 | | /// swallowed if it occurs, but will not generate an error if it does not |
144 | | /// occur. In the second example, the diagnostic must occur at least once. |
145 | | /// As a short-hand, "one or more" can be specified simply by '+'. Example: |
146 | | /// |
147 | | /// \code |
148 | | /// void g(); // expected-note + {{previous declaration is here}} |
149 | | /// \endcode |
150 | | /// |
151 | | /// A range can also be specified by "<n>-<m>". Example: |
152 | | /// |
153 | | /// \code |
154 | | /// void f(); // expected-note 0-1 {{previous declaration is here}} |
155 | | /// \endcode |
156 | | /// |
157 | | /// In this example, the diagnostic may appear only once, if at all. |
158 | | /// |
159 | | /// Regex matching mode may be selected by appending '-re' to type and |
160 | | /// including regexes wrapped in double curly braces in the directive, such as: |
161 | | /// |
162 | | /// \code |
163 | | /// expected-error-re {{format specifies type 'wchar_t **' (aka '{{.+}}')}} |
164 | | /// \endcode |
165 | | /// |
166 | | /// Examples matching error: "variable has incomplete type 'struct s'" |
167 | | /// |
168 | | /// \code |
169 | | /// // expected-error {{variable has incomplete type 'struct s'}} |
170 | | /// // expected-error {{variable has incomplete type}} |
171 | | /// |
172 | | /// // expected-error-re {{variable has type 'struct {{.}}'}} |
173 | | /// // expected-error-re {{variable has type 'struct {{.*}}'}} |
174 | | /// // expected-error-re {{variable has type 'struct {{(.*)}}'}} |
175 | | /// // expected-error-re {{variable has type 'struct{{[[:space:]](.*)}}'}} |
176 | | /// \endcode |
177 | | /// |
178 | | /// VerifyDiagnosticConsumer expects at least one expected-* directive to |
179 | | /// be found inside the source code. If no diagnostics are expected the |
180 | | /// following directive can be used to indicate this: |
181 | | /// |
182 | | /// \code |
183 | | /// // expected-no-diagnostics |
184 | | /// \endcode |
185 | | /// |
186 | | class VerifyDiagnosticConsumer: public DiagnosticConsumer, |
187 | | public CommentHandler { |
188 | | public: |
189 | | /// Directive - Abstract class representing a parsed verify directive. |
190 | | /// |
191 | | class Directive { |
192 | | public: |
193 | | static std::unique_ptr<Directive> |
194 | | create(bool RegexKind, SourceLocation DirectiveLoc, |
195 | | SourceLocation DiagnosticLoc, bool MatchAnyFileAndLine, |
196 | | bool MatchAnyLine, StringRef Text, unsigned Min, unsigned Max); |
197 | | |
198 | | public: |
199 | | /// Constant representing n or more matches. |
200 | | static const unsigned MaxCount = std::numeric_limits<unsigned>::max(); |
201 | | |
202 | | SourceLocation DirectiveLoc; |
203 | | SourceLocation DiagnosticLoc; |
204 | | const std::string Text; |
205 | | unsigned Min, Max; |
206 | | bool MatchAnyLine; |
207 | | bool MatchAnyFileAndLine; // `MatchAnyFileAndLine` implies `MatchAnyLine`. |
208 | | |
209 | | Directive(const Directive &) = delete; |
210 | | Directive &operator=(const Directive &) = delete; |
211 | 300k | virtual ~Directive() = default; |
212 | | |
213 | | // Returns true if directive text is valid. |
214 | | // Otherwise returns false and populates E. |
215 | | virtual bool isValid(std::string &Error) = 0; |
216 | | |
217 | | // Returns true on match. |
218 | | virtual bool match(StringRef S) = 0; |
219 | | |
220 | | protected: |
221 | | Directive(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc, |
222 | | bool MatchAnyFileAndLine, bool MatchAnyLine, StringRef Text, |
223 | | unsigned Min, unsigned Max) |
224 | | : DirectiveLoc(DirectiveLoc), DiagnosticLoc(DiagnosticLoc), Text(Text), |
225 | | Min(Min), Max(Max), MatchAnyLine(MatchAnyLine || MatchAnyFileAndLine), |
226 | 300k | MatchAnyFileAndLine(MatchAnyFileAndLine) { |
227 | 300k | assert(!DirectiveLoc.isInvalid() && "DirectiveLoc is invalid!"); |
228 | 300k | assert((!DiagnosticLoc.isInvalid() || MatchAnyLine) && |
229 | 300k | "DiagnosticLoc is invalid!"); |
230 | 300k | } |
231 | | }; |
232 | | |
233 | | using DirectiveList = std::vector<std::unique_ptr<Directive>>; |
234 | | |
235 | | /// ExpectedData - owns directive objects and deletes on destructor. |
236 | | struct ExpectedData { |
237 | | DirectiveList Errors; |
238 | | DirectiveList Warnings; |
239 | | DirectiveList Remarks; |
240 | | DirectiveList Notes; |
241 | | |
242 | 33.8k | void Reset() { |
243 | 33.8k | Errors.clear(); |
244 | 33.8k | Warnings.clear(); |
245 | 33.8k | Remarks.clear(); |
246 | 33.8k | Notes.clear(); |
247 | 33.8k | } |
248 | | }; |
249 | | |
250 | | enum DirectiveStatus { |
251 | | HasNoDirectives, |
252 | | HasNoDirectivesReported, |
253 | | HasExpectedNoDiagnostics, |
254 | | HasOtherExpectedDirectives |
255 | | }; |
256 | | |
257 | | class MarkerTracker; |
258 | | |
259 | | private: |
260 | | DiagnosticsEngine &Diags; |
261 | | DiagnosticConsumer *PrimaryClient; |
262 | | std::unique_ptr<DiagnosticConsumer> PrimaryClientOwner; |
263 | | std::unique_ptr<TextDiagnosticBuffer> Buffer; |
264 | | std::unique_ptr<MarkerTracker> Markers; |
265 | | const Preprocessor *CurrentPreprocessor = nullptr; |
266 | | const LangOptions *LangOpts = nullptr; |
267 | | SourceManager *SrcManager = nullptr; |
268 | | unsigned ActiveSourceFiles = 0; |
269 | | DirectiveStatus Status; |
270 | | ExpectedData ED; |
271 | | |
272 | | void CheckDiagnostics(); |
273 | | |
274 | 828k | void setSourceManager(SourceManager &SM) { |
275 | 828k | assert((!SrcManager || SrcManager == &SM) && "SourceManager changed!"); |
276 | 828k | SrcManager = &SM; |
277 | 828k | } |
278 | | |
279 | | // These facilities are used for validation in debug builds. |
280 | | class UnparsedFileStatus { |
281 | | llvm::PointerIntPair<const FileEntry *, 1, bool> Data; |
282 | | |
283 | | public: |
284 | | UnparsedFileStatus(const FileEntry *File, bool FoundDirectives) |
285 | 308 | : Data(File, FoundDirectives) {} |
286 | | |
287 | 305 | const FileEntry *getFile() const { return Data.getPointer(); } |
288 | 249 | bool foundDirectives() const { return Data.getInt(); } |
289 | | }; |
290 | | |
291 | | using ParsedFilesMap = llvm::DenseMap<FileID, const FileEntry *>; |
292 | | using UnparsedFilesMap = llvm::DenseMap<FileID, UnparsedFileStatus>; |
293 | | |
294 | | ParsedFilesMap ParsedFiles; |
295 | | UnparsedFilesMap UnparsedFiles; |
296 | | |
297 | | public: |
298 | | /// Create a new verifying diagnostic client, which will issue errors to |
299 | | /// the currently-attached diagnostic client when a diagnostic does not match |
300 | | /// what is expected (as indicated in the source file). |
301 | | VerifyDiagnosticConsumer(DiagnosticsEngine &Diags); |
302 | | ~VerifyDiagnosticConsumer() override; |
303 | | |
304 | | void BeginSourceFile(const LangOptions &LangOpts, |
305 | | const Preprocessor *PP) override; |
306 | | |
307 | | void EndSourceFile() override; |
308 | | |
309 | | enum ParsedStatus { |
310 | | /// File has been processed via HandleComment. |
311 | | IsParsed, |
312 | | |
313 | | /// File has diagnostics and may have directives. |
314 | | IsUnparsed, |
315 | | |
316 | | /// File has diagnostics but guaranteed no directives. |
317 | | IsUnparsedNoDirectives |
318 | | }; |
319 | | |
320 | | /// Update lists of parsed and unparsed files. |
321 | | void UpdateParsedFileStatus(SourceManager &SM, FileID FID, ParsedStatus PS); |
322 | | |
323 | | bool HandleComment(Preprocessor &PP, SourceRange Comment) override; |
324 | | |
325 | | void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, |
326 | | const Diagnostic &Info) override; |
327 | | }; |
328 | | |
329 | | } // namespace clang |
330 | | |
331 | | #endif // LLVM_CLANG_FRONTEND_VERIFYDIAGNOSTICCONSUMER_H |