mikerice updated this revision to Diff 163946.
mikerice marked 7 inline comments as done.
mikerice added a comment.

Thanks for the review.  Updated based on comments from Hans.


https://reviews.llvm.org/D51391

Files:
  include/clang/Basic/DiagnosticDriverKinds.td
  include/clang/Basic/DiagnosticLexKinds.td
  include/clang/Driver/CC1Options.td
  include/clang/Lex/Preprocessor.h
  include/clang/Lex/PreprocessorOptions.h
  lib/Driver/Driver.cpp
  lib/Driver/ToolChains/Clang.cpp
  lib/Frontend/CompilerInvocation.cpp
  lib/Lex/PPDirectives.cpp
  lib/Lex/Pragma.cpp
  lib/Lex/Preprocessor.cpp
  lib/Parse/ParseAST.cpp
  test/Driver/cl-pch.cpp
  test/PCH/Inputs/pch-hdrstop-use.cpp
  test/PCH/Inputs/pch-no-hdrstop-use.cpp
  test/PCH/pch-hdrstop-err.cpp
  test/PCH/pch-hdrstop-warn.cpp
  test/PCH/pch-hdrstop.cpp
  test/PCH/pch-no-hdrstop.cpp

Index: lib/Parse/ParseAST.cpp
===================================================================
--- lib/Parse/ParseAST.cpp
+++ lib/Parse/ParseAST.cpp
@@ -141,26 +141,26 @@
     CleanupParser(ParseOP.get());
 
   S.getPreprocessor().EnterMainSourceFile();
-  if (!S.getPreprocessor().getCurrentLexer()) {
-    // If a PCH through header is specified that does not have an include in
-    // the source, there won't be any tokens or a Lexer.
-    return;
-  }
-
-  P.Initialize();
-
-  Parser::DeclGroupPtrTy ADecl;
   ExternalASTSource *External = S.getASTContext().getExternalSource();
   if (External)
     External->StartTranslationUnit(Consumer);
 
-  for (bool AtEOF = P.ParseFirstTopLevelDecl(ADecl); !AtEOF;
-       AtEOF = P.ParseTopLevelDecl(ADecl)) {
-    // If we got a null return and something *was* parsed, ignore it.  This
-    // is due to a top-level semicolon, an action override, or a parse error
-    // skipping something.
-    if (ADecl && !Consumer->HandleTopLevelDecl(ADecl.get()))
-      return;
+  // If a PCH through header is specified that does not have an include in
+  // the source, or a PCH is being created with #pragma hdrstop with nothing
+  // after the pragma, there won't be any tokens or a Lexer.
+  bool HaveLexer = S.getPreprocessor().getCurrentLexer();
+
+  if (HaveLexer) {
+    P.Initialize();
+    Parser::DeclGroupPtrTy ADecl;
+    for (bool AtEOF = P.ParseFirstTopLevelDecl(ADecl); !AtEOF;
+         AtEOF = P.ParseTopLevelDecl(ADecl)) {
+      // If we got a null return and something *was* parsed, ignore it.  This
+      // is due to a top-level semicolon, an action override, or a parse error
+      // skipping something.
+      if (ADecl && !Consumer->HandleTopLevelDecl(ADecl.get()))
+        return;
+    }
   }
 
   // Process any TopLevelDecls generated by #pragma weak.
@@ -179,7 +179,7 @@
   std::swap(OldCollectStats, S.CollectStats);
   if (PrintStats) {
     llvm::errs() << "\nSTATISTICS:\n";
-    P.getActions().PrintStats();
+    if (HaveLexer) P.getActions().PrintStats();
     S.getASTContext().PrintStats();
     Decl::PrintStats();
     Stmt::PrintStats();
Index: lib/Driver/Driver.cpp
===================================================================
--- lib/Driver/Driver.cpp
+++ lib/Driver/Driver.cpp
@@ -2982,22 +2982,9 @@
     }
   }
 
