1 //===- VerifyDiagnosticConsumer.h - Verifying Diagnostic Client -*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 #ifndef LLVM_CLANG_FRONTEND_VERIFYDIAGNOSTICCONSUMER_H
11 #define LLVM_CLANG_FRONTEND_VERIFYDIAGNOSTICCONSUMER_H
13 #include "clang/Basic/Diagnostic.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"
31 class TextDiagnosticBuffer;
33 /// VerifyDiagnosticConsumer - Create a diagnostic client which will use
34 /// markers in the input source to check that all the emitted diagnostics match
37 /// USING THE DIAGNOSTIC CHECKER:
39 /// Indicating that a line expects an error or a warning is simple. Put a
40 /// comment on the line that has the diagnostic, use:
43 /// expected-{error,warning,remark,note}
46 /// to tag if it's an expected error, remark or warning, and place the expected
47 /// text between {{ and }} markers. The full text doesn't have to be included,
48 /// only enough to ensure that the correct diagnostic was emitted.
50 /// Here's an example:
53 /// int A = B; // expected-error {{use of undeclared identifier 'B'}}
56 /// You can place as many diagnostics on one line as you wish. To make the code
57 /// more readable, you can use slash-newline to separate out the diagnostics.
59 /// Alternatively, it is possible to specify the line on which the diagnostic
60 /// should appear by appending "@<line>" to "expected-<type>", for example:
63 /// #warning some text
64 /// // expected-warning@10 {{some text}}
67 /// The line number may be absolute (as above), or relative to the current
68 /// line by prefixing the number with either '+' or '-'.
70 /// If the diagnostic is generated in a separate file, for example in a shared
71 /// header file, it may be beneficial to be able to declare the file in which
72 /// the diagnostic will appear, rather than placing the expected-* directive in
73 /// the actual file itself. This can be done using the following syntax:
76 /// // expected-error@path/include.h:15 {{error message}}
79 /// The path can be absolute or relative and the same search paths will be used
80 /// as for #include directives. The line number in an external file may be
81 /// substituted with '*' meaning that any line number will match (useful where
82 /// the included file is, for example, a system header where the actual line
83 /// number may change and is not critical).
85 /// The simple syntax above allows each specification to match exactly one
86 /// error. You can use the extended syntax to customize this. The extended
87 /// syntax is "expected-<type> <n> {{diag text}}", where \<type> is one of
88 /// "error", "warning" or "note", and \<n> is a positive integer. This allows
89 /// the diagnostic to appear as many times as specified. Example:
92 /// void f(); // expected-note 2 {{previous declaration is here}}
95 /// Where the diagnostic is expected to occur a minimum number of times, this
96 /// can be specified by appending a '+' to the number. Example:
99 /// void f(); // expected-note 0+ {{previous declaration is here}}
100 /// void g(); // expected-note 1+ {{previous declaration is here}}
103 /// In the first example, the diagnostic becomes optional, i.e. it will be
104 /// swallowed if it occurs, but will not generate an error if it does not
105 /// occur. In the second example, the diagnostic must occur at least once.
106 /// As a short-hand, "one or more" can be specified simply by '+'. Example:
109 /// void g(); // expected-note + {{previous declaration is here}}
112 /// A range can also be specified by "<n>-<m>". Example:
115 /// void f(); // expected-note 0-1 {{previous declaration is here}}
118 /// In this example, the diagnostic may appear only once, if at all.
120 /// Regex matching mode may be selected by appending '-re' to type and
121 /// including regexes wrapped in double curly braces in the directive, such as:
124 /// expected-error-re {{format specifies type 'wchar_t **' (aka '{{.+}}')}}
127 /// Examples matching error: "variable has incomplete type 'struct s'"
130 /// // expected-error {{variable has incomplete type 'struct s'}}
131 /// // expected-error {{variable has incomplete type}}
133 /// // expected-error-re {{variable has type 'struct {{.}}'}}
134 /// // expected-error-re {{variable has type 'struct {{.*}}'}}
135 /// // expected-error-re {{variable has type 'struct {{(.*)}}'}}
136 /// // expected-error-re {{variable has type 'struct{{[[:space:]](.*)}}'}}
139 /// VerifyDiagnosticConsumer expects at least one expected-* directive to
140 /// be found inside the source code. If no diagnostics are expected the
141 /// following directive can be used to indicate this:
144 /// // expected-no-diagnostics
147 class VerifyDiagnosticConsumer: public DiagnosticConsumer,
148 public CommentHandler {
150 /// Directive - Abstract class representing a parsed verify directive.
154 static std::unique_ptr<Directive> create(bool RegexKind,
155 SourceLocation DirectiveLoc,
156 SourceLocation DiagnosticLoc,
157 bool MatchAnyLine, StringRef Text,
158 unsigned Min, unsigned Max);
161 /// Constant representing n or more matches.
162 static const unsigned MaxCount = std::numeric_limits<unsigned>::max();
164 SourceLocation DirectiveLoc;
165 SourceLocation DiagnosticLoc;
166 const std::string Text;
170 Directive(const Directive &) = delete;
171 Directive &operator=(const Directive &) = delete;
172 virtual ~Directive() = default;
174 // Returns true if directive text is valid.
175 // Otherwise returns false and populates E.
176 virtual bool isValid(std::string &Error) = 0;
178 // Returns true on match.
179 virtual bool match(StringRef S) = 0;
182 Directive(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc,
183 bool MatchAnyLine, StringRef Text, unsigned Min, unsigned Max)
184 : DirectiveLoc(DirectiveLoc), DiagnosticLoc(DiagnosticLoc),
185 Text(Text), Min(Min), Max(Max), MatchAnyLine(MatchAnyLine) {
186 assert(!DirectiveLoc.isInvalid() && "DirectiveLoc is invalid!");
187 assert((!DiagnosticLoc.isInvalid() || MatchAnyLine) &&
188 "DiagnosticLoc is invalid!");
192 using DirectiveList = std::vector<std::unique_ptr<Directive>>;
194 /// ExpectedData - owns directive objects and deletes on destructor.
195 struct ExpectedData {
196 DirectiveList Errors;
197 DirectiveList Warnings;
198 DirectiveList Remarks;
209 enum DirectiveStatus {
211 HasNoDirectivesReported,
212 HasExpectedNoDiagnostics,
213 HasOtherExpectedDirectives
217 DiagnosticsEngine &Diags;
218 DiagnosticConsumer *PrimaryClient;
219 std::unique_ptr<DiagnosticConsumer> PrimaryClientOwner;
220 std::unique_ptr<TextDiagnosticBuffer> Buffer;
221 const Preprocessor *CurrentPreprocessor = nullptr;
222 const LangOptions *LangOpts = nullptr;
223 SourceManager *SrcManager = nullptr;
224 unsigned ActiveSourceFiles = 0;
225 DirectiveStatus Status;
228 void CheckDiagnostics();
230 void setSourceManager(SourceManager &SM) {
231 assert((!SrcManager || SrcManager == &SM) && "SourceManager changed!");
235 // These facilities are used for validation in debug builds.
236 class UnparsedFileStatus {
237 llvm::PointerIntPair<const FileEntry *, 1, bool> Data;
240 UnparsedFileStatus(const FileEntry *File, bool FoundDirectives)
241 : Data(File, FoundDirectives) {}
243 const FileEntry *getFile() const { return Data.getPointer(); }
244 bool foundDirectives() const { return Data.getInt(); }
247 using ParsedFilesMap = llvm::DenseMap<FileID, const FileEntry *>;
248 using UnparsedFilesMap = llvm::DenseMap<FileID, UnparsedFileStatus>;
250 ParsedFilesMap ParsedFiles;
251 UnparsedFilesMap UnparsedFiles;
254 /// Create a new verifying diagnostic client, which will issue errors to
255 /// the currently-attached diagnostic client when a diagnostic does not match
256 /// what is expected (as indicated in the source file).
257 VerifyDiagnosticConsumer(DiagnosticsEngine &Diags);
258 ~VerifyDiagnosticConsumer() override;
260 void BeginSourceFile(const LangOptions &LangOpts,
261 const Preprocessor *PP) override;
263 void EndSourceFile() override;
266 /// File has been processed via HandleComment.
269 /// File has diagnostics and may have directives.
272 /// File has diagnostics but guaranteed no directives.
273 IsUnparsedNoDirectives
276 /// Update lists of parsed and unparsed files.
277 void UpdateParsedFileStatus(SourceManager &SM, FileID FID, ParsedStatus PS);
279 bool HandleComment(Preprocessor &PP, SourceRange Comment) override;
281 void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
282 const Diagnostic &Info) override;
287 #endif // LLVM_CLANG_FRONTEND_VERIFYDIAGNOSTICCONSUMER_H