Extended VerifyDiagnosticConsumer to also verify source file for diagnostic.

VerifyDiagnosticConsumer previously would not check that the diagnostic and
its matching directive referenced the same source file.  Common practice was
to create directives that referenced other files but only by line number,
and this led to problems such as when the file containing the directive
didn't have enough lines to match the location of the diagnostic in the
other file, leading to bizarre file formatting and other oddities.

This patch causes VerifyDiagnosticConsumer to match source files as well as
line numbers.  Therefore, a new syntax is made available for directives, for
example:

// expected-error@file:line {{diagnostic message}}

This extends the @line feature where "file" is the file where the diagnostic
is generated.  The @line syntax is still available and uses the current file
for the diagnostic.  "file" can be specified either as a relative or absolute
path - although the latter has less usefulness, I think!  The #include search
paths will be used to locate the file and if it is not found an error will be
generated.

The new check is not optional: if the directive is in a different file to the
diagnostic, the file must be specified.  Therefore, a number of test-cases
have been updated with regard to this.

This closes out PR15613.


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@179677 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Frontend/VerifyDiagnosticConsumer.cpp b/lib/Frontend/VerifyDiagnosticConsumer.cpp
index 82f6e91..4f05aef 100644
--- a/lib/Frontend/VerifyDiagnosticConsumer.cpp
+++ b/lib/Frontend/VerifyDiagnosticConsumer.cpp
@@ -278,8 +278,10 @@
 ///
 /// Returns true if any valid directives were found.
 static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM,
-                           SourceLocation Pos, DiagnosticsEngine &Diags,
+                           Preprocessor *PP, SourceLocation Pos,
                            VerifyDiagnosticConsumer::DirectiveStatus &Status) {
+  DiagnosticsEngine &Diags = PP ? PP->getDiagnostics() : SM.getDiagnostics();
+
   // A single comment may contain multiple directives.
   bool FoundDirective = false;
   for (ParseHelper PH(S); !PH.Done();) {
@@ -353,10 +355,30 @@
           else ExpectedLine -= Line;
           ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), ExpectedLine, 1);
         }
-      } else {
+      } else if (PH.Next(Line)) {
         // Absolute line number.
-        if (PH.Next(Line) && Line > 0)
+        if (Line > 0)
           ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), Line, 1);
+      } else if (PP && PH.Search(":")) {
+        // Specific source file.
+        StringRef Filename(PH.C, PH.P-PH.C);
+        PH.Advance();
+
+        // Lookup file via Preprocessor, like a #include.
+        const DirectoryLookup *CurDir;
+        const FileEntry *FE = PP->LookupFile(Filename, false, NULL, CurDir,
+                                             NULL, NULL, 0);
+        if (!FE) {
+          Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
+                       diag::err_verify_missing_file) << Filename << KindStr;
+          continue;
+        }
+
+        if (SM.translateFile(FE).isInvalid())
+          SM.createFileID(FE, Pos, SrcMgr::C_User);
+
+        if (PH.Next(Line) && Line > 0)
+          ExpectedLoc = SM.translateFileLineCol(FE, Line, 1);
       }
 
       if (ExpectedLoc.isInvalid()) {
@@ -465,7 +487,7 @@
   // Fold any "\<EOL>" sequences
   size_t loc = C.find('\\');
   if (loc == StringRef::npos) {
-    ParseDirective(C, &ED, SM, CommentBegin, PP.getDiagnostics(), Status);
+    ParseDirective(C, &ED, SM, &PP, CommentBegin, Status);
     return false;
   }
 
@@ -495,7 +517,7 @@
   }
 
   if (!C2.empty())
-    ParseDirective(C2, &ED, SM, CommentBegin, PP.getDiagnostics(), Status);
+    ParseDirective(C2, &ED, SM, &PP, CommentBegin, Status);
   return false;
 }
 
@@ -530,8 +552,7 @@
     if (Comment.empty()) continue;
 
     // Find first directive.
-    if (ParseDirective(Comment, 0, SM, Tok.getLocation(),
-                       SM.getDiagnostics(), Status))
+    if (ParseDirective(Comment, 0, SM, 0, Tok.getLocation(), Status))
       return true;
   }
   return false;
@@ -551,8 +572,13 @@
   for (const_diag_iterator I = diag_begin, E = diag_end; I != E; ++I) {
     if (I->first.isInvalid() || !SourceMgr)
       OS << "\n  (frontend)";
-    else
-      OS << "\n  Line " << SourceMgr->getPresumedLineNumber(I->first);
+    else {
+      OS << "\n ";
+      if (const FileEntry *File = SourceMgr->getFileEntryForID(
+                                                SourceMgr->getFileID(I->first)))
+        OS << " File " << File->getName();
+      OS << " Line " << SourceMgr->getPresumedLineNumber(I->first);
+    }
     OS << ": " << I->second;
   }
 
@@ -572,11 +598,12 @@
   llvm::raw_svector_ostream OS(Fmt);
   for (DirectiveList::iterator I = DL.begin(), E = DL.end(); I != E; ++I) {
     Directive &D = **I;
-    OS << "\n  Line " << SourceMgr.getPresumedLineNumber(D.DiagnosticLoc);
+    OS << "\n  File " << SourceMgr.getFilename(D.DiagnosticLoc)
+          << " Line " << SourceMgr.getPresumedLineNumber(D.DiagnosticLoc);
     if (D.DirectiveLoc != D.DiagnosticLoc)
       OS << " (directive at "
-         << SourceMgr.getFilename(D.DirectiveLoc) << ":"
-         << SourceMgr.getPresumedLineNumber(D.DirectiveLoc) << ")";
+         << SourceMgr.getFilename(D.DirectiveLoc) << ':'
+         << SourceMgr.getPresumedLineNumber(D.DirectiveLoc) << ')';
     OS << ": " << D.Text;
   }
 
@@ -585,6 +612,22 @@
   return DL.size();
 }
 
+/// \brief Determine whether two source locations come from the same file.
+static bool IsFromSameFile(SourceManager &SM, SourceLocation DirectiveLoc,
+                           SourceLocation DiagnosticLoc) {
+  while (DiagnosticLoc.isMacroID())
+    DiagnosticLoc = SM.getImmediateMacroCallerLoc(DiagnosticLoc);
+
+  if (SM.isFromSameFile(DirectiveLoc, DiagnosticLoc))
+    return true;
+
+  const FileEntry *DiagFile = SM.getFileEntryForID(SM.getFileID(DiagnosticLoc));
+  if (!DiagFile && SM.isFromMainFile(DirectiveLoc))
+    return true;
+
+  return (DiagFile == SM.getFileEntryForID(SM.getFileID(DirectiveLoc)));
+}
+
 /// CheckLists - Compare expected to seen diagnostic lists and return the
 /// the difference between them.
 ///
@@ -607,6 +650,9 @@
         if (LineNo1 != LineNo2)
           continue;
 
+        if (!IsFromSameFile(SourceMgr, D.DiagnosticLoc, II->first))
+          continue;
+
         const std::string &RightText = II->second;
         if (D.match(RightText))
           break;