From e8a64e5491260714c79dab65d1aa73245931d314 Mon Sep 17 00:00:00 2001 From: Christopher Di Bella Date: Thu, 22 Jul 2021 20:30:43 +0000 Subject: [PATCH] [clang][pp] adds '#pragma include_instead' `#pragma clang include_instead(
)` is a pragma that can be used by system headers (and only system headers) to indicate to a tool that the file containing said pragma is an implementation-detail header and should not be directly included by user code. The library alternative is very messy code that can be seen in the first diff of D106124, and we'd rather avoid that with something more universal. This patch takes the first step by warning a user when they include a detail header in their code, and suggests alternative headers that the user should include instead. Future work will involve adding a fixit to automate the process, as well as cleaning up modules diagnostics to not suggest said detail headers. Other tools, such as clangd can also take advantage of this pragma to add the correct user headers. Differential Revision: https://reviews.llvm.org/D106394 --- .../include/clang/Basic/DiagnosticLexKinds.td | 6 ++ clang/include/clang/Lex/HeaderSearch.h | 17 +++- clang/include/clang/Lex/Preprocessor.h | 5 +- clang/include/clang/Lex/PreprocessorLexer.h | 20 +++- clang/lib/Lex/Lexer.cpp | 4 +- clang/lib/Lex/PPDirectives.cpp | 6 ++ clang/lib/Lex/PPLexerChange.cpp | 45 ++++++++- clang/lib/Lex/Pragma.cpp | 92 +++++++++++++++---- .../Inputs/include_instead/bad-syntax.h | 7 ++ .../Inputs/include_instead/file-not-found.h | 3 + .../include_instead/non-system-header.h | 2 + .../Inputs/include_instead/private-x.h | 4 + .../Inputs/include_instead/private1.h | 2 + .../Inputs/include_instead/private2.h | 4 + .../Inputs/include_instead/private3.h | 5 + .../Inputs/include_instead/public-after.h | 2 + .../Inputs/include_instead/public-before.h | 5 + .../Inputs/include_instead/public-empty.h | 1 + clang/test/Preprocessor/include_instead.cpp | 16 ++++ .../include_instead_file_not_found.cpp | 2 + 20 files changed, 225 insertions(+), 23 deletions(-) create mode 100644 clang/test/Preprocessor/Inputs/include_instead/bad-syntax.h create mode 100644 clang/test/Preprocessor/Inputs/include_instead/file-not-found.h create mode 100644 clang/test/Preprocessor/Inputs/include_instead/non-system-header.h create mode 100644 clang/test/Preprocessor/Inputs/include_instead/private-x.h create mode 100644 clang/test/Preprocessor/Inputs/include_instead/private1.h create mode 100644 clang/test/Preprocessor/Inputs/include_instead/private2.h create mode 100644 clang/test/Preprocessor/Inputs/include_instead/private3.h create mode 100644 clang/test/Preprocessor/Inputs/include_instead/public-after.h create mode 100644 clang/test/Preprocessor/Inputs/include_instead/public-before.h create mode 100644 clang/test/Preprocessor/Inputs/include_instead/public-empty.h create mode 100644 clang/test/Preprocessor/include_instead.cpp create mode 100644 clang/test/Preprocessor/include_instead_file_not_found.cpp diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td index ce6d0d0394b48..690ea693bd366 100644 --- a/clang/include/clang/Basic/DiagnosticLexKinds.td +++ b/clang/include/clang/Basic/DiagnosticLexKinds.td @@ -300,6 +300,12 @@ 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>; + +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 #include @@ -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::SmallVector, 2>, + llvm::SmallSet, 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 7ab13640ce2c0..fe2327f0a4805 100644 --- a/clang/include/clang/Lex/Preprocessor.h +++ b/clang/include/clang/Lex/Preprocessor.h @@ -1953,7 +1953,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. @@ -2363,12 +2364,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 namespace clang { @@ -74,6 +76,13 @@ class PreprocessorLexer { /// we are currently in. SmallVector ConditionalStack; + struct IncludeInfo { + const FileEntry *File; + SourceLocation Location; + }; + // A complete history of all the files included by the current file. + llvm::StringMap 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 &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 556dd8daf652e..232329b57b424 100644 --- a/clang/lib/Lex/PPDirectives.cpp +++ b/clang/lib/Lex/PPDirectives.cpp @@ -2022,6 +2022,12 @@ 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( + {FilenameTok.getLiteralData(), FilenameTok.getLength()}, + 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> 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 c89061ba6d02e..ed82e51b08d5c 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 LexHeader(Preprocessor &PP, + Optional &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 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 File; + llvm::Optional FilenameTok = + LexHeader(*this, File, SuppressIncludeNotFoundError); + if (!FilenameTok) + return; + + Lex(Tok); + if (Tok.isNot(tok::r_paren)) { + Diag(Tok, diag::err_expected) << ")"; return; } + HeaderInfo.AddFileAlias( + TheLexer->getFileEntry(), + {FilenameTok->getLiteralData(), FilenameTok->getLength()}); +} + +/// HandlePragmaDependency - Handle \#pragma GCC dependency "foo" blah. +void Preprocessor::HandlePragmaDependency(Token &DependencyTok) { + Optional File; + llvm::Optional 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") {} @@ -1934,6 +1993,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/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 +// expected-error@-1{{expected (}} + +#pragma clang include_instead(] +// expected-error@-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() +// expected-error@-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() +// expected-error@-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 + +#pragma GCC system_header +#pragma clang include_instead() 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() 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() +#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() +#pragma clang include_instead() +#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 +#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 // no warning expected +#include // no warning expected +#include // 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 + +#include +// expected-error@-1{{header '' is an implementation detail; #include '' instead}} + +#include "include_instead/private2.h" +// expected-error@-1{{header '"include_instead/private2.h"' is an implementation detail; #include either '' or '"include_instead/public-after.h"' instead}} + +#include +// expected-error@-1{{header '' is an implementation detail; #include one of {'', '', '"include_instead/public-before.h"'} instead}} + +#include +#include 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