]> granicus.if.org Git - clang/commitdiff
[clang-cl, PCH] Support for /Yc and /Yu without filename and #pragma hdrstop
authorMike Rice <michael.p.rice@intel.com>
Tue, 11 Sep 2018 17:10:44 +0000 (17:10 +0000)
committerMike Rice <michael.p.rice@intel.com>
Tue, 11 Sep 2018 17:10:44 +0000 (17:10 +0000)
With clang-cl, when the user specifies /Yc or /Yu without a filename
the compiler uses a #pragma hdrstop in the main source file to
determine the end of the PCH. If a header is specified with /Yc or
/Yu #pragma hdrstop has no effect.

The optional #pragma hdrstop filename argument is not yet supported.

Differential Revision: https://reviews.llvm.org/D51391

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@341963 91177308-0d34-0410-b5e6-96231b3b80d8

20 files changed:
docs/ReleaseNotes.rst
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 [new file with mode: 0644]
test/PCH/Inputs/pch-no-hdrstop-use.cpp [new file with mode: 0644]
test/PCH/pch-hdrstop-err.cpp [new file with mode: 0644]
test/PCH/pch-hdrstop-warn.cpp [new file with mode: 0644]
test/PCH/pch-hdrstop.cpp [new file with mode: 0644]
test/PCH/pch-no-hdrstop.cpp [new file with mode: 0644]

index b526fd17ab5393374a6b39d146c2db668c801b37..b3eb9d5e1084b2a5c8064c21d71c27cfb5ee4986 100644 (file)
@@ -87,6 +87,11 @@ Attribute Changes in Clang
 Windows Support
 ---------------
 
+- clang-cl now supports the use of the precompiled header options /Yc and /Yu
+  without the filename argument. When these options are used without the
+  filename, a `#pragma hdrstop` inside the source marks the end of the
+  precompiled code.
+
 - ...
 
 
index 7f75f45c6578193a704f11dbb52a971ac19245b1..262861b6a2d9de5672d2312fb494355e5a00937e 100644 (file)
@@ -151,9 +151,6 @@ def warn_drv_unknown_argument_clang_cl_with_suggestion : Warning<
   "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 8cf6d7e7c09593e2f019e6c299952a623a46eec7..7e8bea7c2496144ab49f312dbfe0aff58bf2719b 100644 (file)
@@ -412,9 +412,16 @@ def err_pp_through_header_not_found : Error<
 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 d43d69b51fa55f16970aeff4e6e3417c9fc16b19..3ac092645a4697eec997049322bb5673e800fa1c 100644 (file)
@@ -604,6 +604,10 @@ def foverride_record_layout_EQ : Joined<["-"], "foverride-record-layout=">,
 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_create : Flag<["-"], "pch-through-hdrstop-create">,
+  HelpText<"When creating a PCH, stop PCH generation after #pragma hdrstop.">;
+def pch_through_hdrstop_use : Flag<["-"], "pch-through-hdrstop-use">,
+  HelpText<"When using a PCH, skip tokens until after a #pragma hdrstop.">;
 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 cc2d59b13363b505a71a68f6ce92b57e238ddfcf..02ea80aa3fc70455c12e1fad4eaf035fee574213 100644 (file)
@@ -726,6 +726,9 @@ private:
   /// 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 @@ public:
   /// 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();
+
+  /// 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 is found.
-  void HandleSkippedThroughHeaderDirective(Token &Result,
+  /// 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 @@ public:
   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 5134aeaa6a297b67df2cb8c1a394a9846ea70932..6b3823911cfd6f6c522b4b331eb8c9cd40b5bf92 100644 (file)
@@ -54,6 +54,16 @@ public:
   /// 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 14cfc48c8f2f5b4ec294e4e7221c2f4384ed2b3a..4764d4e38d7c458e4ac8f0748457de0c94aa0b3b 100644 (file)
@@ -2982,22 +2982,9 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args,
     }
   }
 
-  // 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 @@ std::string Driver::GetClPchPath(Compilation &C, StringRef BaseName) const {
     // 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 959a9b1fbcabf5c3a8babde6d6813a5a4534675b..f3f02a0aa4ad5bc49522eb22008395cd5c7ebac8 100644 (file)
