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