]> granicus.if.org Git - clang/blobdiff - include/clang/Frontend/VerifyDiagnosticConsumer.h
Header guard canonicalization, clang part.
[clang] / include / clang / Frontend / VerifyDiagnosticConsumer.h
index 1ef59ccb781d1b725fb33e55e0c031a1454dc38e..e9bd68b043be8f03901307d62ffe5f4b9bb69681 100644 (file)
@@ -7,16 +7,22 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef LLVM_CLANG_FRONTEND_VERIFYDIAGNOSTICSCLIENT_H
-#define LLVM_CLANG_FRONTEND_VERIFYDIAGNOSTICSCLIENT_H
+#ifndef LLVM_CLANG_FRONTEND_VERIFYDIAGNOSTICCONSUMER_H
+#define LLVM_CLANG_FRONTEND_VERIFYDIAGNOSTICCONSUMER_H
 
 #include "clang/Basic/Diagnostic.h"
-#include "llvm/ADT/OwningPtr.h"
+#include "clang/Lex/Preprocessor.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/PointerIntPair.h"
+#include "llvm/ADT/STLExtras.h"
+#include <climits>
+#include <memory>
 
 namespace clang {
 
 class DiagnosticsEngine;
 class TextDiagnosticBuffer;
+class FileEntry;
 
 /// VerifyDiagnosticConsumer - Create a diagnostic client which will use
 /// markers in the input source to check that all the emitted diagnostics match
@@ -27,67 +33,243 @@ class TextDiagnosticBuffer;
 /// Indicating that a line expects an error or a warning is simple. Put a
 /// comment on the line that has the diagnostic, use:
 ///
-///     expected-{error,warning,note}
+/// \code
+///   expected-{error,warning,remark,note}
+/// \endcode
 ///
-/// to tag if it's an expected error or warning, and place the expected text
-/// between {{ and }} markers. The full text doesn't have to be included, only
-/// enough to ensure that the correct diagnostic was emitted.
+/// to tag if it's an expected error, remark or warning, and place the expected
+/// text between {{ and }} markers. The full text doesn't have to be included,
+/// only enough to ensure that the correct diagnostic was emitted.
 ///
 /// Here's an example:
 ///
+/// \code
 ///   int A = B; // expected-error {{use of undeclared identifier 'B'}}
+/// \endcode
 ///
 /// You can place as many diagnostics on one line as you wish. To make the code
 /// more readable, you can use slash-newline to separate out the diagnostics.
 ///
+/// Alternatively, it is possible to specify the line on which the diagnostic
+/// should appear by appending "@<line>" to "expected-<type>", for example:
+///
+/// \code
+///   #warning some text
+///   // expected-warning@10 {{some text}}
+/// \endcode
+///
+/// The line number may be absolute (as above), or relative to the current
+/// line by prefixing the number with either '+' or '-'.
+///
+/// If the diagnostic is generated in a separate file, for example in a shared
+/// header file, it may be beneficial to be able to declare the file in which
+/// the diagnostic will appear, rather than placing the expected-* directive in
+/// the actual file itself.  This can be done using the following syntax:
+///
+/// \code
+///   // expected-error@path/include.h:15 {{error message}}
+/// \endcode
+///
+/// The path can be absolute or relative and the same search paths will be used
+/// as for #include directives.  The line number in an external file may be
+/// substituted with '*' meaning that any line number will match (useful where
+/// the included file is, for example, a system header where the actual line
+/// number may change and is not critical).
+///
 /// The simple syntax above allows each specification to match exactly one
 /// error.  You can use the extended syntax to customize this. The extended
-/// syntax is "expected-<type> <n> {{diag text}}", where <type> is one of
-/// "error", "warning" or "note", and <n> is a positive integer. This allows the
-/// diagnostic to appear as many times as specified. Example:
+/// syntax is "expected-<type> <n> {{diag text}}", where \<type> is one of
+/// "error", "warning" or "note", and \<n> is a positive integer. This allows
+/// the diagnostic to appear as many times as specified. Example:
 ///
+/// \code
 ///   void f(); // expected-note 2 {{previous declaration is here}}
+/// \endcode
+///
+/// Where the diagnostic is expected to occur a minimum number of times, this
+/// can be specified by appending a '+' to the number. Example:
+///
+/// \code
+///   void f(); // expected-note 0+ {{previous declaration is here}}
+///   void g(); // expected-note 1+ {{previous declaration is here}}
+/// \endcode
+///
+/// In the first example, the diagnostic becomes optional, i.e. it will be
+/// swallowed if it occurs, but will not generate an error if it does not
+/// occur.  In the second example, the diagnostic must occur at least once.
+/// As a short-hand, "one or more" can be specified simply by '+'. Example:
 ///
-/// Regex matching mode may be selected by appending '-re' to type. Example:
+/// \code
+///   void g(); // expected-note + {{previous declaration is here}}
+/// \endcode
 ///
-///   expected-error-re
+/// A range can also be specified by "<n>-<m>".  Example:
+///
+/// \code
+///   void f(); // expected-note 0-1 {{previous declaration is here}}
+/// \endcode
+///
+/// In this example, the diagnostic may appear only once, if at all.
+///
+/// Regex matching mode may be selected by appending '-re' to type and
+/// including regexes wrapped in double curly braces in the directive, such as:
+///
+/// \code
+///   expected-error-re {{format specifies type 'wchar_t **' (aka '{{.+}}')}}
+/// \endcode
 ///
 /// Examples matching error: "variable has incomplete type 'struct s'"
 ///
+/// \code
 ///   // expected-error {{variable has incomplete type 'struct s'}}
 ///   // expected-error {{variable has incomplete type}}
 ///
-///   // expected-error-re {{variable has has type 'struct .'}}
-///   // expected-error-re {{variable has has type 'struct .*'}}
-///   // expected-error-re {{variable has has type 'struct (.*)'}}
-///   // expected-error-re {{variable has has type 'struct[[:space:]](.*)'}}
+///   // expected-error-re {{variable has type 'struct {{.}}'}}
+///   // expected-error-re {{variable has type 'struct {{.*}}'}}
+///   // expected-error-re {{variable has type 'struct {{(.*)}}'}}
+///   // expected-error-re {{variable has type 'struct{{[[:space:]](.*)}}'}}
+/// \endcode
+///
+/// VerifyDiagnosticConsumer expects at least one expected-* directive to
+/// be found inside the source code.  If no diagnostics are expected the
+/// following directive can be used to indicate this:
 ///
-class VerifyDiagnosticConsumer: public DiagnosticConsumer {
+/// \code
+///   // expected-no-diagnostics
+/// \endcode
+///
+class VerifyDiagnosticConsumer: public DiagnosticConsumer,
+                                public CommentHandler {
 public:
+  /// Directive - Abstract class representing a parsed verify directive.
+  ///
+  class Directive {
+  public:
+    static Directive *create(bool RegexKind, SourceLocation DirectiveLoc,
+                             SourceLocation DiagnosticLoc, bool MatchAnyLine,
+                             StringRef Text, unsigned Min, unsigned Max);
+  public:
+    /// Constant representing n or more matches.
+    static const unsigned MaxCount = UINT_MAX;
+
+    SourceLocation DirectiveLoc;
+    SourceLocation DiagnosticLoc;
+    const std::string Text;
+    unsigned Min, Max;
+    bool MatchAnyLine;
+
+    virtual ~Directive() { }
+
+    // Returns true if directive text is valid.
+    // Otherwise returns false and populates E.
+    virtual bool isValid(std::string &Error) = 0;
+
+    // Returns true on match.
+    virtual bool match(StringRef S) = 0;
+
+  protected:
+    Directive(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc,
+              bool MatchAnyLine, StringRef Text, unsigned Min, unsigned Max)
+      : DirectiveLoc(DirectiveLoc), DiagnosticLoc(DiagnosticLoc),
+        Text(Text), Min(Min), Max(Max), MatchAnyLine(MatchAnyLine) {
+    assert(!DirectiveLoc.isInvalid() && "DirectiveLoc is invalid!");
+    assert(!DiagnosticLoc.isInvalid() && "DiagnosticLoc is invalid!");
+    }
+
+  private:
+    Directive(const Directive &) LLVM_DELETED_FUNCTION;
+    void operator=(const Directive &) LLVM_DELETED_FUNCTION;
+  };
+
+  typedef std::vector<Directive*> DirectiveList;
+
+  /// ExpectedData - owns directive objects and deletes on destructor.
+  ///
+  struct ExpectedData {
+    DirectiveList Errors;
+    DirectiveList Warnings;
+    DirectiveList Remarks;
+    DirectiveList Notes;
+
+    void Reset() {
+      llvm::DeleteContainerPointers(Errors);
+      llvm::DeleteContainerPointers(Warnings);
+      llvm::DeleteContainerPointers(Remarks);
+      llvm::DeleteContainerPointers(Notes);
+    }
+
+    ~ExpectedData() { Reset(); }
+  };
+
+  enum DirectiveStatus {
+    HasNoDirectives,
+    HasNoDirectivesReported,
+    HasExpectedNoDiagnostics,
+    HasOtherExpectedDirectives
+  };
+
+private:
   DiagnosticsEngine &Diags;
   DiagnosticConsumer *PrimaryClient;
   bool OwnsPrimaryClient;
-  llvm::OwningPtr<TextDiagnosticBuffer> Buffer;
-  Preprocessor *CurrentPreprocessor;
+  std::unique_ptr<TextDiagnosticBuffer> Buffer;
+  const Preprocessor *CurrentPreprocessor;
+  const LangOptions *LangOpts;
+  SourceManager *SrcManager;
+  unsigned ActiveSourceFiles;
+  DirectiveStatus Status;
+  ExpectedData ED;
 
-private:
-  FileID FirstErrorFID; // FileID of first diagnostic
   void CheckDiagnostics();
+  void setSourceManager(SourceManager &SM) {
+    assert((!SrcManager || SrcManager == &SM) && "SourceManager changed!");
+    SrcManager = &SM;
+  }
+
+  // These facilities are used for validation in debug builds.
+  class UnparsedFileStatus {
+    llvm::PointerIntPair<const FileEntry *, 1, bool> Data;
+  public:
+    UnparsedFileStatus(const FileEntry *File, bool FoundDirectives)
+      : Data(File, FoundDirectives) {}
+    const FileEntry *getFile() const { return Data.getPointer(); }
+    bool foundDirectives() const { return Data.getInt(); }
+  };
+  typedef llvm::DenseMap<FileID, const FileEntry *> ParsedFilesMap;
+  typedef llvm::DenseMap<FileID, UnparsedFileStatus> UnparsedFilesMap;
+  ParsedFilesMap ParsedFiles;
+  UnparsedFilesMap UnparsedFiles;
 
 public:
-  /// Create a new verifying diagnostic client, which will issue errors to \arg
+  /// Create a new verifying diagnostic client, which will issue errors to
   /// the currently-attached diagnostic client when a diagnostic does not match 
   /// what is expected (as indicated in the source file).
   VerifyDiagnosticConsumer(DiagnosticsEngine &Diags);
   ~VerifyDiagnosticConsumer();
 
-  virtual void BeginSourceFile(const LangOptions &LangOpts,
-                               const Preprocessor *PP);
+  void BeginSourceFile(const LangOptions &LangOpts,
+                       const Preprocessor *PP) override;
+
+  void EndSourceFile() override;
+
+  enum ParsedStatus {
+    /// File has been processed via HandleComment.
+    IsParsed,
+
+    /// File has diagnostics and may have directives.
+    IsUnparsed,
+
+    /// File has diagnostics but guaranteed no directives.
+    IsUnparsedNoDirectives
+  };
+
+  /// \brief Update lists of parsed and unparsed files.
+  void UpdateParsedFileStatus(SourceManager &SM, FileID FID, ParsedStatus PS);
 
-  virtual void EndSourceFile();
+  bool HandleComment(Preprocessor &PP, SourceRange Comment) override;
 
-  virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
-                                const DiagnosticInfo &Info);
+  void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
+                        const Diagnostic &Info) override;
 };
 
 } // end namspace clang