Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
14 | pmbaty | 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 | 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 | MatchAnyFileAndLine(MatchAnyFileAndLine) { |
||
227 | assert(!DirectiveLoc.isInvalid() && "DirectiveLoc is invalid!"); |
||
228 | assert((!DiagnosticLoc.isInvalid() || MatchAnyLine) && |
||
229 | "DiagnosticLoc is invalid!"); |
||
230 | } |
||
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 | void Reset() { |
||
243 | Errors.clear(); |
||
244 | Warnings.clear(); |
||
245 | Remarks.clear(); |
||
246 | Notes.clear(); |
||
247 | } |
||
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 | void setSourceManager(SourceManager &SM) { |
||
275 | assert((!SrcManager || SrcManager == &SM) && "SourceManager changed!"); |
||
276 | SrcManager = &SM; |
||
277 | } |
||
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 | : Data(File, FoundDirectives) {} |
||
286 | |||
287 | const FileEntry *getFile() const { return Data.getPointer(); } |
||
288 | 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 |