Index: docs/ReleaseNotes.rst =================================================================== --- docs/ReleaseNotes.rst +++ docs/ReleaseNotes.rst @@ -87,6 +87,11 @@ 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: 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; -def warn_drv_ycyu_no_arg_clang_cl : Warning< - "support for '%0' without a filename not implemented yet; flag ignored">, - InGroup; def warn_drv_ycyu_different_arg_clang_cl : Warning< "support for '/Yc' and '/Yu' with different filenames not implemented yet; flags ignored">, InGroup; Index: include/clang/Basic/DiagnosticLexKinds.td =================================================================== --- include/clang/Basic/DiagnosticLexKinds.td +++ include/clang/Basic/DiagnosticLexKinds.td @@ -412,9 +412,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; +def warn_pp_hdrstop_filename_ignored : Warning< + "#pragma hdrstop filename not supported, " + "/Fp can be used to specify precompiled header filename">, + InGroup; def err_pp_file_not_found_not_fatal : Error< "'%0' file not found with include; use \"quotes\" instead">; def err_pp_error_opening_file : Error< Index: include/clang/Driver/CC1Options.td =================================================================== --- include/clang/Driver/CC1Options.td +++ include/clang/Driver/CC1Options.td @@ -604,6 +604,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_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: 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: 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(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/Frontend/CompilerInvocation.cpp =================================================================== --- lib/Frontend/CompilerInvocation.cpp +++ lib/Frontend/CompilerInvocation.cpp @@ -2862,6 +2862,9 @@ 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: 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/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 []" +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/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,12 +607,23 @@ 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 @@ 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/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: 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. 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-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/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-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/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();