-  // Diagnose unsupported forms of /Yc /Yu. Ignore /Yc/Yu for now if:
-  // * no filename after it
-  // * both /Yc and /Yu passed but with different filenames
-  // * corresponding file not also passed as /FI
+  // Ignore /Yc/Yu if both /Yc and /Yu passed but with different filenames.
   Arg *YcArg = Args.getLastArg(options::OPT__SLASH_Yc);
   Arg *YuArg = Args.getLastArg(options::OPT__SLASH_Yu);
-  if (YcArg && YcArg->getValue()[0] == '\0') {
-    Diag(clang::diag::warn_drv_ycyu_no_arg_clang_cl) << YcArg->getSpelling();
-    Args.eraseArg(options::OPT__SLASH_Yc);
-    YcArg = nullptr;
-  }
-  if (YuArg && YuArg->getValue()[0] == '\0') {
-    Diag(clang::diag::warn_drv_ycyu_no_arg_clang_cl) << YuArg->getSpelling();
-    Args.eraseArg(options::OPT__SLASH_Yu);
-    YuArg = nullptr;
-  }
   if (YcArg && YuArg && strcmp(YcArg->getValue(), YuArg->getValue()) != 0) {
     Diag(clang::diag::warn_drv_ycyu_different_arg_clang_cl);
     Args.eraseArg(options::OPT__SLASH_Yc);
@@ -4279,11 +4266,11 @@
     // extension of .pch is assumed. "
     if (!llvm::sys::path::has_extension(Output))
       Output += ".pch";
-  } else if (Arg *YcArg = C.getArgs().getLastArg(options::OPT__SLASH_Yc)) {
-    Output = YcArg->getValue();
-    llvm::sys::path::replace_extension(Output, ".pch");
   } else {
-    Output = BaseName;
+    if (Arg *YcArg = C.getArgs().getLastArg(options::OPT__SLASH_Yc))
+      Output = YcArg->getValue();
+    if (Output.empty())
+      Output = BaseName;
     llvm::sys::path::replace_extension(Output, ".pch");
   }
   return Output.str();
Index: lib/Driver/ToolChains/Clang.cpp
===================================================================
--- lib/Driver/ToolChains/Clang.cpp
+++ lib/Driver/ToolChains/Clang.cpp
@@ -1105,10 +1105,19 @@
       StringRef ThroughHeader = YcArg ? YcArg->getValue() : YuArg->getValue();
       if (!isa<PrecompileJobAction>(JA)) {
         CmdArgs.push_back("-include-pch");
-        CmdArgs.push_back(Args.MakeArgString(D.GetClPchPath(C, ThroughHeader)));
+        CmdArgs.push_back(Args.MakeArgString(D.GetClPchPath(
+            C, !ThroughHeader.empty()
+                   ? ThroughHeader
+                   : llvm::sys::path::filename(Inputs[0].getBaseInput()))));
+      }
+
+      if (ThroughHeader.empty()) {
+        CmdArgs.push_back(Args.MakeArgString(
+            Twine("-pch-through-hdrstop=") + (YcArg ? "create" : "use")));
+      } else {
+        CmdArgs.push_back(
+            Args.MakeArgString(Twine("-pch-through-header=") + ThroughHeader));
       }
-      CmdArgs.push_back(
-          Args.MakeArgString(Twine("-pch-through-header=") + ThroughHeader));
     }
   }
 
Index: lib/Lex/Preprocessor.cpp
===================================================================
--- lib/Lex/Preprocessor.cpp
+++ lib/Lex/Preprocessor.cpp
@@ -149,6 +149,10 @@
     Ident_AbnormalTermination = nullptr;
   }
 
+  // If using a PCH where a #pragma hdrstop is expected, start skipping tokens.
+  if (usingPCHWithPragmaHdrStop())
+    SkippingUntilPragmaHdrStop = true;
+
   // If using a PCH with a through header, start skipping tokens.
   if (!this->PPOpts->PCHThroughHeader.empty() &&
       !this->PPOpts->ImplicitPCHInclude.empty())
@@ -576,8 +580,9 @@
   }
 
   // Skip tokens from the Predefines and if needed the main file.
