Home | History | Annotate | Download | only in Frontend
      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 
     21 namespace clang {
     22 
     23 class DiagnosticsEngine;
     24 class TextDiagnosticBuffer;
     25 class 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 /// The simple syntax above allows each specification to match exactly one
     65 /// error.  You can use the extended syntax to customize this. The extended
     66 /// syntax is "expected-<type> <n> {{diag text}}", where \<type> is one of
     67 /// "error", "warning" or "note", and \<n> is a positive integer. This allows
     68 /// the diagnostic to appear as many times as specified. Example:
     69 ///
     70 /// \code
     71 ///   void f(); // expected-note 2 {{previous declaration is here}}
     72 /// \endcode
     73 ///
     74 /// Where the diagnostic is expected to occur a minimum number of times, this
     75 /// can be specified by appending a '+' to the number. Example:
     76 ///
     77 /// \code
     78 ///   void f(); // expected-note 0+ {{previous declaration is here}}
     79 ///   void g(); // expected-note 1+ {{previous declaration is here}}
     80 /// \endcode
     81 ///
     82 /// In the first example, the diagnostic becomes optional, i.e. it will be
     83 /// swallowed if it occurs, but will not generate an error if it does not
     84 /// occur.  In the second example, the diagnostic must occur at least once.
     85 /// As a short-hand, "one or more" can be specified simply by '+'. Example:
     86 ///
     87 /// \code
     88 ///   void g(); // expected-note + {{previous declaration is here}}
     89 /// \endcode
     90 ///
     91 /// A range can also be specified by "<n>-<m>".  Example:
     92 ///
     93 /// \code
     94 ///   void f(); // expected-note 0-1 {{previous declaration is here}}
     95 /// \endcode
     96 ///
     97 /// In this example, the diagnostic may appear only once, if at all.
     98 ///
     99 /// Regex matching mode may be selected by appending '-re' to type, such as:
    100 ///
    101 /// \code
    102 ///   expected-error-re
    103 /// \endcode
    104 ///
    105 /// Examples matching error: "variable has incomplete type 'struct s'"
    106 ///
    107 /// \code
    108 ///   // expected-error {{variable has incomplete type 'struct s'}}
    109 ///   // expected-error {{variable has incomplete type}}
    110 ///
    111 ///   // expected-error-re {{variable has has type 'struct .'}}
    112 ///   // expected-error-re {{variable has has type 'struct .*'}}
    113 ///   // expected-error-re {{variable has has type 'struct (.*)'}}
    114 ///   // expected-error-re {{variable has has type 'struct[[:space:]](.*)'}}
    115 /// \endcode
    116 ///
    117 /// VerifyDiagnosticConsumer expects at least one expected-* directive to
    118 /// be found inside the source code.  If no diagnostics are expected the
    119 /// following directive can be used to indicate this:
    120 ///
    121 /// \code
    122 ///   // expected-no-diagnostics
    123 /// \endcode
    124 ///
    125 class VerifyDiagnosticConsumer: public DiagnosticConsumer,
    126                                 public CommentHandler {
    127 public:
    128   /// Directive - Abstract class representing a parsed verify directive.
    129   ///
    130   class Directive {
    131   public:
    132     static Directive *create(bool RegexKind, SourceLocation DirectiveLoc,
    133                              SourceLocation DiagnosticLoc,
    134                              StringRef Text, unsigned Min, unsigned Max);
    135   public:
    136     /// Constant representing n or more matches.
    137     static const unsigned MaxCount = UINT_MAX;
    138 
    139     SourceLocation DirectiveLoc;
    140     SourceLocation DiagnosticLoc;
    141     const std::string Text;
    142     unsigned Min, Max;
    143 
    144     virtual ~Directive() { }
    145 
    146     // Returns true if directive text is valid.
    147     // Otherwise returns false and populates E.
    148     virtual bool isValid(std::string &Error) = 0;
    149 
    150     // Returns true on match.
    151     virtual bool match(StringRef S) = 0;
    152 
    153   protected:
    154     Directive(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc,
    155               StringRef Text, unsigned Min, unsigned Max)
    156       : DirectiveLoc(DirectiveLoc), DiagnosticLoc(DiagnosticLoc),
    157         Text(Text), Min(Min), Max(Max) {
    158     assert(!DirectiveLoc.isInvalid() && "DirectiveLoc is invalid!");
    159     assert(!DiagnosticLoc.isInvalid() && "DiagnosticLoc is invalid!");
    160     }
    161 
    162   private:
    163     Directive(const Directive &) LLVM_DELETED_FUNCTION;
    164     void operator=(const Directive &) LLVM_DELETED_FUNCTION;
    165   };
    166 
    167   typedef std::vector<Directive*> DirectiveList;
    168 
    169   /// ExpectedData - owns directive objects and deletes on destructor.
    170   ///
    171   struct ExpectedData {
    172     DirectiveList Errors;
    173     DirectiveList Warnings;
    174     DirectiveList Notes;
    175 
    176     ~ExpectedData() {
    177       llvm::DeleteContainerPointers(Errors);
    178       llvm::DeleteContainerPointers(Warnings);
    179       llvm::DeleteContainerPointers(Notes);
    180     }
    181   };
    182 
    183   enum DirectiveStatus {
    184     HasNoDirectives,
    185     HasNoDirectivesReported,
    186     HasExpectedNoDiagnostics,
    187     HasOtherExpectedDirectives
    188   };
    189 
    190 private:
    191   DiagnosticsEngine &Diags;
    192   DiagnosticConsumer *PrimaryClient;
    193   bool OwnsPrimaryClient;
    194   OwningPtr<TextDiagnosticBuffer> Buffer;
    195   const Preprocessor *CurrentPreprocessor;
    196   const LangOptions *LangOpts;
    197   SourceManager *SrcManager;
    198   unsigned ActiveSourceFiles;
    199   DirectiveStatus Status;
    200   ExpectedData ED;
    201 
    202   void CheckDiagnostics();
    203   void setSourceManager(SourceManager &SM) {
    204     assert((!SrcManager || SrcManager == &SM) && "SourceManager changed!");
    205     SrcManager = &SM;
    206   }
    207 
    208 #ifndef NDEBUG
    209   class UnparsedFileStatus {
    210     llvm::PointerIntPair<const FileEntry *, 1, bool> Data;
    211 
    212   public:
    213     UnparsedFileStatus(const FileEntry *File, bool FoundDirectives)
    214       : Data(File, FoundDirectives) {}
    215 
    216     const FileEntry *getFile() const { return Data.getPointer(); }
    217     bool foundDirectives() const { return Data.getInt(); }
    218   };
    219 
    220   typedef llvm::DenseMap<FileID, const FileEntry *> ParsedFilesMap;
    221   typedef llvm::DenseMap<FileID, UnparsedFileStatus> UnparsedFilesMap;
    222 
    223   ParsedFilesMap ParsedFiles;
    224   UnparsedFilesMap UnparsedFiles;
    225 #endif
    226 
    227 public:
    228   /// Create a new verifying diagnostic client, which will issue errors to
    229   /// the currently-attached diagnostic client when a diagnostic does not match
    230   /// what is expected (as indicated in the source file).
    231   VerifyDiagnosticConsumer(DiagnosticsEngine &Diags);
    232   ~VerifyDiagnosticConsumer();
    233 
    234   virtual void BeginSourceFile(const LangOptions &LangOpts,
    235                                const Preprocessor *PP);
    236 
    237   virtual void EndSourceFile();
    238 
    239   enum ParsedStatus {
    240     /// File has been processed via HandleComment.
    241     IsParsed,
    242 
    243     /// File has diagnostics and may have directives.
    244     IsUnparsed,
    245 
    246     /// File has diagnostics but guaranteed no directives.
    247     IsUnparsedNoDirectives
    248   };
    249 
    250   /// \brief Update lists of parsed and unparsed files.
    251   void UpdateParsedFileStatus(SourceManager &SM, FileID FID, ParsedStatus PS);
    252 
    253   virtual bool HandleComment(Preprocessor &PP, SourceRange Comment);
    254 
    255   virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
    256                                 const Diagnostic &Info);
    257 
    258   virtual DiagnosticConsumer *clone(DiagnosticsEngine &Diags) const;
    259 };
    260 
    261 } // end namspace clang
    262 
    263 #endif
    264