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.

llvm-svn: 208004
This commit is contained in:
Ben Langmuir
2014-05-05 21:44:13 +00:00
parent 6beaa8adb8
commit 71e1a64f91
34 changed files with 241 additions and 39 deletions

View File

@@ -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">;

View File

@@ -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">,

View File

@@ -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.
///

View File

@@ -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)
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;
}
// If 'File' is part of 'RequestingModule' we can definitely include it.
if (Header.getModule() == RequestingModule)
return;
// Remember private headers for later printing of a diagnostic.
if (violatesPrivateInclude(RequestingModule, File, Header.getRole(),
Header.getModule())) {
Private = Header.getModule();
continue;
}
// 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;
// Remember private headers for later printing of a diagnostic.
if (violatesPrivateInclude(RequestingModule, File, I->getRole(),
I->getModule())) {
Private = I->getModule();
continue;
}
// 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;
}
// 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

View File

@@ -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;
}
}
}
}

View File

@@ -0,0 +1 @@
#include "B/B.h"

View File

@@ -0,0 +1 @@
// AnotherModule.h

View File

@@ -0,0 +1 @@
// AnotherModuleExcluded.h

View File

@@ -0,0 +1 @@
#include "C.h"

View File

@@ -0,0 +1 @@
// C.h

View File

@@ -0,0 +1,2 @@
// FromImportedModuleFail.h
#include "NotInModule.h"

View File

@@ -0,0 +1 @@
#include "FromImportedModuleOK2.h"

View File

@@ -0,0 +1,4 @@
framework module FromImportedModuleOK {
header "FromImportedModuleOK.h"
header "FromImportedModuleOK2.h"
}

View File

@@ -0,0 +1,2 @@
// Header.h
#include "NotInModule.h"

View File

@@ -0,0 +1,5 @@
framework module FromImportedSubModule {
module Sub {
header "Header.h"
}
}

View File

@@ -0,0 +1,3 @@
framework module FromNonModularSubframework {
header "FromNonModularSubframework.h"
}

View File

@@ -0,0 +1 @@
#include "Subframework/Subframework.h"

View File

@@ -0,0 +1,7 @@
framework module FromSubframework {
umbrella header "FromSubframework.h"
framework module Subframework {
umbrella header "Subframework.h"
}
}

View File

@@ -0,0 +1,2 @@
#include "umbrella/foo.h"
#include "umbrella/bar/bar.h"

View File

@@ -0,0 +1,4 @@
framework module IncludeExcluded {
header "IncludeExcluded.h"
exclude header "Excluded.h"
}

View File

@@ -0,0 +1,2 @@
// NotFramework.h
#import "NotInModule.h"

View File

@@ -0,0 +1 @@
// NotInModule.h

View File

@@ -0,0 +1,12 @@
module AnotherModule {
header "AnotherModule.h"
exclude header "AnotherModuleExcluded.h"
}
module Umbrella {
umbrella "umbrella"
}
module NotFramework {
header "NotFramework.h"
}
framework module * { }

View File

@@ -0,0 +1 @@
// bar.h

View File

@@ -0,0 +1 @@
// foo.h

View File

@@ -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