[clang-cl, PCH] Support for /Yc and /Yu without filename and #pragma hdrstop

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

llvm-svn: 341963
This commit is contained in:
Mike Rice 2018-09-11 17:10:44 +00:00
parent 12fd6bd4ad
commit 58df1affed
20 changed files with 333 additions and 66 deletions

View 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.
- ...

View 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>;

View 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<

View 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">,

View 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();
/// Process directives while skipping until the through header is found.
void HandleSkippedThroughHeaderDirective(Token &Result,
/// 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 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

View 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

View 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();

View 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));
}
}

View 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();

View 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:

View 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

View 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() {

View 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();

View file

@ -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.

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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

View file

@ -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")

View file

@ -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; }

View file

@ -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();