[clang-cl, PCH] Implement support for MS-style PCH through headers

Implement support for MS-style PCH through headers.

This enables support for /Yc and /Yu where the through header is either
on the command line or included in the source. It replaces the current
support the requires the header also be specified with /FI.

This change adds a -cc1 option -pch-through-header that is used to either
start or stop compilation during PCH create or use.

When creating a PCH, the compilation ends after compilation of the through
header.

When using a PCH, tokens are skipped until after the through header is seen.

Patch By: mikerice
Differential Revision: https://reviews.llvm.org/D46652

llvm-svn: 336379
diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp
index 949dbf1..4ea0f48 100644
--- a/clang/lib/Lex/PPDirectives.cpp
+++ b/clang/lib/Lex/PPDirectives.cpp
@@ -887,6 +887,22 @@
   bool save;
 };
 
+/// Process a directive while looking for the through header.
+/// Only #include (to check if it is the through header) and #define (to warn
+/// about macros that don't match the PCH) are handled. All other directives
+/// are completely discarded.
+void Preprocessor::HandleSkippedThroughHeaderDirective(Token &Result,
+                                                       SourceLocation HashLoc) {
+  if (const IdentifierInfo *II = Result.getIdentifierInfo()) {
+    if (II->getPPKeywordID() == tok::pp_include)
+      return HandleIncludeDirective(HashLoc, Result);
+    if (II->getPPKeywordID() == tok::pp_define)
+      return HandleDefineDirective(Result,
+                                   /*ImmediatelyAfterHeaderGuard=*/false);
+  }
+  DiscardUntilEndOfDirective();
+}
+
 /// HandleDirective - This callback is invoked when the lexer sees a # token
 /// at the start of a line.  This consumes the directive, modifies the
 /// lexer/preprocessor state, and advances the lexer(s) so that the next token
@@ -948,6 +964,9 @@
   // and reset to previous state when returning from this function.
   ResetMacroExpansionHelper helper(this);
 
+  if (SkippingUntilPCHThroughHeader)
+    return HandleSkippedThroughHeaderDirective(Result, SavedHash.getLocation());
+
   switch (Result.getKind()) {
   case tok::eod:
     return;   // null directive.
@@ -1862,6 +1881,12 @@
     }
   }
 
+  if (usingPCHWithThroughHeader() && SkippingUntilPCHThroughHeader) {
+    if (isPCHThroughHeader(File))
+      SkippingUntilPCHThroughHeader = false;
+    return;
+  }
+
   // Should we enter the source file? Set to false if either the source file is
   // known to have no effect beyond its effect on module visibility -- that is,
   // if it's got an include guard that is already defined or is a modular header
@@ -2587,7 +2612,15 @@
     }
   }
 
-  
+  // When skipping just warn about macros that do not match.
+  if (SkippingUntilPCHThroughHeader) {
+    const MacroInfo *OtherMI = getMacroInfo(MacroNameTok.getIdentifierInfo());
+    if (!OtherMI || !MI->isIdenticalTo(*OtherMI, *this,
+                             /*Syntactic=*/LangOpts.MicrosoftExt))
+      Diag(MI->getDefinitionLoc(), diag::warn_pp_macro_def_mismatch_with_pch)
+          << MacroNameTok.getIdentifierInfo();
+    return;
+  }
 
   // Finally, if this identifier already had a macro defined for it, verify that
   // the macro bodies are identical, and issue diagnostics if they are not.
diff --git a/clang/lib/Lex/PPLexerChange.cpp b/clang/lib/Lex/PPLexerChange.cpp
index c7e0d2c..352814d 100644
--- a/clang/lib/Lex/PPLexerChange.cpp
+++ b/clang/lib/Lex/PPLexerChange.cpp
@@ -13,6 +13,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/Lex/Preprocessor.h"
+#include "clang/Lex/PreprocessorOptions.h"
 #include "clang/Basic/FileManager.h"
 #include "clang/Basic/SourceManager.h"
 #include "clang/Lex/HeaderSearch.h"
@@ -425,6 +426,8 @@
     PragmaAssumeNonNullLoc = SourceLocation();
   }
 
+  bool LeavingPCHThroughHeader = false;
+
   // If this is a #include'd file, pop it off the include stack and continue
   // lexing the #includer file.
   if (!IncludeMacroStack.empty()) {
@@ -481,6 +484,12 @@
       Result.setAnnotationValue(M);
     }
 
+    bool FoundPCHThroughHeader = false;
+    if (CurPPLexer && creatingPCHWithThroughHeader() &&
+        isPCHThroughHeader(
+            SourceMgr.getFileEntryForID(CurPPLexer->getFileID())))
+      FoundPCHThroughHeader = true;
+
     // We're done with the #included file.
     RemoveTopOfLexerStack();
 
@@ -500,8 +509,16 @@
     if (ExitedFromPredefinesFile)
       replayPreambleConditionalStack();
 
