diff options
-rw-r--r-- | include/clang/Basic/DiagnosticLexKinds.td | 2 | ||||
-rw-r--r-- | lib/Lex/PPLexerChange.cpp | 67 | ||||
-rw-r--r-- | test/Modules/Inputs/Module.framework/Headers/Module.h | 2 | ||||
-rw-r--r-- | test/Modules/on-demand-build.m | 8 |
4 files changed, 76 insertions, 3 deletions
diff --git a/include/clang/Basic/DiagnosticLexKinds.td b/include/clang/Basic/DiagnosticLexKinds.td index cc202b48e5..62bd315842 100644 --- a/include/clang/Basic/DiagnosticLexKinds.td +++ b/include/clang/Basic/DiagnosticLexKinds.td @@ -425,5 +425,7 @@ def err_mmap_nested_submodule_id : Error< def warn_auto_module_import : Warning< "treating #%select{include|import|include_next|__include_macros}0 as an " "import of module '%1'">, InGroup<AutoImport>, DefaultIgnore; +def warn_uncovered_module_header : Warning< + "umbrella header does not include header '%0'">, InGroup<IncompleteUmbrella>; } diff --git a/lib/Lex/PPLexerChange.cpp b/lib/Lex/PPLexerChange.cpp index 24dda4f84c..b8839851ec 100644 --- a/lib/Lex/PPLexerChange.cpp +++ b/lib/Lex/PPLexerChange.cpp @@ -18,7 +18,10 @@ #include "clang/Lex/LexDiagnostic.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/SourceManager.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/PathV2.h" +#include "llvm/ADT/StringSwitch.h" using namespace clang; PPCallbacks::~PPCallbacks() {} @@ -199,6 +202,31 @@ void Preprocessor::EnterTokenStream(const Token *Toks, unsigned NumToks, CurLexerKind = CLK_TokenLexer; } +/// \brief Compute the relative path that names the given file relative to +/// the given directory. +static void computeRelativePath(FileManager &FM, const DirectoryEntry *Dir, + const FileEntry *File, + llvm::SmallString<128> &Result) { + Result.clear(); + + StringRef FilePath = File->getDir()->getName(); + StringRef Path = FilePath; + while (!Path.empty()) { + if (const DirectoryEntry *CurDir = FM.getDirectory(Path)) { + if (CurDir == Dir) { + Result = FilePath.substr(Path.size()); + llvm::sys::path::append(Result, + llvm::sys::path::filename(File->getName())); + return; + } + } + + Path = llvm::sys::path::parent_path(Path); + } + + Result = File->getName(); +} + /// 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. @@ -316,6 +344,45 @@ bool Preprocessor::HandleEndOfFile(Token &Result, bool isEndOfMacro) { I=WarnUnusedMacroLocs.begin(), E=WarnUnusedMacroLocs.end(); I!=E; ++I) Diag(*I, diag::pp_macro_not_used); + // If we are building a module that has an umbrella header, make sure that + // each of the headers within the directory covered by the umbrella header + // was actually included by the umbrella header. + if (Module *Mod = getCurrentModule()) { + if (Mod->getUmbrellaHeader()) { + SourceLocation StartLoc + = SourceMgr.getLocForStartOfFile(SourceMgr.getMainFileID()); + + if (getDiagnostics().getDiagnosticLevel( + diag::warn_uncovered_module_header, + StartLoc) != DiagnosticsEngine::Ignored) { + typedef typename llvm::sys::fs::recursive_directory_iterator + recursive_directory_iterator; + const DirectoryEntry *Dir = Mod->getUmbrellaDir(); + llvm::error_code EC; + for (recursive_directory_iterator Entry(Dir->getName(), EC), End; + Entry != End && !EC; Entry.increment(EC)) { + using llvm::StringSwitch; + + // Check whether this entry has an extension typically associated with + // headers. + if (!StringSwitch<bool>(llvm::sys::path::extension(Entry->path())) + .Cases(".h", ".H", ".hh", ".hpp", true) + .Default(false)) + continue; + + if (const FileEntry *Header = getFileManager().getFile(Entry->path())) + if (!getSourceManager().hasFileInfo(Header)) { + // Find the + llvm::SmallString<128> RelativePath; + computeRelativePath(FileMgr, Dir, Header, RelativePath); + Diag(StartLoc, diag::warn_uncovered_module_header) + << RelativePath; + } + } + } + } + } + return true; } diff --git a/test/Modules/Inputs/Module.framework/Headers/Module.h b/test/Modules/Inputs/Module.framework/Headers/Module.h index be88bb5adc..3d10112e55 100644 --- a/test/Modules/Inputs/Module.framework/Headers/Module.h +++ b/test/Modules/Inputs/Module.framework/Headers/Module.h @@ -1,3 +1,5 @@ +// expected-warning{{umbrella header}} + #ifndef MODULE_H #define MODULE_H const char *getModuleVersion(void); diff --git a/test/Modules/on-demand-build.m b/test/Modules/on-demand-build.m index 7843014453..644519b972 100644 --- a/test/Modules/on-demand-build.m +++ b/test/Modules/on-demand-build.m @@ -1,13 +1,15 @@ // RUN: rm -rf %t -// RUN: %clang_cc1 -fno-objc-infer-related-result-type -Werror -fmodule-cache-path %t -F %S/Inputs -verify %s -// RUN: %clang_cc1 -fno-objc-infer-related-result-type -Werror -x objective-c++ -fmodule-cache-path %t -F %S/Inputs -verify %s -// RUN: %clang_cc1 -fno-objc-infer-related-result-type -Werror -fmodule-cache-path %t -F %S/Inputs -verify %s +// RUN: %clang_cc1 -fno-objc-infer-related-result-type -Werror -Wno-error=incomplete-umbrella -fmodule-cache-path %t -F %S/Inputs -verify %s +// RUN: %clang_cc1 -fno-objc-infer-related-result-type -Werror -Wno-error=incomplete-umbrella -x objective-c++ -fmodule-cache-path %t -F %S/Inputs -verify %s +// RUN: %clang_cc1 -fno-objc-infer-related-result-type -Werror -Wno-error=incomplete-umbrella -fmodule-cache-path %t -F %S/Inputs -verify %s #define FOO __import_module__ Module; @interface OtherClass @end + + // in module: expected-note{{class method 'alloc' is assumed to return an instance of its receiver type ('Module *')}} void test_getModuleVersion() { const char *version = getModuleVersion(); |