]> granicus.if.org Git - clang/blobdiff - include/clang/Frontend/VerifyDiagnosticConsumer.h
Header guard canonicalization, clang part.
[clang] / include / clang / Frontend / VerifyDiagnosticConsumer.h
index 0e3bb062e184b1179fea50f16ad4cbd3ca76be08..e9bd68b043be8f03901307d62ffe5f4b9bb69681 100644 (file)
@@ -7,18 +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
@@ -29,11 +33,13 @@ 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:
 ///
@@ -55,6 +61,21 @@ class TextDiagnosticBuffer;
 /// 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
@@ -90,28 +111,42 @@ class TextDiagnosticBuffer;
 ///
 /// In this example, the diagnostic may appear only once, if at all.
 ///
-/// Regex matching mode may be selected by appending '-re' to type. Example:
+/// Regex matching mode may be selected by appending '-re' to type and
+/// including regexes wrapped in double curly braces in the directive, such as:
 ///
-///   expected-error-re
+/// \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:
+///
+/// \code
+///   // expected-no-diagnostics
+/// \endcode
 ///
-class VerifyDiagnosticConsumer: public DiagnosticConsumer {
+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,
+                             SourceLocation DiagnosticLoc, bool MatchAnyLine,
                              StringRef Text, unsigned Min, unsigned Max);
   public:
     /// Constant representing n or more matches.
@@ -121,6 +156,7 @@ public:
     SourceLocation DiagnosticLoc;
     const std::string Text;
     unsigned Min, Max;
+    bool MatchAnyLine;
 
     virtual ~Directive() { }
 
@@ -133,16 +169,16 @@ public:
 
   protected:
     Directive(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc,
-              StringRef Text, unsigned Min, unsigned Max)
+              bool MatchAnyLine, StringRef Text, unsigned Min, unsigned Max)
       : DirectiveLoc(DirectiveLoc), DiagnosticLoc(DiagnosticLoc),
-        Text(Text), Min(Min), Max(Max) {
+        Text(Text), Min(Min), Max(Max), MatchAnyLine(MatchAnyLine) {
     assert(!DirectiveLoc.isInvalid() && "DirectiveLoc is invalid!");
     assert(!DiagnosticLoc.isInvalid() && "DiagnosticLoc is invalid!");
     }
 
   private:
-    Directive(const Directive&); // DO NOT IMPLEMENT
-    void operator=(const Directive&); // DO NOT IMPLEMENT
+    Directive(const Directive &) LLVM_DELETED_FUNCTION;
+    void operator=(const Directive &) LLVM_DELETED_FUNCTION;
   };
 
   typedef std::vector<Directive*> DirectiveList;
@@ -152,24 +188,57 @@ public:
   struct ExpectedData {
     DirectiveList Errors;
     DirectiveList Warnings;
+    DirectiveList Remarks;
     DirectiveList Notes;
 
-    ~ExpectedData() {
+    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;
-  OwningPtr<TextDiagnosticBuffer> Buffer;
-  Preprocessor *CurrentPreprocessor;
+  std::unique_ptr<TextDiagnosticBuffer> Buffer;
+  const Preprocessor *CurrentPreprocessor;
+  const LangOptions *LangOpts;
+  SourceManager *SrcManager;
+  unsigned ActiveSourceFiles;
+  DirectiveStatus Status;
   ExpectedData ED;
-  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
@@ -178,15 +247,29 @@ public:
   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 Diagnostic &Info);
-  
-  virtual DiagnosticConsumer *clone(DiagnosticsEngine &Diags) const;
+  void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
+                        const Diagnostic &Info) override;
 };
 
 } // end namspace clang