@@ -1105,10 +1105,19 @@ void Clang::AddPreprocessingOptions(Compilation &C, const JobAction &JA,
       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 aea81069b5e08d98095e635a2953f9406e3ddebc..b11581ddf79be919b7e6bd5a1399a421553aba01 100644 (file)
@@ -2862,6 +2862,9 @@ static void ParsePreprocessorArgs(PreprocessorOptions &Opts, ArgList &Args,
                                   frontend::ActionKind Action) {
   Opts.ImplicitPCHInclude = Args.getLastArgValue(OPT_include_pch);
   Opts.ImplicitPTHInclude = Args.getLastArgValue(OPT_include_pth);
+  Opts.PCHWithHdrStop = Args.hasArg(OPT_pch_through_hdrstop_create) ||
+                        Args.hasArg(OPT_pch_through_hdrstop_use);
+  Opts.PCHWithHdrStopCreate = Args.hasArg(OPT_pch_through_hdrstop_create);
   Opts.PCHThroughHeader = Args.getLastArgValue(OPT_pch_through_header_EQ);
   if (const Arg *A = Args.getLastArg(OPT_token_cache))
       Opts.TokenCache = A->getValue();
index 66a9faa6e60a5333c2dadd18cb19af67abe0f172..2bb96c1f15adf280931b5d61747868fed894f375 100644 (file)
@@ -887,18 +887,29 @@ private:
   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 @@ void Preprocessor::HandleDirective(Token &Result) {
   // 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 37c0a23646c59f87acd8153a997e16f528b718f7..0a63ed724cf6b6cdb675bf75c4f58b5c6a886f2a 100644 (file)
@@ -876,6 +876,37 @@ void Preprocessor::HandlePragmaModuleBuild(Token &Tok) {
                                        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 @@ public:
   }
 };
 
+/// "\#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 @@ void Preprocessor::RegisterBuiltinPragmas() {
   if (LangOpts.MicrosoftExt) {
     AddPragmaHandler(new PragmaWarningHandler());
     AddPragmaHandler(new PragmaIncludeAliasHandler());
+    AddPragmaHandler(new PragmaHdrstopHandler());
   }
 
   // Pragmas added by plugins
index c7da41172f88bcaacc7dc193dd592ef13f59e465..dcff51ad61b9d44edf9275fb8f1cb466920134f9 100644 (file)
@@ -149,6 +149,10 @@ Preprocessor::Preprocessor(std::shared_ptr<PreprocessorOptions> PPOpts,
     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 @@ void Preprocessor::EnterMainSourceFile() {
   }
 
   // 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,12 +607,23 @@ bool Preprocessor::usingPCHWithThroughHeader() {
          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());
@@ -616,12 +632,18 @@ void Preprocessor::SkipTokensUntilPCHThroughHeader() {
       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 e71b5a9d1e6d0e16fb9a139f3d93bbf861487758..f7703b1bfd8a048551be479b509725c03d7b2461 100644 (file)
@@ -141,26 +141,26 @@ void clang::ParseAST(Sema &S, bool PrintStats, bool SkipFunctionBodies) {
     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 @@ void clang::ParseAST(Sema &S, bool PrintStats, bool SkipFunctionBodies) {
   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 0103db93e1a1c54c8cbe9fbbad3820c7a908d2c7..0d18171d94e6089c96754d1d67c6df03521530e7 100644 (file)
 // 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.
diff --git a/test/PCH/Inputs/pch-hdrstop-use.cpp b/test/PCH/Inputs/pch-hdrstop-use.cpp
new file mode 100644 (file)
index 0000000..5cbb7a7
--- /dev/null
@@ -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);
+}
diff --git a/test/PCH/Inputs/pch-no-hdrstop-use.cpp b/test/PCH/Inputs/pch-no-hdrstop-use.cpp
new file mode 100644 (file)
index 0000000..ec4f962
--- /dev/null
@@ -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;
+}
diff --git a/test/PCH/pch-hdrstop-err.cpp b/test/PCH/pch-hdrstop-err.cpp
new file mode 100644 (file)
index 0000000..7e66c96
--- /dev/null
@@ -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
diff --git a/test/PCH/pch-hdrstop-warn.cpp b/test/PCH/pch-hdrstop-warn.cpp
new file mode 100644 (file)
index 0000000..dbd592f
--- /dev/null
@@ -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")
diff --git a/test/PCH/pch-hdrstop.cpp b/test/PCH/pch-hdrstop.cpp
new file mode 100644 (file)
index 0000000..5fe83a6
--- /dev/null
@@ -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; }
diff --git a/test/PCH/pch-no-hdrstop.cpp b/test/PCH/pch-no-hdrstop.cpp
new file mode 100644 (file)
index 0000000..3eb4c78
--- /dev/null
@@ -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();