Allow -verify directives to be filtered by preprocessing.

This is accomplished by making VerifyDiagnosticsConsumer a CommentHandler,
which then only reads the -verify directives that are actually in live
blocks of code. It also makes it simpler to handle -verify directives that
appear in header files, though we still have to manually reparse some files
depending on how they are generated.

This requires some test changes. In particular, all PCH tests now have their
-verify directives outside the "header" portion of the file, using the @line
syntax added in r159978. Other tests have been modified mostly to make it
clear what is being tested, and to prevent polluting the expected output with
the directives themselves.

Patch by Andy Gibbs! (with slight modifications)

The new Frontend/verify-* tests exercise the functionality of this commit,
as well as r159978, r159979, and r160053 (Andy's other -verify enhancements).

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@160068 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Frontend/VerifyDiagnosticConsumer.cpp b/lib/Frontend/VerifyDiagnosticConsumer.cpp
index 236b700..8aa65f1 100644
--- a/lib/Frontend/VerifyDiagnosticConsumer.cpp
+++ b/lib/Frontend/VerifyDiagnosticConsumer.cpp
@@ -11,6 +11,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "clang/Basic/FileManager.h"
 #include "clang/Frontend/VerifyDiagnosticConsumer.h"
 #include "clang/Frontend/FrontendDiagnostic.h"
 #include "clang/Frontend/TextDiagnosticBuffer.h"
@@ -43,15 +44,15 @@
 
 void VerifyDiagnosticConsumer::BeginSourceFile(const LangOptions &LangOpts,
                                                const Preprocessor *PP) {
-  // FIXME: Const hack, we screw up the preprocessor but in practice its ok
-  // because it doesn't get reused. It would be better if we could make a copy
-  // though.
-  CurrentPreprocessor = const_cast<Preprocessor*>(PP);
+  CurrentPreprocessor = PP;
+  if (PP) const_cast<Preprocessor*>(PP)->addCommentHandler(this);
 
   PrimaryClient->BeginSourceFile(LangOpts, PP);
 }
 
 void VerifyDiagnosticConsumer::EndSourceFile() {
+  if (CurrentPreprocessor)
+    const_cast<Preprocessor*>(CurrentPreprocessor)->removeCommentHandler(this);
   CheckDiagnostics();
 
   PrimaryClient->EndSourceFile();
@@ -61,9 +62,9 @@
 
 void VerifyDiagnosticConsumer::HandleDiagnostic(
       DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) {
-  if (FirstErrorFID.isInvalid() && Info.hasSourceManager()) {
+  if (Info.hasSourceManager()) {
     const SourceManager &SM = Info.getSourceManager();
-    FirstErrorFID = SM.getFileID(Info.getLocation());
+    FilesWithDiagnostics.insert(SM.getFileID(Info.getLocation()));
   }
   // Send the diagnostic to the buffer, we will check it once we reach the end
   // of the source file (or are destructed).
@@ -122,8 +123,8 @@
 class ParseHelper
 {
 public:
-  ParseHelper(const char *Begin, const char *End)
-    : Begin(Begin), End(End), C(Begin), P(Begin), PEnd(NULL) { }
+  ParseHelper(StringRef S)
+    : Begin(S.begin()), End(S.end()), C(Begin), P(Begin), PEnd(NULL) { }
 
   // Return true if string literal is next.
   bool Next(StringRef S) {
@@ -190,11 +191,12 @@
 /// ParseDirective - Go through the comment and see if it indicates expected
 /// diagnostics. If so, then put them in the appropriate directive list.
 ///
-static void ParseDirective(const char *CommentStart, unsigned CommentLen,
-                           ExpectedData &ED, SourceManager &SM,
+/// Returns true if any valid directives were found.
+static bool ParseDirective(StringRef S, ExpectedData &ED, SourceManager &SM,
                            SourceLocation Pos, DiagnosticsEngine &Diags) {
   // A single comment may contain multiple directives.
-  for (ParseHelper PH(CommentStart, CommentStart+CommentLen); !PH.Done();) {
+  bool FoundDirective = false;
+  for (ParseHelper PH(S); !PH.Done();) {
     // Search for token: expected
     if (!PH.Search("expected"))
       break;
@@ -329,19 +331,77 @@
     Directive *D = Directive::create(RegexKind, Pos, ExpectedLoc, Text,
                                      Min, Max);
     std::string Error;
-    if (D->isValid(Error))
+    if (D->isValid(Error)) {
       DL->push_back(D);
-    else {
+      FoundDirective = true;
+    } else {
       Diags.Report(Pos.getLocWithOffset(ContentBegin-PH.Begin),
                    diag::err_verify_invalid_content)
         << KindStr << Error;
     }
   }
+
+  return FoundDirective;
+}
+
+/// HandleComment - Hook into the preprocessor and extract comments containing
+///  expected errors and warnings.
+bool VerifyDiagnosticConsumer::HandleComment(Preprocessor &PP,
+                                             SourceRange Comment) {
+  SourceManager &SM = PP.getSourceManager();
+  SourceLocation CommentBegin = Comment.getBegin();
+
+  const char *CommentRaw = SM.getCharacterData(CommentBegin);
+  StringRef C(CommentRaw, SM.getCharacterData(Comment.getEnd()) - CommentRaw);
+
+  if (C.empty())
+    return false;
+
+  // Fold any "\<EOL>" sequences
+  size_t loc = C.find('\\');
+  if (loc == StringRef::npos) {
+    if (ParseDirective(C, ED, SM, CommentBegin, PP.getDiagnostics()))
+      if (const FileEntry *E = SM.getFileEntryForID(SM.getFileID(CommentBegin)))
+        FilesWithDirectives.insert(E);
+    return false;
+  }
+
+  std::string C2;
+  C2.reserve(C.size());
+
+  for (size_t last = 0;; loc = C.find('\\', last)) {
+    if (loc == StringRef::npos || loc == C.size()) {
+      C2 += C.substr(last);
+      break;
+    }
+    C2 += C.substr(last, loc-last);
+    last = loc + 1;
+
+    if (C[last] == '\n' || C[last] == '\r') {
+      ++last;
+
+      // Escape \r\n  or \n\r, but not \n\n.
+      if (last < C.size())
+        if (C[last] == '\n' || C[last] == '\r')
+          if (C[last] != C[last-1])
+            ++last;
+    } else {
+      // This was just a normal backslash.
+      C2 += '\\';
+    }
+  }
+
+  if (!C2.empty())
+    if (ParseDirective(C2, ED, SM, CommentBegin, PP.getDiagnostics()))
+      if (const FileEntry *E = SM.getFileEntryForID(SM.getFileID(CommentBegin)))
+        FilesWithDirectives.insert(E);
+  return false;
 }
 
 /// FindExpectedDiags - Lex the main source file to find all of the
 //   expected errors and warnings.
-static void FindExpectedDiags(Preprocessor &PP, ExpectedData &ED, FileID FID) {
+static void FindExpectedDiags(const Preprocessor &PP, ExpectedData &ED,
+                              FileID FID) {
   // Create a raw lexer to pull all the comments out of FID.
   if (FID.isInvalid())
     return;
@@ -364,8 +424,7 @@
     if (Comment.empty()) continue;
 
     // Find all expected errors/warnings/notes.
-    ParseDirective(&Comment[0], Comment.size(), ED, SM, Tok.getLocation(),
-                   PP.getDiagnostics());
+    ParseDirective(Comment, ED, SM, Tok.getLocation(), PP.getDiagnostics());
   };
 }
 
@@ -497,18 +556,21 @@
   // markers. If not then any diagnostics are unexpected.
   if (CurrentPreprocessor) {
     SourceManager &SM = CurrentPreprocessor->getSourceManager();
-    // Extract expected-error strings from main file.
-    FindExpectedDiags(*CurrentPreprocessor, ED, SM.getMainFileID());
-    // Only check for expectations in other diagnostic locations
-    // if they are not the main file (via ID or FileEntry) - the main
-    // file has already been looked at, and its expectations must not
-    // be added twice.
-    if (!FirstErrorFID.isInvalid() && FirstErrorFID != SM.getMainFileID()
-        && (!SM.getFileEntryForID(FirstErrorFID)
-            || (SM.getFileEntryForID(FirstErrorFID) !=
-                SM.getFileEntryForID(SM.getMainFileID())))) {
-      FindExpectedDiags(*CurrentPreprocessor, ED, FirstErrorFID);
-      FirstErrorFID = FileID();
+    // Only check for expectations in other diagnostic locations not
+    // captured during normal parsing.
+    // FIXME: This check is currently necessary while synthesized files may
+    // not have their expected-* directives captured during parsing.  These
+    // cases should be fixed and the following loop replaced with one which
+    // checks only during a debug build and asserts on a mismatch.
+    for (FilesWithDiagnosticsSet::iterator I = FilesWithDiagnostics.begin(),
+                                         End = FilesWithDiagnostics.end();
+            I != End; ++I) {
+      const FileEntry *E = SM.getFileEntryForID(*I);
+      if (!E || !FilesWithDirectives.count(E)) {
+        if (E)
+          FilesWithDirectives.insert(E);
+        FindExpectedDiags(*CurrentPreprocessor, ED, *I);
+      }
     }
 
     // Check that the expected diagnostics occurred.