-  if (usingPCHWithThroughHeader() && SkippingUntilPCHThroughHeader)
-    SkipTokensUntilPCHThroughHeader();
+  if ((usingPCHWithThroughHeader() && SkippingUntilPCHThroughHeader) ||
+      (usingPCHWithPragmaHdrStop() && SkippingUntilPragmaHdrStop))
+    SkipTokensWhileUsingPCH();
 }
 
 void Preprocessor::setPCHThroughHeaderFileID(FileID FID) {
@@ -602,26 +607,43 @@
          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 Preprocessor::creatingPCHWithPragmaHdrStop() {
+  return TUKind == TU_Prefix && PPOpts->PCHWithHdrStop;
+}
+
+bool Preprocessor::usingPCHWithPragmaHdrStop() {
+  return TUKind != TU_Prefix && PPOpts->PCHWithHdrStop;
+}
+
+/// Skip tokens until after the #include of the through header or
+/// until after a #pragma hdrstop is seen. 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::SkipTokensWhileUsingPCH() {
   bool ReachedMainFileEOF = false;
+  bool UsingPCHThroughHeader = SkippingUntilPCHThroughHeader;
+  bool UsingPragmaHdrStop = SkippingUntilPragmaHdrStop;
   Token Tok;
   while (true) {
     bool InPredefines = (CurLexer->getFileID() == getPredefinesFileID());
     CurLexer->Lex(Tok);
     if (Tok.is(tok::eof) && !InPredefines) {
       ReachedMainFileEOF = true;
       break;
     }
-    if (!SkippingUntilPCHThroughHeader)
+    if (UsingPCHThroughHeader && !SkippingUntilPCHThroughHeader)
+      break;
+    if (UsingPragmaHdrStop && !SkippingUntilPragmaHdrStop)
       break;
   }
-  if (ReachedMainFileEOF)
-    Diag(SourceLocation(), diag::err_pp_through_header_not_seen)
-        << PPOpts->PCHThroughHeader << 1;
+  if (ReachedMainFileEOF) {
+    if (UsingPCHThroughHeader)
+      Diag(SourceLocation(), diag::err_pp_through_header_not_seen)
+          << PPOpts->PCHThroughHeader << 1;
+    else if (!PPOpts->PCHWithHdrStopCreate)
+      Diag(SourceLocation(), diag::err_pp_pragma_hdrstop_not_seen);
+  }
 }
 
 void Preprocessor::replayPreambleConditionalStack() {
Index: lib/Lex/Pragma.cpp
===================================================================
--- lib/Lex/Pragma.cpp
+++ lib/Lex/Pragma.cpp
@@ -876,6 +876,37 @@
                                        StringRef(Start, End - Start));
 }
 
+void Preprocessor::HandlePragmaHdrstop(Token &Tok) {
+  Lex(Tok);
+  if (Tok.is(tok::l_paren)) {
+    Diag(Tok.getLocation(), diag::warn_pp_hdrstop_filename_ignored);
+
+    std::string FileName;
+    if (!LexStringLiteral(Tok, FileName, "pragma hdrstop", false))
+      return;
+
+    if (Tok.isNot(tok::r_paren)) {
+      Diag(Tok, diag::err_expected) << tok::r_paren;
+      return;
+    }
+    Lex(Tok);
+  }
+  if (Tok.isNot(tok::eod))
+    Diag(Tok.getLocation(), diag::ext_pp_extra_tokens_at_eol)
+        << "pragma hdrstop";
+
+  if (creatingPCHWithPragmaHdrStop() &&
+      SourceMgr.isInMainFile(Tok.getLocation())) {
+    assert(CurLexer && "no lexer for #pragma hdrstop processing");
+    Token &Result = Tok;
+    Result.startToken();
+    CurLexer->FormTokenWithChars(Result, CurLexer->BufferEnd, tok::eof);
+    CurLexer->cutOffLexing();
+  }
+  if (usingPCHWithPragmaHdrStop())
+    SkippingUntilPragmaHdrStop = false;
+}
+
 /// AddPragmaHandler - Add the specified pragma handler to the preprocessor.
 /// If 'Namespace' is non-null, then it is a token required to exist on the
 /// pragma line before the pragma string starts, e.g. "STDC" or "GCC".
