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