summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Langmuir <blangmuir@apple.com>2014-05-05 21:44:13 +0000
committerBen Langmuir <blangmuir@apple.com>2014-05-05 21:44:13 +0000
commit57973be024e1b0cb252ae43ddc05d4ce4203b1f1 (patch)
treeccb36e76278459190018d25fc0b8fd7ff89dd9c5
parent6197f101bd0d753866f39268fb1f63e03eadce6f (diff)
downloadclang-57973be024e1b0cb252ae43ddc05d4ce4203b1f1.tar.gz
clang-57973be024e1b0cb252ae43ddc05d4ce4203b1f1.tar.bz2
clang-57973be024e1b0cb252ae43ddc05d4ce4203b1f1.tar.xz
Add -Wnon-modular-include* options
Warn on non-modular includes in various contexts. -Wnon-modular-include -Wnon-modular-include-in-module -Wnon-modular-include-in-framework-module Where each group is a subgroup of those above it. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@208004 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/clang/Basic/DiagnosticGroups.td6
-rw-r--r--include/clang/Basic/DiagnosticLexKinds.td8
-rw-r--r--include/clang/Lex/ModuleMap.h7
-rw-r--r--lib/Lex/ModuleMap.cpp84
-rw-r--r--lib/Lex/PPDirectives.cpp18
-rw-r--r--test/Modules/Inputs/require-modular-includes/A.framework/Headers/A.h1
-rw-r--r--test/Modules/Inputs/require-modular-includes/AnotherModule.h1
-rw-r--r--test/Modules/Inputs/require-modular-includes/AnotherModuleExcluded.h1
-rw-r--r--test/Modules/Inputs/require-modular-includes/B.framework/Headers/B.h1
-rw-r--r--test/Modules/Inputs/require-modular-includes/C.h1
-rw-r--r--test/Modules/Inputs/require-modular-includes/ExcludedFromAnotherModule.framework/Headers/ExcludedFromAnotherModule.h1
-rw-r--r--test/Modules/Inputs/require-modular-includes/FromAnotherModule.framework/Headers/FromAnotherModule.h1
-rw-r--r--test/Modules/Inputs/require-modular-includes/FromImportedModuleFail.framework/Headers/FromImportedModuleFail.h2
-rw-r--r--test/Modules/Inputs/require-modular-includes/FromImportedModuleOK.framework/Headers/FromImportedModuleOK.h1
-rw-r--r--test/Modules/Inputs/require-modular-includes/FromImportedModuleOK.framework/Headers/FromImportedModuleOK2.h1
-rw-r--r--test/Modules/Inputs/require-modular-includes/FromImportedModuleOK.framework/Modules/module.modulemap4
-rw-r--r--test/Modules/Inputs/require-modular-includes/FromImportedSubModule.framework/Headers/Header.h2
-rw-r--r--test/Modules/Inputs/require-modular-includes/FromImportedSubModule.framework/Modules/module.modulemap5
-rw-r--r--test/Modules/Inputs/require-modular-includes/FromNonModularSubframework.framework/Frameworks/Subframework.framework/Headers/Subframework.h0
-rw-r--r--test/Modules/Inputs/require-modular-includes/FromNonModularSubframework.framework/Headers/FromNonModularSubframework.h1
-rw-r--r--test/Modules/Inputs/require-modular-includes/FromNonModularSubframework.framework/Modules/module.modulemap3
-rw-r--r--test/Modules/Inputs/require-modular-includes/FromSubframework.framework/Frameworks/Subframework.framework/Headers/Subframework.h1
-rw-r--r--test/Modules/Inputs/require-modular-includes/FromSubframework.framework/Headers/FromSubframework.h1
-rw-r--r--test/Modules/Inputs/require-modular-includes/FromSubframework.framework/Modules/module.modulemap7
-rw-r--r--test/Modules/Inputs/require-modular-includes/FromUmbrella.framework/Headers/FromUmbrella.h2
-rw-r--r--test/Modules/Inputs/require-modular-includes/IncludeExcluded.framework/Headers/Excluded.h1
-rw-r--r--test/Modules/Inputs/require-modular-includes/IncludeExcluded.framework/Headers/IncludeExcluded.h1
-rw-r--r--test/Modules/Inputs/require-modular-includes/IncludeExcluded.framework/Modules/module.modulemap4
-rw-r--r--test/Modules/Inputs/require-modular-includes/NotFramework.h2
-rw-r--r--test/Modules/Inputs/require-modular-includes/NotInModule.h1
-rw-r--r--test/Modules/Inputs/require-modular-includes/module.modulemap12
-rw-r--r--test/Modules/Inputs/require-modular-includes/umbrella/bar/bar.h1
-rw-r--r--test/Modules/Inputs/require-modular-includes/umbrella/foo.h1
-rw-r--r--test/Modules/require-modular-includes.m95
34 files changed, 240 insertions, 38 deletions
diff --git a/include/clang/Basic/DiagnosticGroups.td b/include/clang/Basic/DiagnosticGroups.td
index b1665f5373..a2cc7406ab 100644
--- a/include/clang/Basic/DiagnosticGroups.td
+++ b/include/clang/Basic/DiagnosticGroups.td
@@ -186,6 +186,12 @@ def IncompatiblePointerTypes
[IncompatiblePointerTypesDiscardsQualifiers]>;
def IncompleteUmbrella : DiagGroup<"incomplete-umbrella">;
def IncompleteModule : DiagGroup<"incomplete-module", [IncompleteUmbrella]>;
+def NonModularIncludeInFrameworkModule
+ : DiagGroup<"non-modular-include-in-framework-module">;
+def NonModularIncludeInModule : DiagGroup<"non-modular-include-in-module",
+ [NonModularIncludeInFrameworkModule]>;
+def NonModularInclude : DiagGroup<"non-modular-include",
+ [NonModularIncludeInModule]>;
def InvalidNoreturn : DiagGroup<"invalid-noreturn">;
def InvalidSourceEncoding : DiagGroup<"invalid-source-encoding">;
def KNRPromotedParameter : DiagGroup<"knr-promoted-parameter">;
diff --git a/include/clang/Basic/DiagnosticLexKinds.td b/include/clang/Basic/DiagnosticLexKinds.td
index aef3c585a9..abd386fc36 100644
--- a/include/clang/Basic/DiagnosticLexKinds.td
+++ b/include/clang/Basic/DiagnosticLexKinds.td
@@ -620,6 +620,14 @@ def error_use_of_private_header_outside_module : Error<
"use of private header from outside its module: '%0'">;
def error_undeclared_use_of_module : Error<
"module %0 does not depend on a module exporting '%1'">;
+def warn_non_modular_include_in_framework_module : Warning<
+ "include of non-modular header inside framework module '%0'">,
+ InGroup<NonModularIncludeInFrameworkModule>, DefaultIgnore;
+def warn_non_modular_include_in_module : Warning<
+ "include of non-modular header inside module '%0'">,
+ InGroup<NonModularIncludeInModule>, DefaultIgnore;
+def warn_non_modular_include : Warning<
+ "include of non-modular header">, InGroup<NonModularInclude>, DefaultIgnore;
def warn_header_guard : Warning<
"%0 is used as a header guard here, followed by #define of a different macro">,
diff --git a/include/clang/Lex/ModuleMap.h b/include/clang/Lex/ModuleMap.h
index dbd9fd1c0c..74c83b26ea 100644
--- a/include/clang/Lex/ModuleMap.h
+++ b/include/clang/Lex/ModuleMap.h
@@ -195,6 +195,13 @@ private:
KnownHeader findHeaderInUmbrellaDirs(const FileEntry *File,
SmallVectorImpl<const DirectoryEntry *> &IntermediateDirs);
+ /// \brief A convenience method to determine if \p File is (possibly nested)
+ /// in an umbrella directory.
+ bool isHeaderInUmbrellaDirs(const FileEntry *File) {
+ SmallVector<const DirectoryEntry *, 2> IntermediateDirs;
+ return static_cast<bool>(findHeaderInUmbrellaDirs(File, IntermediateDirs));
+ }
+
public:
/// \brief Construct a new module map.
///
diff --git a/lib/Lex/ModuleMap.cpp b/lib/Lex/ModuleMap.cpp
index 39c1121011..f89d91d056 100644
--- a/lib/Lex/ModuleMap.cpp
+++ b/lib/Lex/ModuleMap.cpp
@@ -230,56 +230,57 @@ static bool violatesPrivateInclude(Module *RequestingModule,
RequestedModule->getTopLevelModule() != RequestingModule;
}
+static Module *getTopLevelOrNull(Module *M) {
+ return M ? M->getTopLevelModule() : nullptr;
+}
+
void ModuleMap::diagnoseHeaderInclusion(Module *RequestingModule,
SourceLocation FilenameLoc,
StringRef Filename,
const FileEntry *File) {
// No errors for indirect modules. This may be a bit of a problem for modules
// with no source files.
- if (RequestingModule != SourceModule)
+ if (getTopLevelOrNull(RequestingModule) != getTopLevelOrNull(SourceModule))
return;
if (RequestingModule)
resolveUses(RequestingModule, /*Complain=*/false);
- HeadersMap::iterator Known = findKnownHeader(File);
- if (Known == Headers.end()) {
- if (LangOpts.ModulesStrictDeclUse)
- Diags.Report(FilenameLoc, diag::error_undeclared_use_of_module)
- << RequestingModule->getFullModuleName() << Filename;
- return;
- }
-
+ bool Excluded = false;
Module *Private = NULL;
Module *NotUsed = NULL;
- for (SmallVectorImpl<KnownHeader>::iterator I = Known->second.begin(),
- E = Known->second.end();
- I != E; ++I) {
- // Excluded headers don't really belong to a module.
- if (I->getRole() == ModuleMap::ExcludedHeader)
- continue;
- // If 'File' is part of 'RequestingModule' we can definitely include it.
- if (I->getModule() == RequestingModule)
- return;
+ HeadersMap::iterator Known = findKnownHeader(File);
+ if (Known != Headers.end()) {
+ for (const KnownHeader &Header : Known->second) {
+ // Excluded headers don't really belong to a module.
+ if (Header.getRole() == ModuleMap::ExcludedHeader) {
+ Excluded = true;
+ continue;
+ }
- // Remember private headers for later printing of a diagnostic.
- if (violatesPrivateInclude(RequestingModule, File, I->getRole(),
- I->getModule())) {
- Private = I->getModule();
- continue;
- }
+ // If 'File' is part of 'RequestingModule' we can definitely include it.
+ if (Header.getModule() == RequestingModule)
+ return;
- // If uses need to be specified explicitly, we are only allowed to return
- // modules that are explicitly used by the requesting module.
- if (RequestingModule && LangOpts.ModulesDeclUse &&
- !directlyUses(RequestingModule, I->getModule())) {
- NotUsed = I->getModule();
- continue;
- }
+ // Remember private headers for later printing of a diagnostic.
+ if (violatesPrivateInclude(RequestingModule, File, Header.getRole(),
+ Header.getModule())) {
+ Private = Header.getModule();
+ continue;
+ }
- // We have found a module that we can happily use.
- return;
+ // If uses need to be specified explicitly, we are only allowed to return
+ // modules that are explicitly used by the requesting module.
+ if (RequestingModule && LangOpts.ModulesDeclUse &&
+ !directlyUses(RequestingModule, Header.getModule())) {
+ NotUsed = Header.getModule();
+ continue;
+ }
+
+ // We have found a module that we can happily use.
+ return;
+ }
}
// We have found a header, but it is private.
@@ -296,7 +297,22 @@ void ModuleMap::diagnoseHeaderInclusion(Module *RequestingModule,
return;
}
- // Headers for which we have not found a module are fine to include.
+ if (Excluded || isHeaderInUmbrellaDirs(File))
+ return;
+
+ // At this point, only non-modular includes remain.
+
+ if (LangOpts.ModulesStrictDeclUse) {
+ Diags.Report(FilenameLoc, diag::error_undeclared_use_of_module)
+ << RequestingModule->getFullModuleName() << Filename;
+ } else if (RequestingModule) {
+ diag::kind DiagID = RequestingModule->getTopLevelModule()->IsFramework ?
+ diag::warn_non_modular_include_in_framework_module :
+ diag::warn_non_modular_include_in_module;
+ Diags.Report(FilenameLoc, DiagID) << RequestingModule->getFullModuleName();
+ } else {
+ Diags.Report(FilenameLoc, diag::warn_non_modular_include);
+ }
}
ModuleMap::KnownHeader
diff --git a/lib/Lex/PPDirectives.cpp b/lib/Lex/PPDirectives.cpp
index 57dc49536a..b1675bc043 100644
--- a/lib/Lex/PPDirectives.cpp
+++ b/lib/Lex/PPDirectives.cpp
@@ -611,22 +611,32 @@ const FileEntry *Preprocessor::LookupFile(
// to one of the headers on the #include stack. Walk the list of the current
// headers on the #include stack and pass them to HeaderInfo.
if (IsFileLexer()) {
- if ((CurFileEnt = SourceMgr.getFileEntryForID(CurPPLexer->getFileID())))
+ if ((CurFileEnt = SourceMgr.getFileEntryForID(CurPPLexer->getFileID()))) {
if ((FE = HeaderInfo.LookupSubframeworkHeader(Filename, CurFileEnt,
SearchPath, RelativePath,
- SuggestedModule)))
+ SuggestedModule))) {
+ if (SuggestedModule && !LangOpts.AsmPreprocessor)
+ HeaderInfo.getModuleMap().diagnoseHeaderInclusion(
+ getModuleForLocation(FilenameLoc), FilenameLoc, Filename, FE);
return FE;
+ }
+ }
}
for (unsigned i = 0, e = IncludeMacroStack.size(); i != e; ++i) {
IncludeStackInfo &ISEntry = IncludeMacroStack[e-i-1];
if (IsFileLexer(ISEntry)) {
if ((CurFileEnt =
- SourceMgr.getFileEntryForID(ISEntry.ThePPLexer->getFileID())))
+ SourceMgr.getFileEntryForID(ISEntry.ThePPLexer->getFileID()))) {
if ((FE = HeaderInfo.LookupSubframeworkHeader(
Filename, CurFileEnt, SearchPath, RelativePath,
- SuggestedModule)))
+ SuggestedModule))) {
+ if (SuggestedModule && !LangOpts.AsmPreprocessor)
+ HeaderInfo.getModuleMap().diagnoseHeaderInclusion(
+ getModuleForLocation(FilenameLoc), FilenameLoc, Filename, FE);
return FE;
+ }
+ }
}
}
diff --git a/test/Modules/Inputs/require-modular-includes/A.framework/Headers/A.h b/test/Modules/Inputs/require-modular-includes/A.framework/Headers/A.h
new file mode 100644
index 0000000000..fad91bdbfd
--- /dev/null
+++ b/test/Modules/Inputs/require-modular-includes/A.framework/Headers/A.h
@@ -0,0 +1 @@
+#include "B/B.h"
diff --git a/test/Modules/Inputs/require-modular-includes/AnotherModule.h b/test/Modules/Inputs/require-modular-includes/AnotherModule.h
new file mode 100644
index 0000000000..96913aa22a
--- /dev/null
+++ b/test/Modules/Inputs/require-modular-includes/AnotherModule.h
@@ -0,0 +1 @@
+// AnotherModule.h
diff --git a/test/Modules/Inputs/require-modular-includes/AnotherModuleExcluded.h b/test/Modules/Inputs/require-modular-includes/AnotherModuleExcluded.h
new file mode 100644
index 0000000000..b539dd9ba9
--- /dev/null
+++ b/test/Modules/Inputs/require-modular-includes/AnotherModuleExcluded.h
@@ -0,0 +1 @@
+// AnotherModuleExcluded.h
diff --git a/test/Modules/Inputs/require-modular-includes/B.framework/Headers/B.h b/test/Modules/Inputs/require-modular-includes/B.framework/Headers/B.h
new file mode 100644
index 0000000000..77d6ff1e5f
--- /dev/null
+++ b/test/Modules/Inputs/require-modular-includes/B.framework/Headers/B.h
@@ -0,0 +1 @@
+#include "C.h"
diff --git a/test/Modules/Inputs/require-modular-includes/C.h b/test/Modules/Inputs/require-modular-includes/C.h
new file mode 100644
index 0000000000..a0121d46b4
--- /dev/null
+++ b/test/Modules/Inputs/require-modular-includes/C.h
@@ -0,0 +1 @@
+// C.h
diff --git a/test/Modules/Inputs/require-modular-includes/ExcludedFromAnotherModule.framework/Headers/ExcludedFromAnotherModule.h b/test/Modules/Inputs/require-modular-includes/ExcludedFromAnotherModule.framework/Headers/ExcludedFromAnotherModule.h
new file mode 100644
index 0000000000..0c0ce7bb56
--- /dev/null
+++ b/test/Modules/Inputs/require-modular-includes/ExcludedFromAnotherModule.framework/Headers/ExcludedFromAnotherModule.h
@@ -0,0 +1 @@
+#include "AnotherModuleExcluded.h"
diff --git a/test/Modules/Inputs/require-modular-includes/FromAnotherModule.framework/Headers/FromAnotherModule.h b/test/Modules/Inputs/require-modular-includes/FromAnotherModule.framework/Headers/FromAnotherModule.h
new file mode 100644
index 0000000000..1fe5c08ace
--- /dev/null
+++ b/test/Modules/Inputs/require-modular-includes/FromAnotherModule.framework/Headers/FromAnotherModule.h
@@ -0,0 +1 @@
+#include "AnotherModule.h"
diff --git a/test/Modules/Inputs/require-modular-includes/FromImportedModuleFail.framework/Headers/FromImportedModuleFail.h b/test/Modules/Inputs/require-modular-includes/FromImportedModuleFail.framework/Headers/FromImportedModuleFail.h
new file mode 100644
index 0000000000..a80bcc6945
--- /dev/null
+++ b/test/Modules/Inputs/require-modular-includes/FromImportedModuleFail.framework/Headers/FromImportedModuleFail.h
@@ -0,0 +1,2 @@
+// FromImportedModuleFail.h
+#include "NotInModule.h"
diff --git a/test/Modules/Inputs/require-modular-includes/FromImportedModuleOK.framework/Headers/FromImportedModuleOK.h b/test/Modules/Inputs/require-modular-includes/FromImportedModuleOK.framework/Headers/FromImportedModuleOK.h
new file mode 100644
index 0000000000..3b2056c454
--- /dev/null
+++ b/test/Modules/Inputs/require-modular-includes/FromImportedModuleOK.framework/Headers/FromImportedModuleOK.h
@@ -0,0 +1 @@
+#include "FromImportedModuleOK2.h"
diff --git a/test/Modules/Inputs/require-modular-includes/FromImportedModuleOK.framework/Headers/FromImportedModuleOK2.h b/test/Modules/Inputs/require-modular-includes/FromImportedModuleOK.framework/Headers/FromImportedModuleOK2.h
new file mode 100644
index 0000000000..775fd6e4d8
--- /dev/null
+++ b/test/Modules/Inputs/require-modular-includes/FromImportedModuleOK.framework/Headers/FromImportedModuleOK2.h
@@ -0,0 +1 @@
+// FromImportedModuleOK2.h
diff --git a/test/Modules/Inputs/require-modular-includes/FromImportedModuleOK.framework/Modules/module.modulemap b/test/Modules/Inputs/require-modular-includes/FromImportedModuleOK.framework/Modules/module.modulemap
new file mode 100644
index 0000000000..26381110d2
--- /dev/null
+++ b/test/Modules/Inputs/require-modular-includes/FromImportedModuleOK.framework/Modules/module.modulemap
@@ -0,0 +1,4 @@
+framework module FromImportedModuleOK {
+ header "FromImportedModuleOK.h"
+ header "FromImportedModuleOK2.h"
+}
diff --git a/test/Modules/Inputs/require-modular-includes/FromImportedSubModule.framework/Headers/Header.h b/test/Modules/Inputs/require-modular-includes/FromImportedSubModule.framework/Headers/Header.h
new file mode 100644
index 0000000000..613cd9d681
--- /dev/null
+++ b/test/Modules/Inputs/require-modular-includes/FromImportedSubModule.framework/Headers/Header.h
@@ -0,0 +1,2 @@
+// Header.h
+#include "NotInModule.h"
diff --git a/test/Modules/Inputs/require-modular-includes/FromImportedSubModule.framework/Modules/module.modulemap b/test/Modules/Inputs/require-modular-includes/FromImportedSubModule.framework/Modules/module.modulemap
new file mode 100644
index 0000000000..c07efec72b
--- /dev/null
+++ b/test/Modules/Inputs/require-modular-includes/FromImportedSubModule.framework/Modules/module.modulemap
@@ -0,0 +1,5 @@
+framework module FromImportedSubModule {
+ module Sub {
+ header "Header.h"
+ }
+}
diff --git a/test/Modules/Inputs/require-modular-includes/FromNonModularSubframework.framework/Frameworks/Subframework.framework/Headers/Subframework.h b/test/Modules/Inputs/require-modular-includes/FromNonModularSubframework.framework/Frameworks/Subframework.framework/Headers/Subframework.h
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/Modules/Inputs/require-modular-includes/FromNonModularSubframework.framework/Frameworks/Subframework.framework/Headers/Subframework.h
diff --git a/test/Modules/Inputs/require-modular-includes/FromNonModularSubframework.framework/Headers/FromNonModularSubframework.h b/test/Modules/Inputs/require-modular-includes/FromNonModularSubframework.framework/Headers/FromNonModularSubframework.h
new file mode 100644
index 0000000000..4680be4a37
--- /dev/null
+++ b/test/Modules/Inputs/require-modular-includes/FromNonModularSubframework.framework/Headers/FromNonModularSubframework.h
@@ -0,0 +1 @@
+#include "Subframework/Subframework.h"
diff --git a/test/Modules/Inputs/require-modular-includes/FromNonModularSubframework.framework/Modules/module.modulemap b/test/Modules/Inputs/require-modular-includes/FromNonModularSubframework.framework/Modules/module.modulemap
new file mode 100644
index 0000000000..c308ea429b
--- /dev/null
+++ b/test/Modules/Inputs/require-modular-includes/FromNonModularSubframework.framework/Modules/module.modulemap
@@ -0,0 +1,3 @@
+framework module FromNonModularSubframework {
+ header "FromNonModularSubframework.h"
+}
diff --git a/test/Modules/Inputs/require-modular-includes/FromSubframework.framework/Frameworks/Subframework.framework/Headers/Subframework.h b/test/Modules/Inputs/require-modular-includes/FromSubframework.framework/Frameworks/Subframework.framework/Headers/Subframework.h
new file mode 100644
index 0000000000..c08fac0b55
--- /dev/null
+++ b/test/Modules/Inputs/require-modular-includes/FromSubframework.framework/Frameworks/Subframework.framework/Headers/Subframework.h
@@ -0,0 +1 @@
+// Subframework.h
diff --git a/test/Modules/Inputs/require-modular-includes/FromSubframework.framework/Headers/FromSubframework.h b/test/Modules/Inputs/require-modular-includes/FromSubframework.framework/Headers/FromSubframework.h
new file mode 100644
index 0000000000..4680be4a37
--- /dev/null
+++ b/test/Modules/Inputs/require-modular-includes/FromSubframework.framework/Headers/FromSubframework.h
@@ -0,0 +1 @@
+#include "Subframework/Subframework.h"
diff --git a/test/Modules/Inputs/require-modular-includes/FromSubframework.framework/Modules/module.modulemap b/test/Modules/Inputs/require-modular-includes/FromSubframework.framework/Modules/module.modulemap
new file mode 100644
index 0000000000..250f05ace0
--- /dev/null
+++ b/test/Modules/Inputs/require-modular-includes/FromSubframework.framework/Modules/module.modulemap
@@ -0,0 +1,7 @@
+framework module FromSubframework {
+ umbrella header "FromSubframework.h"
+
+ framework module Subframework {
+ umbrella header "Subframework.h"
+ }
+}
diff --git a/test/Modules/Inputs/require-modular-includes/FromUmbrella.framework/Headers/FromUmbrella.h b/test/Modules/Inputs/require-modular-includes/FromUmbrella.framework/Headers/FromUmbrella.h
new file mode 100644
index 0000000000..f6f891f53f
--- /dev/null
+++ b/test/Modules/Inputs/require-modular-includes/FromUmbrella.framework/Headers/FromUmbrella.h
@@ -0,0 +1,2 @@
+#include "umbrella/foo.h"
+#include "umbrella/bar/bar.h"
diff --git a/test/Modules/Inputs/require-modular-includes/IncludeExcluded.framework/Headers/Excluded.h b/test/Modules/Inputs/require-modular-includes/IncludeExcluded.framework/Headers/Excluded.h
new file mode 100644
index 0000000000..f330e6c0ec
--- /dev/null
+++ b/test/Modules/Inputs/require-modular-includes/IncludeExcluded.framework/Headers/Excluded.h
@@ -0,0 +1 @@
+// Excluded.h
diff --git a/test/Modules/Inputs/require-modular-includes/IncludeExcluded.framework/Headers/IncludeExcluded.h b/test/Modules/Inputs/require-modular-includes/IncludeExcluded.framework/Headers/IncludeExcluded.h
new file mode 100644
index 0000000000..f2ffdc327e
--- /dev/null
+++ b/test/Modules/Inputs/require-modular-includes/IncludeExcluded.framework/Headers/IncludeExcluded.h
@@ -0,0 +1 @@
+#include "Excluded.h"
diff --git a/test/Modules/Inputs/require-modular-includes/IncludeExcluded.framework/Modules/module.modulemap b/test/Modules/Inputs/require-modular-includes/IncludeExcluded.framework/Modules/module.modulemap
new file mode 100644
index 0000000000..a1753539bd
--- /dev/null
+++ b/test/Modules/Inputs/require-modular-includes/IncludeExcluded.framework/Modules/module.modulemap
@@ -0,0 +1,4 @@
+framework module IncludeExcluded {
+ header "IncludeExcluded.h"
+ exclude header "Excluded.h"
+}
diff --git a/test/Modules/Inputs/require-modular-includes/NotFramework.h b/test/Modules/Inputs/require-modular-includes/NotFramework.h
new file mode 100644
index 0000000000..3afd8fc64a
--- /dev/null
+++ b/test/Modules/Inputs/require-modular-includes/NotFramework.h
@@ -0,0 +1,2 @@
+// NotFramework.h
+#import "NotInModule.h"
diff --git a/test/Modules/Inputs/require-modular-includes/NotInModule.h b/test/Modules/Inputs/require-modular-includes/NotInModule.h
new file mode 100644
index 0000000000..c2a626cab9
--- /dev/null
+++ b/test/Modules/Inputs/require-modular-includes/NotInModule.h
@@ -0,0 +1 @@
+// NotInModule.h
diff --git a/test/Modules/Inputs/require-modular-includes/module.modulemap b/test/Modules/Inputs/require-modular-includes/module.modulemap
new file mode 100644
index 0000000000..0ac4d5b703
--- /dev/null
+++ b/test/Modules/Inputs/require-modular-includes/module.modulemap
@@ -0,0 +1,12 @@
+module AnotherModule {
+ header "AnotherModule.h"
+ exclude header "AnotherModuleExcluded.h"
+}
+module Umbrella {
+ umbrella "umbrella"
+}
+module NotFramework {
+ header "NotFramework.h"
+}
+
+framework module * { }
diff --git a/test/Modules/Inputs/require-modular-includes/umbrella/bar/bar.h b/test/Modules/Inputs/require-modular-includes/umbrella/bar/bar.h
new file mode 100644
index 0000000000..410aba2639
--- /dev/null
+++ b/test/Modules/Inputs/require-modular-includes/umbrella/bar/bar.h
@@ -0,0 +1 @@
+// bar.h
diff --git a/test/Modules/Inputs/require-modular-includes/umbrella/foo.h b/test/Modules/Inputs/require-modular-includes/umbrella/foo.h
new file mode 100644
index 0000000000..249664863b
--- /dev/null
+++ b/test/Modules/Inputs/require-modular-includes/umbrella/foo.h
@@ -0,0 +1 @@
+// foo.h
diff --git a/test/Modules/require-modular-includes.m b/test/Modules/require-modular-includes.m
new file mode 100644
index 0000000000..55674319ff
--- /dev/null
+++ b/test/Modules/require-modular-includes.m
@@ -0,0 +1,95 @@
+// RUN: rm -rf %t
+// REQUIRES: shell
+
+// Including a header from the imported module
+// RUN: echo '@import FromImportedModuleOK;' | \
+// RUN: %clang_cc1 -Wnon-modular-include-in-framework-module -fmodules \
+// RUN: -fmodules-cache-path=%t -F %S/Inputs/require-modular-includes \
+// RUN: -Werror -fsyntax-only -x objective-c -
+
+// Including a non-modular header
+// RUN: echo '@import FromImportedModuleFail;' | \
+// RUN: %clang_cc1 -Wnon-modular-include-in-framework-module -fmodules \
+// RUN: -fmodules-cache-path=%t -F %S/Inputs/require-modular-includes \
+// RUN: -I %S/Inputs/require-modular-includes \
+// RUN: -fsyntax-only -x objective-c - 2>&1 | FileCheck %s
+
+// Including a header from a subframework
+// RUN: echo '@import FromSubframework;' | \
+// RUN: %clang_cc1 -Wnon-modular-include-in-framework-module -fmodules \
+// RUN: -fmodules-cache-path=%t -F %S/Inputs/require-modular-includes \
+// RUN: -Werror -fsyntax-only -x objective-c -
+
+// Including a header from a subframework (fail)
+// RUN: echo '@import FromNonModularSubframework;' | \
+// RUN: %clang_cc1 -Wnon-modular-include-in-framework-module -fmodules \
+// RUN: -fmodules-cache-path=%t -F %S/Inputs/require-modular-includes \
+// RUN: -I %S/Inputs/require-modular-includes \
+// RUN: -fsyntax-only -x objective-c - 2>&1 | FileCheck %s
+
+// Including a non-modular header from a submodule
+// RUN: echo '@import FromImportedSubModule;' | \
+// RUN: %clang_cc1 -Wnon-modular-include-in-framework-module -fmodules \
+// RUN: -fmodules-cache-path=%t -F %S/Inputs/require-modular-includes \
+// RUN: -I %S/Inputs/require-modular-includes \
+// RUN: -fsyntax-only -x objective-c - 2>&1 | FileCheck %s
+
+// Including a non-modular header (directly) with -fmodule-name set
+// RUN: echo '#include "NotInModule.h"' | \
+// RUN: %clang_cc1 -Wnon-modular-include-in-framework-module -fmodules \
+// RUN: -fmodules-cache-path=%t -I %S/Inputs/require-modular-includes \
+// RUN: -Werror -fmodule-name=A -fsyntax-only -x objective-c -
+
+// Including a non-modular header (directly) with -Wnon-modular-include
+// RUN: echo '#include "NotInModule.h"' | \
+// RUN: %clang_cc1 -Wnon-modular-include -fmodules \
+// RUN: -fmodules-cache-path=%t -I %S/Inputs/require-modular-includes \
+// RUN: -fmodule-name=A -fsyntax-only -x objective-c - 2>&1 | FileCheck %s
+
+// Including an excluded header
+// RUN: echo '@import IncludeExcluded;' | \
+// RUN: %clang_cc1 -Wnon-modular-include-in-framework-module -fmodules \
+// RUN: -fmodules-cache-path=%t -F %S/Inputs/require-modular-includes \
+// RUN: -Werror -fsyntax-only -x objective-c -
+
+// Including a header from another module
+// RUN: echo '@import FromAnotherModule;' | \
+// RUN: %clang_cc1 -Wnon-modular-include-in-framework-module -fmodules \
+// RUN: -fmodules-cache-path=%t -F %S/Inputs/require-modular-includes \
+// RUN: -I %S/Inputs/require-modular-includes \
+// RUN: -Werror -fsyntax-only -x objective-c -
+
+// Including an excluded header from another module
+// RUN: echo '@import ExcludedFromAnotherModule;' | \
+// RUN: %clang_cc1 -Wnon-modular-include-in-framework-module -fmodules \
+// RUN: -fmodules-cache-path=%t -F %S/Inputs/require-modular-includes \
+// RUN: -I %S/Inputs/require-modular-includes \
+// RUN: -Werror -fsyntax-only -x objective-c -
+
+// Including a header from an umbrella directory
+// RUN: echo '@import FromUmbrella;' | \
+// RUN: %clang_cc1 -Wnon-modular-include-in-framework-module -fmodules \
+// RUN: -fmodules-cache-path=%t -F %S/Inputs/require-modular-includes \
+// RUN: -I %S/Inputs/require-modular-includes \
+// RUN: -Werror -fsyntax-only -x objective-c -
+
+// A includes B includes non-modular C
+// RUN: echo '@import A;' | \
+// RUN: %clang_cc1 -Wnon-modular-include-in-framework-module -fmodules \
+// RUN: -fmodules-cache-path=%t -F %S/Inputs/require-modular-includes \
+// RUN: -I %S/Inputs/require-modular-includes \
+// RUN: -fsyntax-only -x objective-c - 2>&1 | FileCheck %s
+
+// Non-framework module (pass)
+// RUN: echo '@import NotFramework;' | \
+// RUN: %clang_cc1 -Wnon-modular-include-in-framework-module -fmodules \
+// RUN: -fmodules-cache-path=%t -I %S/Inputs/require-modular-includes \
+// RUN: -Werror -fsyntax-only -x objective-c -
+
+// Non-framework module (fail)
+// RUN: echo '@import NotFramework;' | \
+// RUN: not %clang_cc1 -Werror=non-modular-include -fmodules \
+// RUN: -fmodules-cache-path=%t -I %S/Inputs/require-modular-includes \
+// RUN: -fsyntax-only -x objective-c - 2>&1 | FileCheck %s
+
+// CHECK: include of non-modular header