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 /// 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 ///
    137 class VerifyDiagnosticConsumer: public DiagnosticConsumer,
    138                                 public CommentHandler {
    139 public:
    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 
    202 private:
    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 
    239 public:
    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