-    // Client should lex another token unless we generated an EOM.
-    return LeavingSubmodule;
+    if (!isEndOfMacro && CurPPLexer && FoundPCHThroughHeader &&
+        (isInPrimaryFile() ||
+         CurPPLexer->getFileID() == getPredefinesFileID())) {
+      // Leaving the through header. Continue directly to end of main file
+      // processing.
+      LeavingPCHThroughHeader = true;
+    } else {
+      // Client should lex another token unless we generated an EOM.
+      return LeavingSubmodule;
+    }
   }
 
   // If this is the end of the main file, form an EOF token.
@@ -522,6 +539,12 @@
         Result.setLocation(Result.getLocation().getLocWithOffset(-1));
     }
 
+    if (creatingPCHWithThroughHeader() && !LeavingPCHThroughHeader) {
+      // Reached the end of the compilation without finding the through header.
+      Diag(CurLexer->getFileLoc(), diag::err_pp_through_header_not_seen)
+          << PPOpts->PCHThroughHeader << 0;
+    }
+
     if (!isIncrementalProcessingEnabled())
       // We're done with lexing.
       CurLexer.reset();
diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp
index 358103d..0217a2e 100644
--- a/clang/lib/Lex/Preprocessor.cpp
+++ b/clang/lib/Lex/Preprocessor.cpp
@@ -149,6 +149,11 @@
     Ident_AbnormalTermination = nullptr;
   }
 
+  // If using a PCH with a through header, start skipping tokens.
+  if (!this->PPOpts->PCHThroughHeader.empty() &&
+      !this->PPOpts->ImplicitPCHInclude.empty())
+    SkippingUntilPCHThroughHeader = true;
+
   if (this->PPOpts->GeneratePreamble)
     PreambleConditionalStack.startRecording();
 }
@@ -551,6 +556,72 @@
 
   // Start parsing the predefines.
   EnterSourceFile(FID, nullptr, SourceLocation());
+
+  if (!PPOpts->PCHThroughHeader.empty()) {
+    // Lookup and save the FileID for the through header. If it isn't found
+    // in the search path, it's a fatal error.
+    const DirectoryLookup *CurDir;
+    const FileEntry *File = LookupFile(
+        SourceLocation(), PPOpts->PCHThroughHeader,
+        /*isAngled=*/false, /*FromDir=*/nullptr, /*FromFile=*/nullptr, CurDir,
+        /*SearchPath=*/nullptr, /*RelativePath=*/nullptr,
+        /*SuggestedModule=*/nullptr, /*IsMapped=*/nullptr);
+    if (!File) {
+      Diag(SourceLocation(), diag::err_pp_through_header_not_found)
+          << PPOpts->PCHThroughHeader;
+      return;
+    }
+    setPCHThroughHeaderFileID(
+        SourceMgr.createFileID(File, SourceLocation(), SrcMgr::C_User));
+  }
+
+  // Skip tokens from the Predefines and if needed the main file.
+  if (usingPCHWithThroughHeader() && SkippingUntilPCHThroughHeader)
+    SkipTokensUntilPCHThroughHeader();
+}
+
+void Preprocessor::setPCHThroughHeaderFileID(FileID FID) {
+  assert(PCHThroughHeaderFileID.isInvalid() &&
+         "PCHThroughHeaderFileID already set!");
+  PCHThroughHeaderFileID = FID;
+}
+
+bool Preprocessor::isPCHThroughHeader(const FileEntry *FE) {
+  assert(PCHThroughHeaderFileID.isValid() &&
+         "Invalid PCH through header FileID");
+  return FE == SourceMgr.getFileEntryForID(PCHThroughHeaderFileID);
+}
+
+bool Preprocessor::creatingPCHWithThroughHeader() {
+  return TUKind == TU_Prefix && !PPOpts->PCHThroughHeader.empty() &&
+         PCHThroughHeaderFileID.isValid();
+}
+
+bool Preprocessor::usingPCHWithThroughHeader() {
+  return TUKind != TU_Prefix && !PPOpts->PCHThroughHeader.empty() &&
+         PCHThroughHeaderFileID.isValid();
+}
+
+/// Skip tokens until after the #include of the through header.
+/// Tokens in the predefines file and the main file may be skipped. If the end
+/// of the predefines file is reached, skipping continues into the main file.
+/// If the end of the main file is reached, it's a fatal error.
+void Preprocessor::SkipTokensUntilPCHThroughHeader() {
+  bool ReachedMainFileEOF = false;
+  Token Tok;
+  while (true) {
+    bool InPredefines = (CurLexer->getFileID() == getPredefinesFileID());
+    CurLexer->Lex(Tok);
+    if (Tok.is(tok::eof) && !InPredefines) {
+      ReachedMainFileEOF = true;
+      break;
+    }
+    if (!SkippingUntilPCHThroughHeader)
+      break;
+  }
+  if (ReachedMainFileEOF)
+    Diag(SourceLocation(), diag::err_pp_through_header_not_seen)
+        << PPOpts->PCHThroughHeader << 1;
 }
 
 void Preprocessor::replayPreambleConditionalStack() {