@@ -1220,6 +1251,15 @@
   }
 };
 
+/// "\#pragma hdrstop [<header-name-string>]"
+struct PragmaHdrstopHandler : public PragmaHandler {
+  PragmaHdrstopHandler() : PragmaHandler("hdrstop") {}
+  void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer,
+                    Token &DepToken) override {
+    PP.HandlePragmaHdrstop(DepToken);
+  }
+};
+
 /// "\#pragma warning(...)".  MSVC's diagnostics do not map cleanly to clang's
 /// diagnostics, so we don't really implement this pragma.  We parse it and
 /// ignore it to avoid -Wunknown-pragma warnings.
@@ -1799,6 +1839,7 @@
   if (LangOpts.MicrosoftExt) {
     AddPragmaHandler(new PragmaWarningHandler());
     AddPragmaHandler(new PragmaIncludeAliasHandler());
+    AddPragmaHandler(new PragmaHdrstopHandler());
   }
 
   // Pragmas added by plugins
Index: lib/Lex/PPDirectives.cpp
===================================================================
--- lib/Lex/PPDirectives.cpp
+++ lib/Lex/PPDirectives.cpp
@@ -887,18 +887,29 @@
   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,
+/// Process a directive while looking for the through header or a #pragma
+/// hdrstop. The following directives are handled:
+/// #include (to check if it is the through header)
+/// #define (to warn about macros that don't match the PCH)
+/// #pragma (to check for pragma hdrstop).
+/// All other directives are completely discarded.
+void Preprocessor::HandleSkippedDirectiveWhileUsingPCH(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)
+    if (II->getPPKeywordID() == tok::pp_define) {
       return HandleDefineDirective(Result,
                                    /*ImmediatelyAfterHeaderGuard=*/false);
+    }
+    if (SkippingUntilPCHThroughHeader &&
+        II->getPPKeywordID() == tok::pp_include) {
+      return HandleIncludeDirective(HashLoc, Result);
+    }
+    if (SkippingUntilPragmaHdrStop && II->getPPKeywordID() == tok::pp_pragma) {
+      Token P = LookAhead(0);
+      auto *II = P.getIdentifierInfo();
+      if (II && II->getName() == "hdrstop")
+        return HandlePragmaDirective(HashLoc, PIK_HashPragma);
+    }
   }
   DiscardUntilEndOfDirective();
 }
@@ -964,8 +975,8 @@
   // and reset to previous state when returning from this function.
   ResetMacroExpansionHelper helper(this);
 
