summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard Smith <richard-llvm@metafoo.co.uk>2017-06-29 02:19:42 +0000
committerRichard Smith <richard-llvm@metafoo.co.uk>2017-06-29 02:19:42 +0000
commit2d7d5c241bce687a4c7018a023efb7b22769ca9d (patch)
treea3fdc9c5332f32302de0c1d4644c81fc026ca59c
parent64b75be74b549be56038f8795788ebb73714709e (diff)
Track the set of module maps read while building a .pcm file and reload those when preprocessing from that .pcm file.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@306628 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/clang/Basic/SourceManager.h50
-rw-r--r--include/clang/Lex/HeaderSearch.h2
-rw-r--r--include/clang/Serialization/ASTReader.h7
-rw-r--r--lib/Frontend/CompilerInstance.cpp7
-rw-r--r--lib/Frontend/DependencyFile.cpp6
-rw-r--r--lib/Frontend/FrontendAction.cpp42
-rw-r--r--lib/Lex/ModuleMap.cpp3
-rw-r--r--lib/Serialization/ASTReader.cpp23
-rw-r--r--lib/Serialization/ASTWriter.cpp18
-rw-r--r--test/Modules/Inputs/preprocess-decluse/a.h1
-rw-r--r--test/Modules/Inputs/preprocess-decluse/a.modulemap1
-rw-r--r--test/Modules/Inputs/preprocess-decluse/b.h1
-rw-r--r--test/Modules/Inputs/preprocess-decluse/b.modulemap1
-rw-r--r--test/Modules/Inputs/preprocess-decluse/main.modulemap1
-rw-r--r--test/Modules/preprocess-decluse.cpp18
15 files changed, 127 insertions, 54 deletions
diff --git a/include/clang/Basic/SourceManager.h b/include/clang/Basic/SourceManager.h
index 5e01f64167..e658af742e 100644
--- a/include/clang/Basic/SourceManager.h
+++ b/include/clang/Basic/SourceManager.h
@@ -80,9 +80,19 @@ namespace SrcMgr {
/// system_header is seen or in various other cases.
///
enum CharacteristicKind {
- C_User, C_System, C_ExternCSystem
+ C_User, C_System, C_ExternCSystem, C_User_ModuleMap, C_System_ModuleMap
};
+ /// Determine whether a file / directory characteristic is for system code.
+ inline bool isSystem(CharacteristicKind CK) {
+ return CK != C_User && CK != C_User_ModuleMap;
+ }
+
+ /// Determine whether a file characteristic is for a module map.
+ inline bool isModuleMap(CharacteristicKind CK) {
+ return CK == C_User_ModuleMap || CK == C_System_ModuleMap;
+ }
+
/// \brief One instance of this struct is kept for every file loaded or used.
///
/// This object owns the MemoryBuffer object.
@@ -251,12 +261,14 @@ namespace SrcMgr {
/// preprocessing of this \#include, including this SLocEntry.
///
/// Zero means the preprocessor didn't provide such info for this SLocEntry.
- unsigned NumCreatedFIDs;
+ unsigned NumCreatedFIDs : 31;
+
+ /// \brief Whether this FileInfo has any \#line directives.
+ unsigned HasLineDirectives : 1;
- /// \brief Contains the ContentCache* and the bits indicating the
- /// characteristic of the file and whether it has \#line info, all
- /// bitmangled together.
- uintptr_t Data;
+ /// \brief The content cache and the characteristic of the file.
+ llvm::PointerIntPair<const ContentCache*, 3, CharacteristicKind>
+ ContentAndKind;
friend class clang::SourceManager;
friend class clang::ASTWriter;
@@ -269,10 +281,9 @@ namespace SrcMgr {
FileInfo X;
X.IncludeLoc = IL.getRawEncoding();
X.NumCreatedFIDs = 0;
- X.Data = (uintptr_t)Con;
- assert((X.Data & 7) == 0 &&"ContentCache pointer insufficiently aligned");
- assert((unsigned)FileCharacter < 4 && "invalid file character");
- X.Data |= (unsigned)FileCharacter;
+ X.HasLineDirectives = false;
+ X.ContentAndKind.setPointer(Con);
+ X.ContentAndKind.setInt(FileCharacter);
return X;
}
@@ -280,22 +291,22 @@ namespace SrcMgr {
return SourceLocation::getFromRawEncoding(IncludeLoc);
}
- const ContentCache* getContentCache() const {
- return reinterpret_cast<const ContentCache*>(Data & ~uintptr_t(7));
+ const ContentCache *getContentCache() const {
+ return ContentAndKind.getPointer();
}
/// \brief Return whether this is a system header or not.
CharacteristicKind getFileCharacteristic() const {
- return (CharacteristicKind)(Data & 3);
+ return ContentAndKind.getInt();
}
/// \brief Return true if this FileID has \#line directives in it.
- bool hasLineDirectives() const { return (Data & 4) != 0; }
+ bool hasLineDirectives() const { return HasLineDirectives; }
/// \brief Set the flag that indicates that this FileID has
/// line table entries associated with it.
void setHasLineDirectives() {
- Data |= 4;
+ HasLineDirectives = true;
}
};
@@ -407,6 +418,8 @@ namespace SrcMgr {
};
public:
+ SLocEntry() : Offset(), IsExpansion(), File() {}
+
unsigned getOffset() const { return Offset; }
bool isExpansion() const { return IsExpansion; }
@@ -789,9 +802,8 @@ public:
FileID createFileID(const FileEntry *SourceFile, SourceLocation IncludePos,
SrcMgr::CharacteristicKind FileCharacter,
int LoadedID = 0, unsigned LoadedOffset = 0) {
- const SrcMgr::ContentCache *
- IR = getOrCreateContentCache(SourceFile,
- /*isSystemFile=*/FileCharacter != SrcMgr::C_User);
+ const SrcMgr::ContentCache *IR =
+ getOrCreateContentCache(SourceFile, isSystem(FileCharacter));
assert(IR && "getOrCreateContentCache() cannot return NULL");
return createFileID(IR, IncludePos, FileCharacter, LoadedID, LoadedOffset);
}
@@ -1360,7 +1372,7 @@ public:
/// \brief Returns if a SourceLocation is in a system header.
bool isInSystemHeader(SourceLocation Loc) const {
- return getFileCharacteristic(Loc) != SrcMgr::C_User;
+ return isSystem(getFileCharacteristic(Loc));
}
/// \brief Returns if a SourceLocation is in an "extern C" system header.
diff --git a/include/clang/Lex/HeaderSearch.h b/include/clang/Lex/HeaderSearch.h
index ee17dcbb8b..1b7f80c87f 100644
--- a/include/clang/Lex/HeaderSearch.h
+++ b/include/clang/Lex/HeaderSearch.h
@@ -47,7 +47,7 @@ struct HeaderFileInfo {
/// whether it is C++ clean or not. This can be set by the include paths or
/// by \#pragma gcc system_header. This is an instance of
/// SrcMgr::CharacteristicKind.
- unsigned DirInfo : 2;
+ unsigned DirInfo : 3;
/// \brief Whether this header file info was supplied by an external source,
/// and has not changed since.
diff --git a/include/clang/Serialization/ASTReader.h b/include/clang/Serialization/ASTReader.h
index 737f6fb3d4..9b94aadd76 100644
--- a/include/clang/Serialization/ASTReader.h
+++ b/include/clang/Serialization/ASTReader.h
@@ -1146,6 +1146,7 @@ private:
time_t StoredTime;
bool Overridden;
bool Transient;
+ bool TopLevelModuleMap;
};
/// \brief Reads the stored information about an input file.
@@ -2249,6 +2250,12 @@ public:
llvm::function_ref<void(const serialization::InputFile &IF,
bool isSystem)> Visitor);
+ /// Visit all the top-level module maps loaded when building the given module
+ /// file.
+ void visitTopLevelModuleMaps(serialization::ModuleFile &MF,
+ llvm::function_ref<
+ void(const FileEntry *)> Visitor);
+
bool isProcessingUpdateRecords() { return ProcessingUpdateRecords; }
};
diff --git a/lib/Frontend/CompilerInstance.cpp b/lib/Frontend/CompilerInstance.cpp
index e5da2ae4f2..3b5335478d 100644
--- a/lib/Frontend/CompilerInstance.cpp
+++ b/lib/Frontend/CompilerInstance.cpp
@@ -825,8 +825,11 @@ bool CompilerInstance::InitializeSourceManager(
const FrontendInputFile &Input, DiagnosticsEngine &Diags,
FileManager &FileMgr, SourceManager &SourceMgr, HeaderSearch *HS,
DependencyOutputOptions &DepOpts, const FrontendOptions &Opts) {
- SrcMgr::CharacteristicKind
- Kind = Input.isSystem() ? SrcMgr::C_System : SrcMgr::C_User;
+ SrcMgr::CharacteristicKind Kind =
+ Input.getKind().getFormat() == InputKind::ModuleMap
+ ? Input.isSystem() ? SrcMgr::C_System_ModuleMap
+ : SrcMgr::C_User_ModuleMap
+ : Input.isSystem() ? SrcMgr::C_System : SrcMgr::C_User;
if (Input.isBuffer()) {
SourceMgr.setMainFileID(SourceMgr.createFileID(
diff --git a/lib/Frontend/DependencyFile.cpp b/lib/Frontend/DependencyFile.cpp
index bd14c53e4d..561eb9c4a3 100644
--- a/lib/Frontend/DependencyFile.cpp
+++ b/lib/Frontend/DependencyFile.cpp
@@ -55,8 +55,8 @@ struct DepCollectorPPCallbacks : public PPCallbacks {
llvm::sys::path::remove_leading_dotslash(FE->getName());
DepCollector.maybeAddDependency(Filename, /*FromModule*/false,
- FileType != SrcMgr::C_User,
- /*IsModuleFile*/false, /*IsMissing*/false);
+ isSystem(FileType),
+ /*IsModuleFile*/false, /*IsMissing*/false);
}
void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
@@ -265,7 +265,7 @@ bool DFGImpl::FileMatchesDepCriteria(const char *Filename,
if (IncludeSystemHeaders)
return true;
- return FileType == SrcMgr::C_User;
+ return !isSystem(FileType);
}
void DFGImpl::FileChanged(SourceLocation Loc,
diff --git a/lib/Frontend/FrontendAction.cpp b/lib/Frontend/FrontendAction.cpp
index f81a06b318..eed8631bdb 100644
--- a/lib/Frontend/FrontendAction.cpp
+++ b/lib/Frontend/FrontendAction.cpp
@@ -200,12 +200,12 @@ FrontendAction::CreateWrappedASTConsumer(CompilerInstance &CI,
///
/// \param CI The compiler instance.
/// \param InputFile Populated with the filename from the line marker.
-/// \param AddLineNote If \c true, add a line note corresponding to this line
-/// directive. Only use this if the directive will not actually be
-/// visited by the preprocessor.
+/// \param IsModuleMap If \c true, add a line note corresponding to this line
+/// directive. (We need to do this because the directive will not be
+/// visited by the preprocessor.)
static SourceLocation ReadOriginalFileName(CompilerInstance &CI,
std::string &InputFile,
- bool AddLineNote = false) {
+ bool IsModuleMap = false) {
auto &SourceMgr = CI.getSourceManager();
auto MainFileID = SourceMgr.getMainFileID();
@@ -231,7 +231,7 @@ static SourceLocation ReadOriginalFileName(CompilerInstance &CI,
unsigned LineNo;
SourceLocation LineNoLoc = T.getLocation();
- if (AddLineNote) {
+ if (IsModuleMap) {
llvm::SmallString<16> Buffer;
if (Lexer::getSpelling(LineNoLoc, Buffer, SourceMgr, CI.getLangOpts())
.getAsInteger(10, LineNo))
@@ -250,10 +250,10 @@ static SourceLocation ReadOriginalFileName(CompilerInstance &CI,
return SourceLocation();
InputFile = Literal.GetString().str();
- if (AddLineNote)
+ if (IsModuleMap)
CI.getSourceManager().AddLineNote(
LineNoLoc, LineNo, SourceMgr.getLineTableFilenameID(InputFile), false,
- false, SrcMgr::C_User);
+ false, SrcMgr::C_User_ModuleMap);
return T.getLocation();
}
@@ -403,7 +403,7 @@ static bool loadModuleMapForModuleBuild(CompilerInstance &CI, bool IsSystem,
Offset = 0;
if (IsPreprocessed) {
SourceLocation EndOfLineMarker =
- ReadOriginalFileName(CI, PresumedModuleMapFile, /*AddLineNote*/true);
+ ReadOriginalFileName(CI, PresumedModuleMapFile, /*IsModuleMap*/ true);
if (EndOfLineMarker.isValid())
Offset = CI.getSourceManager().getDecomposedLoc(EndOfLineMarker).second;
}
@@ -547,21 +547,29 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI,
CI.getPreprocessorOpts() = AST->getPreprocessorOpts();
CI.getLangOpts() = AST->getLangOpts();
- // Preload all the module files loaded transitively by the AST unit.
- if (auto ASTReader = AST->getASTReader()) {
- auto &MM = ASTReader->getModuleManager();
- for (ModuleFile &MF : MM)
- if (&MF != &MM.getPrimaryModule())
- CI.getFrontendOpts().ModuleFiles.push_back(MF.FileName);
- }
- // FIXME: Preload module maps loaded by the AST unit.
-
// Set the shared objects, these are reset when we finish processing the
// file, otherwise the CompilerInstance will happily destroy them.
CI.setFileManager(&AST->getFileManager());
CI.createSourceManager(CI.getFileManager());
CI.getSourceManager().initializeForReplay(AST->getSourceManager());
+ // Preload all the module files loaded transitively by the AST unit. Also
+ // load all module map files that were parsed as part of building the AST
+ // unit.
+ if (auto ASTReader = AST->getASTReader()) {
+ auto &MM = ASTReader->getModuleManager();
+ auto &PrimaryModule = MM.getPrimaryModule();
+
+ for (ModuleFile &MF : MM)
+ if (&MF != &PrimaryModule)
+ CI.getFrontendOpts().ModuleFiles.push_back(MF.FileName);
+
+ ASTReader->visitTopLevelModuleMaps(PrimaryModule,
+ [&](const FileEntry *FE) {
+ CI.getFrontendOpts().ModuleMapFiles.push_back(FE->getName());
+ });
+ }
+
// Set up the input file for replay purposes.
auto Kind = AST->getInputKind();
if (Kind.getFormat() == InputKind::ModuleMap) {
diff --git a/lib/Lex/ModuleMap.cpp b/lib/Lex/ModuleMap.cpp
index 018d59e5e8..40f78ce25c 100644
--- a/lib/Lex/ModuleMap.cpp
+++ b/lib/Lex/ModuleMap.cpp
@@ -2710,7 +2710,8 @@ bool ModuleMap::parseModuleMapFile(const FileEntry *File, bool IsSystem,
// If the module map file wasn't already entered, do so now.
if (ID.isInvalid()) {
- auto FileCharacter = IsSystem ? SrcMgr::C_System : SrcMgr::C_User;
+ auto FileCharacter =
+ IsSystem ? SrcMgr::C_System_ModuleMap : SrcMgr::C_User_ModuleMap;
ID = SourceMgr.createFileID(File, ExternModuleLoc, FileCharacter);
}
diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp
index 7c9ff09125..fab7539aaa 100644
--- a/lib/Serialization/ASTReader.cpp
+++ b/lib/Serialization/ASTReader.cpp
@@ -1398,8 +1398,7 @@ bool ASTReader::ReadSLocEntry(int ID) {
}
const SrcMgr::ContentCache *ContentCache
- = SourceMgr.getOrCreateContentCache(File,
- /*isSystemFile=*/FileCharacter != SrcMgr::C_User);
+ = SourceMgr.getOrCreateContentCache(File, isSystem(FileCharacter));
if (OverriddenBuffer && !ContentCache->BufferOverridden &&
ContentCache->ContentsEntry == ContentCache->OrigEntry &&
!ContentCache->getRawBuffer()) {
@@ -1697,9 +1696,9 @@ HeaderFileInfoTrait::ReadData(internal_key_ref key, const unsigned char *d,
HeaderFileInfo HFI;
unsigned Flags = *d++;
// FIXME: Refactor with mergeHeaderFileInfo in HeaderSearch.cpp.
- HFI.isImport |= (Flags >> 4) & 0x01;
- HFI.isPragmaOnce |= (Flags >> 3) & 0x01;
- HFI.DirInfo = (Flags >> 1) & 0x03;
+ HFI.isImport |= (Flags >> 5) & 0x01;
+ HFI.isPragmaOnce |= (Flags >> 4) & 0x01;
+ HFI.DirInfo = (Flags >> 1) & 0x07;
HFI.IndexHeaderMapHeader = Flags & 0x01;
// FIXME: Find a better way to handle this. Maybe just store a
// "has been included" flag?
@@ -2029,6 +2028,7 @@ ASTReader::readInputFileInfo(ModuleFile &F, unsigned ID) {
R.StoredTime = static_cast<time_t>(Record[2]);
R.Overridden = static_cast<bool>(Record[3]);
R.Transient = static_cast<bool>(Record[4]);
+ R.TopLevelModuleMap = static_cast<bool>(Record[5]);
R.Filename = Blob;
ResolveImportedPath(F, R.Filename);
return R;
@@ -8909,6 +8909,19 @@ void ASTReader::visitInputFiles(serialization::ModuleFile &MF,
}
}
+void ASTReader::visitTopLevelModuleMaps(
+ serialization::ModuleFile &MF,
+ llvm::function_ref<void(const FileEntry *FE)> Visitor) {
+ unsigned NumInputs = MF.InputFilesLoaded.size();
+ for (unsigned I = 0; I < NumInputs; ++I) {
+ InputFileInfo IFI = readInputFileInfo(MF, I + 1);
+ if (IFI.TopLevelModuleMap)
+ // FIXME: This unnecessarily re-reads the InputFileInfo.
+ if (auto *FE = getInputFile(MF, I + 1).getFile())
+ Visitor(FE);
+ }
+}
+
std::string ASTReader::getOwningModuleNameForDiagnostic(const Decl *D) {
// If we know the owning module, use it.
if (Module *M = D->getImportedOwningModule())
diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp
index 1c0db14ced..c6129d326c 100644
--- a/lib/Serialization/ASTWriter.cpp
+++ b/lib/Serialization/ASTWriter.cpp
@@ -1692,6 +1692,7 @@ namespace {
bool IsSystemFile;
bool IsTransient;
bool BufferOverridden;
+ bool IsTopLevelModuleMap;
};
} // end anonymous namespace
@@ -1710,6 +1711,7 @@ void ASTWriter::WriteInputFiles(SourceManager &SourceMgr,
IFAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 32)); // Modification time
IFAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // Overridden
IFAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // Transient
+ IFAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // Module map
IFAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // File name
unsigned IFAbbrevCode = Stream.EmitAbbrev(std::move(IFAbbrev));
@@ -1724,7 +1726,8 @@ void ASTWriter::WriteInputFiles(SourceManager &SourceMgr,
// We only care about file entries that were not overridden.
if (!SLoc->isFile())
continue;
- const SrcMgr::ContentCache *Cache = SLoc->getFile().getContentCache();
+ const SrcMgr::FileInfo &File = SLoc->getFile();
+ const SrcMgr::ContentCache *Cache = File.getContentCache();
if (!Cache->OrigEntry)
continue;
@@ -1733,6 +1736,8 @@ void ASTWriter::WriteInputFiles(SourceManager &SourceMgr,
Entry.IsSystemFile = Cache->IsSystemFile;
Entry.IsTransient = Cache->IsTransient;
Entry.BufferOverridden = Cache->BufferOverridden;
+ Entry.IsTopLevelModuleMap = isModuleMap(File.getFileCharacteristic()) &&
+ File.getIncludeLoc().isInvalid();
if (Cache->IsSystemFile)
SortedFiles.push_back(Entry);
else
@@ -1763,7 +1768,8 @@ void ASTWriter::WriteInputFiles(SourceManager &SourceMgr,
(uint64_t)Entry.File->getSize(),
(uint64_t)getTimestampForOutput(Entry.File),
Entry.BufferOverridden,
- Entry.IsTransient};
+ Entry.IsTransient,
+ Entry.IsTopLevelModuleMap};
EmitRecordWithPath(IFAbbrevCode, Record, Entry.File->getName());
}
@@ -1798,7 +1804,7 @@ static unsigned CreateSLocFileAbbrev(llvm::BitstreamWriter &Stream) {
Abbrev->Add(BitCodeAbbrevOp(SM_SLOC_FILE_ENTRY));
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // Offset
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // Include location
- Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); // Characteristic
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // Characteristic
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // Line directives
// FileEntry fields.
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Input File ID
@@ -1817,7 +1823,7 @@ static unsigned CreateSLocBufferAbbrev(llvm::BitstreamWriter &Stream) {
Abbrev->Add(BitCodeAbbrevOp(SM_SLOC_BUFFER_ENTRY));
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // Offset
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // Include location
- Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); // Characteristic
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // Characteristic
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // Line directives
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Buffer name blob
return Stream.EmitAbbrev(std::move(Abbrev));
@@ -1925,8 +1931,8 @@ namespace {
endian::Writer<little> LE(Out);
uint64_t Start = Out.tell(); (void)Start;
- unsigned char Flags = (Data.HFI.isImport << 4)
- | (Data.HFI.isPragmaOnce << 3)
+ unsigned char Flags = (Data.HFI.isImport << 5)
+ | (Data.HFI.isPragmaOnce << 4)
| (Data.HFI.DirInfo << 1)
| Data.HFI.IndexHeaderMapHeader;
LE.write<uint8_t>(Flags);
diff --git a/test/Modules/Inputs/preprocess-decluse/a.h b/test/Modules/Inputs/preprocess-decluse/a.h
new file mode 100644
index 0000000000..17ab46c848
--- /dev/null
+++ b/test/Modules/Inputs/preprocess-decluse/a.h
@@ -0,0 +1 @@
+// a.h
diff --git a/test/Modules/Inputs/preprocess-decluse/a.modulemap b/test/Modules/Inputs/preprocess-decluse/a.modulemap
new file mode 100644
index 0000000000..47105fe363
--- /dev/null
+++ b/test/Modules/Inputs/preprocess-decluse/a.modulemap
@@ -0,0 +1 @@
+module A { textual header "a.h" }
diff --git a/test/Modules/Inputs/preprocess-decluse/b.h b/test/Modules/Inputs/preprocess-decluse/b.h
new file mode 100644
index 0000000000..2243de1baf
--- /dev/null
+++ b/test/Modules/Inputs/preprocess-decluse/b.h
@@ -0,0 +1 @@
+#include "a.h"
diff --git a/test/Modules/Inputs/preprocess-decluse/b.modulemap b/test/Modules/Inputs/preprocess-decluse/b.modulemap
new file mode 100644
index 0000000000..bd0bdf0e2e
--- /dev/null
+++ b/test/Modules/Inputs/preprocess-decluse/b.modulemap
@@ -0,0 +1 @@
+module B { header "b.h" use A }
diff --git a/test/Modules/Inputs/preprocess-decluse/main.modulemap b/test/Modules/Inputs/preprocess-decluse/main.modulemap
new file mode 100644
index 0000000000..0392c846ce
--- /dev/null
+++ b/test/Modules/Inputs/preprocess-decluse/main.modulemap
@@ -0,0 +1 @@
+module Main { use B }
diff --git a/test/Modules/preprocess-decluse.cpp b/test/Modules/preprocess-decluse.cpp
new file mode 100644
index 0000000000..5c87e0ddda
--- /dev/null
+++ b/test/Modules/preprocess-decluse.cpp
@@ -0,0 +1,18 @@
+// RUN: rm -rf %t
+// RUN: %clang_cc1 -fmodules -fmodules-strict-decluse -I%S/Inputs/preprocess-decluse \
+// RUN: -fmodule-name=B -emit-module -o %t/b.pcm \
+// RUN: -fmodule-map-file=%S/Inputs/preprocess-decluse/a.modulemap \
+// RUN: -x c++-module-map %S/Inputs/preprocess-decluse/b.modulemap
+// RUN: %clang_cc1 -fmodules -fmodules-strict-decluse -I%S/Inputs/preprocess-decluse \
+// RUN: -fmodule-map-file=%S/Inputs/preprocess-decluse/main.modulemap \
+// RUN: -fmodule-file=%t/b.pcm -fmodule-name=Main %s -verify
+// RUN: %clang_cc1 -fmodules -fmodules-strict-decluse -I%S/Inputs/preprocess-decluse \
+// RUN: -fmodule-map-file=%S/Inputs/preprocess-decluse/main.modulemap \
+// RUN: -fmodule-file=%t/b.pcm -fmodule-name=Main %s \
+// RUN: -E -frewrite-imports -o %t/rewrite.ii
+// RUN: %clang_cc1 -fmodules -fmodules-strict-decluse -I%S/Inputs/preprocess-decluse \
+// RUN: -fmodule-map-file=%S/Inputs/preprocess-decluse/main.modulemap \
+// RUN: -fmodule-name=Main %t/rewrite.ii -verify
+
+// expected-no-diagnostics
+#include "b.h"