[clang] 0871954 - Revert "Revert "[clang][pp] adds '#pragma include_instead'""
Christopher Di Bella via cfe-commits
cfe-commits at lists.llvm.org
Thu Jul 29 12:22:57 PDT 2021
Author: Christopher Di Bella
Date: 2021-07-29T19:21:43Z
New Revision: 087195419719e908394081b4cc6f365170b9882c
URL: https://github.com/llvm/llvm-project/commit/087195419719e908394081b4cc6f365170b9882c
DIFF: https://github.com/llvm/llvm-project/commit/087195419719e908394081b4cc6f365170b9882c.diff
LOG: Revert "Revert "[clang][pp] adds '#pragma include_instead'""
Includes regression test for problem noted by @hans.
This reverts commit 973de7185606a21fd5e9d5e8c014fbf898c0e72f.
Differential Revision: https://reviews.llvm.org/D106898
Added:
clang/test/PCH/ms-pch-macro-include_instead-regression.c
clang/test/Preprocessor/Inputs/include_instead/bad-syntax.h
clang/test/Preprocessor/Inputs/include_instead/file-not-found.h
clang/test/Preprocessor/Inputs/include_instead/non-system-header.h
clang/test/Preprocessor/Inputs/include_instead/private-x.h
clang/test/Preprocessor/Inputs/include_instead/private1.h
clang/test/Preprocessor/Inputs/include_instead/private2.h
clang/test/Preprocessor/Inputs/include_instead/private3.h
clang/test/Preprocessor/Inputs/include_instead/public-after.h
clang/test/Preprocessor/Inputs/include_instead/public-before.h
clang/test/Preprocessor/Inputs/include_instead/public-empty.h
clang/test/Preprocessor/include_instead.cpp
clang/test/Preprocessor/include_instead_file_not_found.cpp
Modified:
clang/include/clang/Basic/DiagnosticLexKinds.td
clang/include/clang/Lex/HeaderSearch.h
clang/include/clang/Lex/Preprocessor.h
clang/include/clang/Lex/PreprocessorLexer.h
clang/lib/Lex/Lexer.cpp
clang/lib/Lex/PPDirectives.cpp
clang/lib/Lex/PPLexerChange.cpp
clang/lib/Lex/Pragma.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td
index 174f6c3dfd4c6..f621cdb466560 100644
--- a/clang/include/clang/Basic/DiagnosticLexKinds.td
+++ b/clang/include/clang/Basic/DiagnosticLexKinds.td
@@ -300,6 +300,13 @@ def pp_pragma_once_in_main_file : Warning<"#pragma once in main file">,
def pp_pragma_sysheader_in_main_file : Warning<
"#pragma system_header ignored in main file">,
InGroup<DiagGroup<"pragma-system-header-outside-header">>;
+
+def err_pragma_include_instead_not_sysheader : Error<
+ "'#pragma clang include_instead' cannot be used outside of system headers">;
+def err_pragma_include_instead_system_reserved : Error<
+ "header '%0' is an implementation detail; #include %select{'%2'|either '%2' "
+ "or '%3'|one of %2}1 instead">;
+
def pp_poisoning_existing_macro : Warning<"poisoning existing macro">;
def pp_out_of_date_dependency : Warning<
"current file is older than dependency %0">;
diff --git a/clang/include/clang/Lex/HeaderSearch.h b/clang/include/clang/Lex/HeaderSearch.h
index 93d6ea72270aa..a35a394f719b0 100644
--- a/clang/include/clang/Lex/HeaderSearch.h
+++ b/clang/include/clang/Lex/HeaderSearch.h
@@ -20,9 +20,12 @@
#include "clang/Lex/ModuleMap.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SetVector.h"
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringMap.h"
-#include "llvm/ADT/StringSet.h"
#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSet.h"
#include "llvm/Support/Allocator.h"
#include <cassert>
#include <cstddef>
@@ -110,6 +113,14 @@ struct HeaderFileInfo {
/// of the framework.
StringRef Framework;
+ /// List of aliases that this header is known as.
+ /// Most headers should only have at most one alias, but a handful
+ /// have two.
+ llvm::SetVector<llvm::SmallString<32>,
+ llvm::SmallVector<llvm::SmallString<32>, 2>,
+ llvm::SmallSet<llvm::SmallString<32>, 2>>
+ Aliases;
+
HeaderFileInfo()
: isImport(false), isPragmaOnce(false), DirInfo(SrcMgr::C_User),
External(false), isModuleHeader(false), isCompilingModuleHeader(false),
@@ -453,6 +464,10 @@ class HeaderSearch {
getFileInfo(File).DirInfo = SrcMgr::C_System;
}
+ void AddFileAlias(const FileEntry *File, StringRef Alias) {
+ getFileInfo(File).Aliases.insert(Alias);
+ }
+
/// Mark the specified file as part of a module.
void MarkFileModuleHeader(const FileEntry *FE,
ModuleMap::ModuleHeaderRole Role,
diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h
index 45ee0ad9fd21e..8ecd6a965cf86 100644
--- a/clang/include/clang/Lex/Preprocessor.h
+++ b/clang/include/clang/Lex/Preprocessor.h
@@ -1961,7 +1961,8 @@ class Preprocessor {
/// This either returns the EOF token and returns true, or
/// pops a level off the include stack and returns false, at which point the
/// client should call lex again.
- bool HandleEndOfFile(Token &Result, bool isEndOfMacro = false);
+ bool HandleEndOfFile(Token &Result, SourceLocation Loc,
+ bool isEndOfMacro = false);
/// Callback invoked when the current TokenLexer hits the end of its
/// token stream.
@@ -2373,12 +2374,14 @@ class Preprocessor {
// Pragmas.
void HandlePragmaDirective(PragmaIntroducer Introducer);
+ void ResolvePragmaIncludeInstead(SourceLocation Location) const;
public:
void HandlePragmaOnce(Token &OnceTok);
void HandlePragmaMark(Token &MarkTok);
void HandlePragmaPoison();
void HandlePragmaSystemHeader(Token &SysHeaderTok);
+ void HandlePragmaIncludeInstead(Token &Tok);
void HandlePragmaDependency(Token &DependencyTok);
void HandlePragmaPushMacro(Token &Tok);
void HandlePragmaPopMacro(Token &Tok);
diff --git a/clang/include/clang/Lex/PreprocessorLexer.h b/clang/include/clang/Lex/PreprocessorLexer.h
index 03b1cc2c10e2c..b43197a6031cb 100644
--- a/clang/include/clang/Lex/PreprocessorLexer.h
+++ b/clang/include/clang/Lex/PreprocessorLexer.h
@@ -14,11 +14,13 @@
#ifndef LLVM_CLANG_LEX_PREPROCESSORLEXER_H
#define LLVM_CLANG_LEX_PREPROCESSORLEXER_H
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Lex/HeaderSearch.h"
#include "clang/Lex/MultipleIncludeOpt.h"
#include "clang/Lex/Token.h"
-#include "clang/Basic/SourceLocation.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringMap.h"
#include <cassert>
namespace clang {
@@ -74,6 +76,13 @@ class PreprocessorLexer {
/// we are currently in.
SmallVector<PPConditionalInfo, 4> ConditionalStack;
+ struct IncludeInfo {
+ const FileEntry *File;
+ SourceLocation Location;
+ };
+ // A complete history of all the files included by the current file.
+ llvm::StringMap<IncludeInfo> IncludeHistory;
+
PreprocessorLexer() : FID() {}
PreprocessorLexer(Preprocessor *pp, FileID fid);
virtual ~PreprocessorLexer() = default;
@@ -175,6 +184,15 @@ class PreprocessorLexer {
ConditionalStack.clear();
ConditionalStack.append(CL.begin(), CL.end());
}
+
+ void addInclude(StringRef Filename, const FileEntry &File,
+ SourceLocation Location) {
+ IncludeHistory.insert({Filename, {&File, Location}});
+ }
+
+ const llvm::StringMap<IncludeInfo> &getIncludeHistory() const {
+ return IncludeHistory;
+ }
};
} // namespace clang
diff --git a/clang/lib/Lex/Lexer.cpp b/clang/lib/Lex/Lexer.cpp
index 3034af231e0ee..64944492eb99b 100644
--- a/clang/lib/Lex/Lexer.cpp
+++ b/clang/lib/Lex/Lexer.cpp
@@ -2811,11 +2811,11 @@ bool Lexer::LexEndOfFile(Token &Result, const char *CurPtr) {
ConditionalStack.pop_back();
}
+ SourceLocation EndLoc = getSourceLocation(BufferEnd);
// C99 5.1.1.2p2: If the file is non-empty and didn't end in a newline, issue
// a pedwarn.
if (CurPtr != BufferStart && (CurPtr[-1] != '\n' && CurPtr[-1] != '\r')) {
DiagnosticsEngine &Diags = PP->getDiagnostics();
- SourceLocation EndLoc = getSourceLocation(BufferEnd);
unsigned DiagID;
if (LangOpts.CPlusPlus11) {
@@ -2838,7 +2838,7 @@ bool Lexer::LexEndOfFile(Token &Result, const char *CurPtr) {
BufferPtr = CurPtr;
// Finally, let the preprocessor handle this.
- return PP->HandleEndOfFile(Result, isPragmaLexer());
+ return PP->HandleEndOfFile(Result, EndLoc, isPragmaLexer());
}
/// isNextPPTokenLParen - Return 1 if the next unexpanded token lexed from
diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp
index 60075d09280d9..b0300b3d68ad0 100644
--- a/clang/lib/Lex/PPDirectives.cpp
+++ b/clang/lib/Lex/PPDirectives.cpp
@@ -2032,6 +2032,10 @@ Preprocessor::ImportAction Preprocessor::HandleHeaderIncludeOrImport(
IsFrameworkFound, IsImportDecl, IsMapped, LookupFrom, LookupFromFile,
LookupFilename, RelativePath, SearchPath, SuggestedModule, isAngled);
+ // Record the header's filename for later use.
+ if (File)
+ CurLexer->addInclude(OriginalFilename, File->getFileEntry(), FilenameLoc);
+
if (usingPCHWithThroughHeader() && SkippingUntilPCHThroughHeader) {
if (File && isPCHThroughHeader(&File->getFileEntry()))
SkippingUntilPCHThroughHeader = false;
diff --git a/clang/lib/Lex/PPLexerChange.cpp b/clang/lib/Lex/PPLexerChange.cpp
index b979b965f46a6..16170969a3229 100644
--- a/clang/lib/Lex/PPLexerChange.cpp
+++ b/clang/lib/Lex/PPLexerChange.cpp
@@ -12,6 +12,7 @@
//===----------------------------------------------------------------------===//
#include "clang/Basic/FileManager.h"
+#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/HeaderSearch.h"
#include "clang/Lex/LexDiagnostic.h"
@@ -22,6 +23,7 @@
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/MemoryBufferRef.h"
#include "llvm/Support/Path.h"
+
using namespace clang;
//===----------------------------------------------------------------------===//
@@ -299,10 +301,46 @@ void Preprocessor::diagnoseMissingHeaderInUmbrellaDir(const Module &Mod) {
}
}
+void Preprocessor::ResolvePragmaIncludeInstead(
+ const SourceLocation Location) const {
+ assert(Location.isValid());
+ if (CurLexer == nullptr)
+ return;
+
+ if (SourceMgr.isInSystemHeader(Location))
+ return;
+
+ for (const auto &Include : CurLexer->getIncludeHistory()) {
+ StringRef Filename = Include.getKey();
+ const PreprocessorLexer::IncludeInfo &Info = Include.getValue();
+ ArrayRef<SmallString<32>> Aliases =
+ HeaderInfo.getFileInfo(Info.File).Aliases.getArrayRef();
+
+ if (Aliases.empty())
+ continue;
+
+ switch (Aliases.size()) {
+ case 1:
+ Diag(Info.Location, diag::err_pragma_include_instead_system_reserved)
+ << Filename << 0 << Aliases[0];
+ continue;
+ case 2:
+ Diag(Info.Location, diag::err_pragma_include_instead_system_reserved)
+ << Filename << 1 << Aliases[0] << Aliases[1];
+ continue;
+ default: {
+ Diag(Info.Location, diag::err_pragma_include_instead_system_reserved)
+ << Filename << 2 << ("{'" + llvm::join(Aliases, "', '") + "'}");
+ }
+ }
+ }
+}
+
/// HandleEndOfFile - This callback is invoked when the lexer hits the end of
/// the current file. This either returns the EOF token or pops a level off
/// the include stack and keeps going.
-bool Preprocessor::HandleEndOfFile(Token &Result, bool isEndOfMacro) {
+bool Preprocessor::HandleEndOfFile(Token &Result, SourceLocation EndLoc,
+ bool isEndOfMacro) {
assert(!CurTokenLexer &&
"Ending a file when currently in a macro!");
@@ -372,6 +410,9 @@ bool Preprocessor::HandleEndOfFile(Token &Result, bool isEndOfMacro) {
}
}
+ if (EndLoc.isValid())
+ ResolvePragmaIncludeInstead(EndLoc);
+
// Complain about reaching a true EOF within arc_cf_code_audited.
// We don't want to complain about reaching the end of a macro
// instantiation or a _Pragma.
@@ -560,7 +601,7 @@ bool Preprocessor::HandleEndOfTokenLexer(Token &Result) {
TokenLexerCache[NumCachedTokenLexers++] = std::move(CurTokenLexer);
// Handle this like a #include file being popped off the stack.
- return HandleEndOfFile(Result, true);
+ return HandleEndOfFile(Result, {}, true);
}
/// RemoveTopOfLexerStack - Pop the current lexer/macro exp off the top of the
diff --git a/clang/lib/Lex/Pragma.cpp b/clang/lib/Lex/Pragma.cpp
index cb3c5a1f89e31..4eadd99085923 100644
--- a/clang/lib/Lex/Pragma.cpp
+++ b/clang/lib/Lex/Pragma.cpp
@@ -13,6 +13,7 @@
#include "clang/Lex/Pragma.h"
#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/DiagnosticLex.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/IdentifierTable.h"
#include "clang/Basic/LLVM.h"
@@ -35,11 +36,12 @@
#include "clang/Lex/TokenLexer.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallVector.h"
-#include "llvm/ADT/StringSwitch.h"
#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/Timer.h"
@@ -495,43 +497,88 @@ void Preprocessor::HandlePragmaSystemHeader(Token &SysHeaderTok) {
SrcMgr::C_System);
}
-/// HandlePragmaDependency - Handle \#pragma GCC dependency "foo" blah.
-void Preprocessor::HandlePragmaDependency(Token &DependencyTok) {
+static llvm::Optional<Token> LexHeader(Preprocessor &PP,
+ Optional<FileEntryRef> &File,
+ bool SuppressIncludeNotFoundError) {
Token FilenameTok;
- if (LexHeaderName(FilenameTok, /*AllowConcatenation*/false))
- return;
+ if (PP.LexHeaderName(FilenameTok, /*AllowConcatenation*/ false))
+ return llvm::None;
// If the next token wasn't a header-name, diagnose the error.
if (FilenameTok.isNot(tok::header_name)) {
- Diag(FilenameTok.getLocation(), diag::err_pp_expects_filename);
- return;
+ PP.Diag(FilenameTok.getLocation(), diag::err_pp_expects_filename);
+ return llvm::None;
}
// Reserve a buffer to get the spelling.
SmallString<128> FilenameBuffer;
bool Invalid = false;
- StringRef Filename = getSpelling(FilenameTok, FilenameBuffer, &Invalid);
+ StringRef Filename = PP.getSpelling(FilenameTok, FilenameBuffer, &Invalid);
if (Invalid)
- return;
+ return llvm::None;
bool isAngled =
- GetIncludeFilenameSpelling(FilenameTok.getLocation(), Filename);
+ PP.GetIncludeFilenameSpelling(FilenameTok.getLocation(), Filename);
// If GetIncludeFilenameSpelling set the start ptr to null, there was an
// error.
if (Filename.empty())
- return;
+ return llvm::None;
// Search include directories for this file.
const DirectoryLookup *CurDir;
- Optional<FileEntryRef> File =
- LookupFile(FilenameTok.getLocation(), Filename, isAngled, nullptr,
- nullptr, CurDir, nullptr, nullptr, nullptr, nullptr, nullptr);
+ File = PP.LookupFile(FilenameTok.getLocation(), Filename, isAngled, nullptr,
+ nullptr, CurDir, nullptr, nullptr, nullptr, nullptr,
+ nullptr);
if (!File) {
if (!SuppressIncludeNotFoundError)
- Diag(FilenameTok, diag::err_pp_file_not_found) << Filename;
+ PP.Diag(FilenameTok, diag::err_pp_file_not_found) << Filename;
+ return llvm::None;
+ }
+
+ return FilenameTok;
+}
+
+/// HandlePragmaIncludeInstead - Handle \#pragma clang include_instead(header).
+void Preprocessor::HandlePragmaIncludeInstead(Token &Tok) {
+ // Get the current file lexer we're looking at. Ignore _Pragma 'files' etc.
+ PreprocessorLexer *TheLexer = getCurrentFileLexer();
+
+ if (!SourceMgr.isInSystemHeader(Tok.getLocation())) {
+ Diag(Tok, diag::err_pragma_include_instead_not_sysheader);
+ return;
+ }
+
+ Lex(Tok);
+ if (Tok.isNot(tok::l_paren)) {
+ Diag(Tok, diag::err_expected) << "(";
return;
}
+ Optional<FileEntryRef> File;
+ llvm::Optional<Token> FilenameTok =
+ LexHeader(*this, File, SuppressIncludeNotFoundError);
+ if (!FilenameTok)
+ return;
+
+ Lex(Tok);
+ if (Tok.isNot(tok::r_paren)) {
+ Diag(Tok, diag::err_expected) << ")";
+ return;
+ }
+
+ SmallString<128> FilenameBuffer;
+ StringRef Filename = getSpelling(*FilenameTok, FilenameBuffer);
+ HeaderInfo.AddFileAlias(TheLexer->getFileEntry(), Filename);
+}
+
+/// HandlePragmaDependency - Handle \#pragma GCC dependency "foo" blah.
+void Preprocessor::HandlePragmaDependency(Token &DependencyTok) {
+ Optional<FileEntryRef> File;
+ llvm::Optional<Token> FilenameTok =
+ LexHeader(*this, File, SuppressIncludeNotFoundError);
+ if (!FilenameTok)
+ return;
+
const FileEntry *CurFile = getCurrentFileLexer()->getFileEntry();
// If this file is older than the file it depends on, emit a diagnostic.
@@ -547,7 +594,7 @@ void Preprocessor::HandlePragmaDependency(Token &DependencyTok) {
// Remove the trailing ' ' if present.
if (!Message.empty())
Message.erase(Message.end()-1);
- Diag(FilenameTok, diag::pp_out_of_date_dependency) << Message;
+ Diag(*FilenameTok, diag::pp_out_of_date_dependency) << Message;
}
}
@@ -1022,6 +1069,18 @@ struct PragmaSystemHeaderHandler : public PragmaHandler {
}
};
+/// PragmaIncludeInsteadHandler - "\#pragma clang include_instead(header)" marks
+/// the current file as non-includable if the including header is not a system
+/// header.
+struct PragmaIncludeInsteadHandler : public PragmaHandler {
+ PragmaIncludeInsteadHandler() : PragmaHandler("include_instead") {}
+
+ void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
+ Token &IIToken) override {
+ PP.HandlePragmaIncludeInstead(IIToken);
+ }
+};
+
struct PragmaDependencyHandler : public PragmaHandler {
PragmaDependencyHandler() : PragmaHandler("dependency") {}
@@ -1985,6 +2044,7 @@ void Preprocessor::RegisterBuiltinPragmas() {
// #pragma clang ...
AddPragmaHandler("clang", new PragmaPoisonHandler());
AddPragmaHandler("clang", new PragmaSystemHeaderHandler());
+ AddPragmaHandler("clang", new PragmaIncludeInsteadHandler());
AddPragmaHandler("clang", new PragmaDebugHandler());
AddPragmaHandler("clang", new PragmaDependencyHandler());
AddPragmaHandler("clang", new PragmaDiagnosticHandler("clang"));
diff --git a/clang/test/PCH/ms-pch-macro-include_instead-regression.c b/clang/test/PCH/ms-pch-macro-include_instead-regression.c
new file mode 100644
index 0000000000000..1fac344a67b79
--- /dev/null
+++ b/clang/test/PCH/ms-pch-macro-include_instead-regression.c
@@ -0,0 +1,6 @@
+// Enabling MS extensions should allow us to add BAR definitions.
+// RUN: %clang_cc1 -DMSEXT -fms-extensions -DBAZ="\"Inputs/pch-through1.h\"" -emit-pch -o %t1.pch
+// RUN: %clang_cc1 -DMSEXT -fms-extensions -include-pch %t1.pch -verify %s
+
+#include BAZ
+// expected-no-diagnostics
diff --git a/clang/test/Preprocessor/Inputs/include_instead/bad-syntax.h b/clang/test/Preprocessor/Inputs/include_instead/bad-syntax.h
new file mode 100644
index 0000000000000..564570f9df730
--- /dev/null
+++ b/clang/test/Preprocessor/Inputs/include_instead/bad-syntax.h
@@ -0,0 +1,7 @@
+#pragma GCC system_header
+
+#pragma clang include_instead <include_instead/public-before.h>
+// expected-error at -1{{expected (}}
+
+#pragma clang include_instead(<include_instead/public-after.h>]
+// expected-error at -1{{expected )}}
diff --git a/clang/test/Preprocessor/Inputs/include_instead/file-not-found.h b/clang/test/Preprocessor/Inputs/include_instead/file-not-found.h
new file mode 100644
index 0000000000000..4c39d8b89e899
--- /dev/null
+++ b/clang/test/Preprocessor/Inputs/include_instead/file-not-found.h
@@ -0,0 +1,3 @@
+#pragma GCC system_header
+#pragma clang include_instead(<include_instead/does_not_exist.h>)
+// expected-error at -1{{'include_instead/does_not_exist.h' file not found}}
diff --git a/clang/test/Preprocessor/Inputs/include_instead/non-system-header.h b/clang/test/Preprocessor/Inputs/include_instead/non-system-header.h
new file mode 100644
index 0000000000000..052cb168ee333
--- /dev/null
+++ b/clang/test/Preprocessor/Inputs/include_instead/non-system-header.h
@@ -0,0 +1,2 @@
+#pragma clang include_instead(<include_instead/public1.h>)
+// expected-error at -1{{'#pragma clang include_instead' cannot be used outside of system headers}}
diff --git a/clang/test/Preprocessor/Inputs/include_instead/private-x.h b/clang/test/Preprocessor/Inputs/include_instead/private-x.h
new file mode 100644
index 0000000000000..5fb1a3b1ebac4
--- /dev/null
+++ b/clang/test/Preprocessor/Inputs/include_instead/private-x.h
@@ -0,0 +1,4 @@
+#include <include_instead/private1.h>
+
+#pragma GCC system_header
+#pragma clang include_instead(<include_instead/public-gcc-system-header-before-includes.h>)
diff --git a/clang/test/Preprocessor/Inputs/include_instead/private1.h b/clang/test/Preprocessor/Inputs/include_instead/private1.h
new file mode 100644
index 0000000000000..60a8a7e921228
--- /dev/null
+++ b/clang/test/Preprocessor/Inputs/include_instead/private1.h
@@ -0,0 +1,2 @@
+#pragma GCC system_header
+#pragma clang include_instead(<include_instead/public-before.h>)
diff --git a/clang/test/Preprocessor/Inputs/include_instead/private2.h b/clang/test/Preprocessor/Inputs/include_instead/private2.h
new file mode 100644
index 0000000000000..b2f6b5a83c9f4
--- /dev/null
+++ b/clang/test/Preprocessor/Inputs/include_instead/private2.h
@@ -0,0 +1,4 @@
+#pragma GCC system_header
+
+#pragma clang include_instead(<include_instead/public-before.h>)
+#pragma clang include_instead("include_instead/public-after.h")
diff --git a/clang/test/Preprocessor/Inputs/include_instead/private3.h b/clang/test/Preprocessor/Inputs/include_instead/private3.h
new file mode 100644
index 0000000000000..bfbecc9ec3b9e
--- /dev/null
+++ b/clang/test/Preprocessor/Inputs/include_instead/private3.h
@@ -0,0 +1,5 @@
+#pragma GCC system_header
+
+#pragma clang include_instead(<include_instead/public-after.h>)
+#pragma clang include_instead(<include_instead/public-empty.h>)
+#pragma clang include_instead("include_instead/public-before.h")
diff --git a/clang/test/Preprocessor/Inputs/include_instead/public-after.h b/clang/test/Preprocessor/Inputs/include_instead/public-after.h
new file mode 100644
index 0000000000000..89af3065e39da
--- /dev/null
+++ b/clang/test/Preprocessor/Inputs/include_instead/public-after.h
@@ -0,0 +1,2 @@
+#include <include_instead/private2.h>
+#pragma GCC system_header
diff --git a/clang/test/Preprocessor/Inputs/include_instead/public-before.h b/clang/test/Preprocessor/Inputs/include_instead/public-before.h
new file mode 100644
index 0000000000000..85c47deff4e88
--- /dev/null
+++ b/clang/test/Preprocessor/Inputs/include_instead/public-before.h
@@ -0,0 +1,5 @@
+#pragma GCC system_header
+
+#include <include_instead/private1.h> // no warning expected
+#include <include_instead/private2.h> // no warning expected
+#include <include_instead/private3.h> // no warning expected
diff --git a/clang/test/Preprocessor/Inputs/include_instead/public-empty.h b/clang/test/Preprocessor/Inputs/include_instead/public-empty.h
new file mode 100644
index 0000000000000..5379acae968b3
--- /dev/null
+++ b/clang/test/Preprocessor/Inputs/include_instead/public-empty.h
@@ -0,0 +1 @@
+// This file simply needs to exist.
diff --git a/clang/test/Preprocessor/include_instead.cpp b/clang/test/Preprocessor/include_instead.cpp
new file mode 100644
index 0000000000000..6111fa8c886fa
--- /dev/null
+++ b/clang/test/Preprocessor/include_instead.cpp
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -I %S/Inputs %s
+
+#include <include_instead/bad-syntax.h>
+#include <include_instead/non-system-header.h>
+
+#include <include_instead/private1.h>
+// expected-error at -1{{header '<include_instead/private1.h>' is an implementation detail; #include '<include_instead/public-before.h>' instead}}
+
+#include "include_instead/private2.h"
+// expected-error at -1{{header '"include_instead/private2.h"' is an implementation detail; #include either '<include_instead/public-before.h>' or '"include_instead/public-after.h"' instead}}
+
+#include <include_instead/private3.h>
+// expected-error at -1{{header '<include_instead/private3.h>' is an implementation detail; #include one of {'<include_instead/public-after.h>', '<include_instead/public-empty.h>', '"include_instead/public-before.h"'} instead}}
+
+#include <include_instead/public-before.h>
+#include <include_instead/public-after.h>
diff --git a/clang/test/Preprocessor/include_instead_file_not_found.cpp b/clang/test/Preprocessor/include_instead_file_not_found.cpp
new file mode 100644
index 0000000000000..5d1bc88505dd0
--- /dev/null
+++ b/clang/test/Preprocessor/include_instead_file_not_found.cpp
@@ -0,0 +1,2 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -I %S/Inputs %s
+#include <include_instead/file-not-found.h>
More information about the cfe-commits
mailing list