-  if (SkippingUntilPCHThroughHeader)
-    return HandleSkippedThroughHeaderDirective(Result, SavedHash.getLocation());
+  if (SkippingUntilPCHThroughHeader || SkippingUntilPragmaHdrStop)
+    return HandleSkippedDirectiveWhileUsingPCH(Result, SavedHash.getLocation());
 
   switch (Result.getKind()) {
   case tok::eod:
Index: lib/Frontend/CompilerInvocation.cpp
===================================================================
--- lib/Frontend/CompilerInvocation.cpp
+++ lib/Frontend/CompilerInvocation.cpp
@@ -2864,6 +2864,14 @@
                                   frontend::ActionKind Action) {
   Opts.ImplicitPCHInclude = Args.getLastArgValue(OPT_include_pch);
   Opts.ImplicitPTHInclude = Args.getLastArgValue(OPT_include_pth);
+  if (Arg *A = Args.getLastArg(OPT_pch_through_hdrstop_EQ)) {
+    Opts.PCHWithHdrStop = true;
+    StringRef Val = A->getValue();
+    if (Val == "create")
+      Opts.PCHWithHdrStopCreate = true;
+    else if (Val != "use")
+      Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Val;
+  }
   Opts.PCHThroughHeader = Args.getLastArgValue(OPT_pch_through_header_EQ);
   if (const Arg *A = Args.getLastArg(OPT_token_cache))
       Opts.TokenCache = A->getValue();
Index: include/clang/Driver/CC1Options.td
===================================================================
--- include/clang/Driver/CC1Options.td
+++ include/clang/Driver/CC1Options.td
@@ -606,6 +606,10 @@
 def pch_through_header_EQ : Joined<["-"], "pch-through-header=">,
   HelpText<"Stop PCH generation after including this file.  When using a PCH, "
            "skip tokens until after this file is included.">;
+def pch_through_hdrstop_EQ : Joined<["-"], "pch-through-hdrstop=">,
+  HelpText<"Stop PCH generation after #pragma hdrstop.  When using a PCH, "
+           "skip tokens until after a #pragma hdrstop.  Value is 'create' "
+           "or 'use'.">;
 def fno_pch_timestamp : Flag<["-"], "fno-pch-timestamp">,
   HelpText<"Disable inclusion of timestamp in precompiled headers">;
 def building_pch_with_obj : Flag<["-"], "building-pch-with-obj">,
Index: include/clang/Lex/Preprocessor.h
===================================================================
--- include/clang/Lex/Preprocessor.h
+++ include/clang/Lex/Preprocessor.h
@@ -726,6 +726,9 @@
   /// The file ID for the PCH through header.
   FileID PCHThroughHeaderFileID;
 
+  /// Whether tokens are being skipped until a #pragma hdrstop is seen.
+  bool SkippingUntilPragmaHdrStop = false;
+
   /// Whether tokens are being skipped until the through header is seen.
   bool SkippingUntilPCHThroughHeader = false;
 
@@ -1168,11 +1171,19 @@
   /// True if using a PCH with a through header.
   bool usingPCHWithThroughHeader();
 
-  /// Skip tokens until after the #include of the through header.
-  void SkipTokensUntilPCHThroughHeader();
+  /// True if creating a PCH with a #pragma hdrstop.
+  bool creatingPCHWithPragmaHdrStop();
+
+  /// True if using a PCH with a #pragma hdrstop.
+  bool usingPCHWithPragmaHdrStop();
 
-  /// Process directives while skipping until the through header is found.
-  void HandleSkippedThroughHeaderDirective(Token &Result,
+  /// Skip tokens until after the #include of the through header or
+  /// until after a #pragma hdrstop.
+  void SkipTokensWhileUsingPCH();
+
+  /// Process directives while skipping until the through header or
+  /// #pragma hdrstop is found.
+  void HandleSkippedDirectiveWhileUsingPCH(Token &Result,
                                            SourceLocation HashLoc);
 
   /// Enter the specified FileID as the main source file,
@@ -2203,6 +2214,7 @@
   void HandlePragmaPopMacro(Token &Tok);
   void HandlePragmaIncludeAlias(Token &Tok);
   void HandlePragmaModuleBuild(Token &Tok);
+  void HandlePragmaHdrstop(Token &Tok);
   IdentifierInfo *ParsePragmaPushOrPopMacro(Token &Tok);
 
   // Return true and store the first token only if any CommentHandler
Index: include/clang/Lex/PreprocessorOptions.h
===================================================================
--- include/clang/Lex/PreprocessorOptions.h
+++ include/clang/Lex/PreprocessorOptions.h
@@ -54,6 +54,16 @@
   /// definitions and expansions.
   bool DetailedRecord = false;
 
+  /// When true, we are creating or using a PCH where a #pragma hdrstop is
+  /// expected to indicate the beginning or end of the PCH.
+  bool PCHWithHdrStop = false;
+
+  /// When true, we are creating a PCH or creating the PCH object while
+  /// expecting a #pragma hdrstop to separate the two.  Allow for a
+  /// missing #pragma hdrstop, which generates a PCH for the whole file,
+  /// and creates an empty PCH object.
+  bool PCHWithHdrStopCreate = false;
+
   /// If non-empty, the filename used in an #include directive in the primary
   /// source file (or command-line preinclude) that is used to implement
   /// MSVC-style precompiled headers. When creating a PCH, after the #include
Index: include/clang/Basic/DiagnosticDriverKinds.td
===================================================================
--- include/clang/Basic/DiagnosticDriverKinds.td
+++ include/clang/Basic/DiagnosticDriverKinds.td
@@ -151,9 +151,6 @@
   "unknown argument ignored in clang-cl '%0' (did you mean '%1'?)">,
   InGroup<UnknownArgument>;
 
-def warn_drv_ycyu_no_arg_clang_cl : Warning<
-  "support for '%0' without a filename not implemented yet; flag ignored">,
-  InGroup<ClangClPch>;
 def warn_drv_ycyu_different_arg_clang_cl : Warning<
   "support for '/Yc' and '/Yu' with different filenames not implemented yet; flags ignored">,
   InGroup<ClangClPch>;
Index: include/clang/Basic/DiagnosticLexKinds.td
===================================================================
--- include/clang/Basic/DiagnosticLexKinds.td
+++ include/clang/Basic/DiagnosticLexKinds.td
@@ -409,9 +409,16 @@
 def err_pp_through_header_not_seen : Error<
   "#include of '%0' not seen while attempting to "
   "%select{create|use}1 precompiled header">, DefaultFatal;
+def err_pp_pragma_hdrstop_not_seen : Error<
+  "#pragma hdrstop not seen while attempting to use precompiled header">,
+  DefaultFatal;
 def warn_pp_macro_def_mismatch_with_pch : Warning<
   "definition of macro %0 does not match definition in precompiled header">,
   InGroup<ClangClPch>;
+def warn_pp_hdrstop_filename_ignored : Warning<
+  "#pragma hdrstop filename not supported, "
+  "/Fp can be used to specify precompiled header filename">,
+  InGroup<ClangClPch>;
 def err_pp_file_not_found_not_fatal : Error<
   "'%0' file not found with <angled> include; use \"quotes\" instead">;
 def err_pp_error_opening_file : Error<
Index: test/PCH/pch-hdrstop-warn.cpp
===================================================================
--- test/PCH/pch-hdrstop-warn.cpp
+++ test/PCH/pch-hdrstop-warn.cpp
@@ -0,0 +1,10 @@
+// Create PCH with #pragma hdrstop
+// RUN: %clang_cc1 -verify -I %S -emit-pch -pch-through-hdrstop=create \
+// RUN:   -fms-extensions -o %t.pch -x c++-header %s
+
+// Create PCH object with #pragma hdrstop
+// RUN: %clang_cc1 -verify -I %S -emit-obj -include-pch %t.pch \
+// RUN:   -pch-through-hdrstop=create -fms-extensions -o %t.obj -x c++ %s
+
+//expected-warning@+1{{hdrstop filename not supported}}
+#pragma hdrstop("name.pch")
Index: test/PCH/pch-no-hdrstop.cpp
===================================================================
--- test/PCH/pch-no-hdrstop.cpp
+++ test/PCH/pch-no-hdrstop.cpp
@@ -0,0 +1,18 @@
+// expected-no-diagnostics
+// Create PCH with #pragma hdrstop processing with no #pragma hdrstop
+// RUN: %clang_cc1 -verify -I %S -emit-pch -pch-through-hdrstop=create \
+// RUN:   -fms-extensions -o %t.pch -x c++-header %s
+
+// Create the PCH object
+// RUN: %clang_cc1 -verify -I %S -emit-obj -include-pch %t.pch \
+// RUN:   -pch-through-hdrstop=create -fms-extensions -o %t.obj -x c++ %s
+
+// The use must still have a #pragma hdrstop
+// RUN: %clang_cc1 -verify -I %S -emit-obj -include-pch %t.pch \
+// RUN:   -pch-through-hdrstop=use -fms-extensions -o %t.obj \
+// RUN:   -x c++ %S/Inputs/pch-no-hdrstop-use.cpp
+
+#include "Inputs/pch-through1.h"
+static int bar() { return 42; }
+#include "Inputs/pch-through2.h"
+int pch();
Index: test/PCH/pch-hdrstop-err.cpp
===================================================================
--- test/PCH/pch-hdrstop-err.cpp
+++ test/PCH/pch-hdrstop-err.cpp
@@ -0,0 +1,14 @@
+// Create PCH with #pragma hdrstop
+// RUN: %clang_cc1 -I %S -emit-pch -pch-through-hdrstop=create \
+// RUN:   -fms-extensions -o %t.pch -x c++-header %s
+
+// Use PCH with no #pragma hdrstop
+// RUN: not %clang_cc1 -I %S -emit-obj -include-pch %t.pch \
+// RUN:   -pch-through-hdrstop=use -fms-extensions -o %t.obj -x c++ %s 2>&1 \
+// RUN:   | FileCheck --check-prefix=CHECK-U %s
+
+#include "Inputs/pch-through1.h"
+static int bar() { return 42; }
+#include "Inputs/pch-through2.h"
+int pch();
+//CHECK-U: hdrstop not seen while attempting to use precompiled header
Index: test/PCH/Inputs/pch-hdrstop-use.cpp
===================================================================
--- test/PCH/Inputs/pch-hdrstop-use.cpp
+++ test/PCH/Inputs/pch-hdrstop-use.cpp
@@ -0,0 +1,13 @@
+#include "Inputs/pch-through1.h"
+static int bar() { return 42; }
+#include "Inputs/pch-through2.h"
+int pch();
+#pragma hdrstop
+
+//expected-no-diagnostics
+//CHECK-NOT: FunctionDecl{{.*}}other
+//CHECK: FunctionDecl{{.*}}main
+int main()
+{
+  return pch() - 42*42 + bar() - 42 + through1(0) + through2(33);
+}
Index: test/PCH/Inputs/pch-no-hdrstop-use.cpp
===================================================================
--- test/PCH/Inputs/pch-no-hdrstop-use.cpp
+++ test/PCH/Inputs/pch-no-hdrstop-use.cpp
@@ -0,0 +1,11 @@
+#include "Inputs/pch-through1.h"
+static int bar() { return 42; }
+#include "Inputs/pch-through2.h"
+int pch();
+#pragma hdrstop
+
+//expected-no-diagnostics
+int main()
+{
+  return pch() + through1(0) + through2(-1) + bar() - 42;
+}
Index: test/PCH/pch-hdrstop.cpp
===================================================================
--- test/PCH/pch-hdrstop.cpp
+++ test/PCH/pch-hdrstop.cpp
@@ -0,0 +1,28 @@
+// expected-no-diagnostics
+// Create PCH with #pragma hdrstop
+// RUN: %clang_cc1 -verify -I %S -emit-pch -pch-through-hdrstop=create \
+// RUN:   -fms-extensions -o %t.pch -x c++-header %s
+
+// Create PCH object with #pragma hdrstop
+// RUN: %clang_cc1 -verify -I %S -emit-obj -include-pch %t.pch \
+// RUN:   -pch-through-hdrstop=create -fms-extensions -o %t.obj -x c++ %s
+
+// Use PCH with #pragma hdrstop
+// RUN: %clang_cc1 -verify -I %S -emit-obj -include-pch %t.pch \
+// RUN:   -pch-through-hdrstop=use -fms-extensions -o %t.obj \
+// RUN:   -x c++ %S/Inputs/pch-hdrstop-use.cpp
+
+// Ensure the PCH stops at the hdrstop
+// RUN: %clang_cc1 -ast-dump -I %S -include-pch %t.pch \
+// RUN:   -pch-through-hdrstop=use -fms-extensions -o %t.obj \
+// RUN:   -x c++ %S/Inputs/pch-hdrstop-use.cpp 2>&1 \
+// RUN:   | FileCheck %S/Inputs/pch-hdrstop-use.cpp
+
+#include "Inputs/pch-through1.h"
+static int bar() { return 42; }
+#include "Inputs/pch-through2.h"
+int pch();
+#pragma hdrstop
+
+int pch() { return 42*42; }
+int other() { return 42; }
Index: test/Driver/cl-pch.cpp
===================================================================
--- test/Driver/cl-pch.cpp
+++ test/Driver/cl-pch.cpp
@@ -264,6 +264,71 @@
 // CHECK-YU-SLASH: -include
 // CHECK-YU-SLASH: ".{{[/\\]+}}pchfile.h"
 
+// /Yc without an argument creates a PCH from the code before #pragma hdrstop.
+// /Yu without an argument uses a PCH and starts compiling after the
+// #pragma hdrstop.
+// RUN: %clang_cl -Werror /Yc /Fpycnoarg.pch /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YC-NOARG %s
+// 1. Create .pch file
+// CHECK-YC-NOARG: cc1
+// CHECK-YC-NOARG: -emit-pch
+// CHECK-YC-NOARG: -pch-through-hdrstop=create
+// CHECK-YC-NOARG: -o
+// CHECK-YC-NOARG: ycnoarg.pch
+// CHECK-YC-NOARG: -x
+// CHECK-YC-NOARG: "c++-header"
+// CHECK-YC-NOARG: cl-pch.cpp
+// 2. Use .pch file: Includes ycnoarg.pch
+// CHECK-YC-NOARG: cc1
+// CHECK-YC-NOARG: -emit-obj
+// CHECK-YC-NOARG: -include-pch
+// CHECK-YC-NOARG: ycnoarg.pch
+// CHECK-YC-NOARG: -pch-through-hdrstop=create
+// CHECK-YC-NOARG: -o
+// CHECK-YC-NOARG: cl-pch.obj
+// CHECK-YC-NOARG: -x
+// CHECK-YC-NOARG: "c++"
+// CHECK-YC-NOARG: cl-pch.cpp
+
+// RUN: %clang_cl -Werror /Yu /Fpycnoarg.pch /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YU-NOARG %s
+// Use .pch file, but don't build it.
+// CHECK-YU-NOARG-NOT: -emit-pch
+// CHECK-YU-NOARG: cc1
+// CHECK-YU-NOARG: -emit-obj
+// CHECK-YU-NOARG: -include-pch
+// CHECK-YU-NOARG: ycnoarg.pch
+// CHECK-YU-NOARG: -pch-through-hdrstop=use
+// CHECK-YU-NOARG: -o
+// CHECK-YU-NOARG: cl-pch.obj
+// CHECK-YU-NOARG: -x
+// CHECK-YU-NOARG: "c++"
+// CHECK-YU-NOARG: cl-pch.cpp
+
+// /Yc with no argument and no /FP
+// RUN: %clang_cl -Werror /Yc /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YC-NOARG-NOFP %s
+// 1. Create .pch file
+// CHECK-YC-NOARG-NOFP: cc1
+// CHECK-YC-NOARG-NOFP: -emit-pch
+// CHECK-YC-NOARG-NOFP: -pch-through-hdrstop=create
+// CHECK-YC-NOARG-NOFP: -o
+// CHECK-YC-NOARG-NOFP: cl-pch.pch
+// CHECK-YC-NOARG-NOFP: -x
+// CHECK-YC-NOARG-NOFP: "c++-header"
+// CHECK-YC-NOARG-NOFP: cl-pch.cpp
+// 2. Use .pch file: Includes cl-pch.pch
+// CHECK-YC-NOARG-NOFP: cc1
+// CHECK-YC-NOARG-NOFP: -emit-obj
+// CHECK-YC-NOARG-NOFP: -include-pch
+// CHECK-YC-NOARG-NOFP: cl-pch.pch
+// CHECK-YC-NOARG-NOFP: -pch-through-hdrstop=create
+// CHECK-YC-NOARG-NOFP: -o
+// CHECK-YC-NOARG-NOFP: cl-pch.obj
+// CHECK-YC-NOARG-NOFP: -x
+// CHECK-YC-NOARG-NOFP: "c++"
+// CHECK-YC-NOARG-NOFP: cl-pch.cpp
+
 // cl.exe warns on multiple /Yc, /Yu, /Fp arguments, but clang-cl silently just
 // uses the last one.  This is true for e.g. /Fo too, so not warning on this
 // is self-consistent with clang-cl's flag handling.
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to