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