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