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.
+
- ...
"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>;
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<
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">,
/// 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;
/// 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,
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
/// 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
}
}
- // 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);
// 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();
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));
}
}
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();
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();
}
// 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:
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".
}
};
+/// "\#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.
if (LangOpts.MicrosoftExt) {
AddPragmaHandler(new PragmaWarningHandler());
AddPragmaHandler(new PragmaIncludeAliasHandler());
+ AddPragmaHandler(new PragmaHdrstopHandler());
}
// Pragmas added by plugins
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())
}
// 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) {
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());
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() {
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.
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();
// 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.
--- /dev/null
+#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);
+}
--- /dev/null
+#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;
+}
--- /dev/null
+// 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
--- /dev/null
+// 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")
--- /dev/null
+// 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; }
--- /dev/null
+// 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();