Index: include/clang/Basic/SourceManager.h =================================================================== --- include/clang/Basic/SourceManager.h +++ include/clang/Basic/SourceManager.h @@ -657,6 +657,8 @@ /// \brief The file ID for the main source file of the translation unit. FileID MainFileID; + bool MainFileIsHeader = false; + /// \brief The file ID for the precompiled preamble there is one. FileID PreambleFileID; @@ -759,6 +761,14 @@ MainFileID = FID; } + void setMainFileHeader(bool mainFileIsHeader) { + MainFileIsHeader = mainFileIsHeader; + } + + bool isMainFileHeader() const { + return MainFileIsHeader; + } + /// \brief Set the file ID for the precompiled preamble. void setPreambleFileID(FileID Preamble) { assert(PreambleFileID.isInvalid() && "PreambleFileID already set!"); Index: include/clang/Frontend/FrontendOptions.h =================================================================== --- include/clang/Frontend/FrontendOptions.h +++ include/clang/Frontend/FrontendOptions.h @@ -88,18 +88,23 @@ /// \brief The kind of input, e.g., C source, AST file, LLVM IR. InputKind Kind; + bool IsHeader; + /// \brief Whether we're dealing with a 'system' input (vs. a 'user' input). bool IsSystem; public: FrontendInputFile() : Buffer(nullptr), Kind(IK_None), IsSystem(false) { } - FrontendInputFile(StringRef File, InputKind Kind, bool IsSystem = false) - : File(File.str()), Buffer(nullptr), Kind(Kind), IsSystem(IsSystem) { } - FrontendInputFile(llvm::MemoryBuffer *buffer, InputKind Kind, + FrontendInputFile(StringRef File, InputKind Kind, bool IsHeader = false, bool IsSystem = false) - : Buffer(buffer), Kind(Kind), IsSystem(IsSystem) { } + : File(File.str()), Buffer(nullptr), Kind(Kind), IsHeader(IsHeader) + , IsSystem(IsSystem) { } + FrontendInputFile(llvm::MemoryBuffer *buffer, InputKind Kind, + bool IsHeader = false, bool IsSystem = false) + : Buffer(buffer), Kind(Kind), IsHeader(IsHeader), IsSystem(IsSystem) { } InputKind getKind() const { return Kind; } + bool isHeader() const { return IsHeader; } bool isSystem() const { return IsSystem; } bool isEmpty() const { return File.empty() && Buffer == nullptr; } Index: lib/Frontend/CompilerInstance.cpp =================================================================== --- lib/Frontend/CompilerInstance.cpp +++ lib/Frontend/CompilerInstance.cpp @@ -724,6 +724,7 @@ const FrontendOptions &Opts) { SrcMgr::CharacteristicKind Kind = Input.isSystem() ? SrcMgr::C_System : SrcMgr::C_User; + SourceMgr.setMainFileHeader(Input.isHeader()); if (Input.isBuffer()) { SourceMgr.setMainFileID(SourceMgr.createFileID( Index: lib/Frontend/CompilerInvocation.cpp =================================================================== --- lib/Frontend/CompilerInvocation.cpp +++ lib/Frontend/CompilerInvocation.cpp @@ -1141,6 +1141,7 @@ } InputKind DashX = IK_None; + bool IsHeader = false; if (const Arg *A = Args.getLastArg(OPT_x)) { DashX = llvm::StringSwitch(A->getValue()) .Case("c", IK_C) @@ -1168,6 +1169,13 @@ if (DashX == IK_None) Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << A->getValue(); + IsHeader = llvm::StringSwitch(A->getValue()) + .Case("c-header", true) + .Case("cl-header", true) + .Case("objective-c-header", true) + .Case("c++-header", true) + .Case("objective-c++-header", true) + .Default(false); } // '-' is the default input if none is given. @@ -1184,7 +1192,7 @@ if (i == 0) DashX = IK; } - Opts.Inputs.emplace_back(std::move(Inputs[i]), IK); + Opts.Inputs.emplace_back(std::move(Inputs[i]), IK, IsHeader); } return DashX; Index: lib/Frontend/FrontendActions.cpp =================================================================== --- lib/Frontend/FrontendActions.cpp +++ lib/Frontend/FrontendActions.cpp @@ -375,7 +375,7 @@ Module::getModuleInputBufferName()); // Ownership of InputBuffer will be transferred to the SourceManager. setCurrentInput(FrontendInputFile(InputBuffer.release(), getCurrentFileKind(), - Module->IsSystem)); + false, Module->IsSystem)); return true; } Index: lib/Lex/PPDirectives.cpp =================================================================== --- lib/Lex/PPDirectives.cpp +++ lib/Lex/PPDirectives.cpp @@ -1834,7 +1834,10 @@ // diagnostic. const DirectoryLookup *Lookup = CurDirLookup; const FileEntry *LookupFromFile = nullptr; - if (isInPrimaryFile()) { + if (isInPrimaryFile() && SourceMgr.isMainFileHeader()) { + // If the main file is a header, then it's either for PCH/AST generation, + // or libclang opened it. Either way, handle it as a normal include below. + } else if (isInPrimaryFile()) { Lookup = nullptr; Diag(IncludeNextTok, diag::pp_include_next_in_primary); } else if (CurSubmodule) { Index: lib/Lex/PPMacroExpansion.cpp =================================================================== --- lib/Lex/PPMacroExpansion.cpp +++ lib/Lex/PPMacroExpansion.cpp @@ -1389,7 +1389,10 @@ // Preprocessor::HandleIncludeNextDirective. const DirectoryLookup *Lookup = PP.GetCurDirLookup(); const FileEntry *LookupFromFile = nullptr; - if (PP.isInPrimaryFile()) { + if (PP.isInPrimaryFile() && PP.getSourceManager().isMainFileHeader()) { + // If the main file is a header, then it's either for PCH/AST generation, + // or libclang opened it. Either way, handle it as a normal include below. + } else if (PP.isInPrimaryFile()) { Lookup = nullptr; PP.Diag(Tok, diag::pp_include_next_in_primary); } else if (PP.getCurrentSubmodule()) { Index: lib/Lex/Pragma.cpp =================================================================== --- lib/Lex/Pragma.cpp +++ lib/Lex/Pragma.cpp @@ -354,7 +354,9 @@ /// HandlePragmaOnce - Handle \#pragma once. OnceTok is the 'once'. /// void Preprocessor::HandlePragmaOnce(Token &OnceTok) { - if (isInPrimaryFile()) { + // If the main file is a header, then it's either for PCH/AST generation, + // or libclang opened it. Allow #pragma once either way. + if (isInPrimaryFile() && !SourceMgr.isMainFileHeader()) { Diag(OnceTok, diag::pp_pragma_once_in_main_file); return; } Index: test/Preprocessor/header_is_main_file.c =================================================================== --- /dev/null +++ test/Preprocessor/header_is_main_file.c @@ -0,0 +1,8 @@ +// RUN: %clang_cc1 -x c-header -ffreestanding -Eonly -verify %s +// expected-no-diagnostics + +#pragma once +#include_next "stdint.h" +#if !__has_include_next("stdint.h") +#error "__has_include_next failed" +#endif