bool IsDirectory;
bool IsNamedPipe;
bool InPCH;
+ bool IsVFSMapped; // FIXME: remove this when files support multiple names
+ FileData()
+ : Size(0), ModTime(0), IsDirectory(false), IsNamedPipe(false),
+ InPCH(false), IsVFSMapped(false) {}
};
/// \brief Abstract interface for introducing a FileManager cache for 'stat'
llvm::sys::fs::perms Perms;
public:
+ bool IsVFSMapped; // FIXME: remove when files support multiple names
+
+public:
Status() : Type(llvm::sys::fs::file_type::status_error) {}
Status(const llvm::sys::fs::file_status &Status);
Status(StringRef Name, StringRef RealName, llvm::sys::fs::UniqueID UID,
NamedFileEnt.setValue(&UFE);
if (UFE.isValid()) { // Already have an entry with this inode, return it.
+
+ // FIXME: this hack ensures that if we look up a file by a virtual path in
+ // the VFS that the getDir() will have the virtual path, even if we found
+ // the file by a 'real' path first. This is required in order to find a
+ // module's structure when its headers/module map are mapped in the VFS.
+ // We should remove this as soon as we can properly support a file having
+ // multiple names.
+ if (DirInfo != UFE.Dir && Data.IsVFSMapped)
+ UFE.Dir = DirInfo;
+
// If the stat process opened the file, close it to avoid a FD leak.
if (F)
delete F;
Data.IsDirectory = Status.isDirectory();
Data.IsNamedPipe = Status.getType() == llvm::sys::fs::file_type::fifo_file;
Data.InPCH = false;
+ Data.IsVFSMapped = Status.IsVFSMapped;
}
/// FileSystemStatCache::get - Get the 'stat' information for the specified
Status::Status(const file_status &Status)
: UID(Status.getUniqueID()), MTime(Status.getLastModificationTime()),
User(Status.getUser()), Group(Status.getGroup()), Size(Status.getSize()),
- Type(Status.type()), Perms(Status.permissions()) {}
+ Type(Status.type()), Perms(Status.permissions()), IsVFSMapped(false) {}
Status::Status(StringRef Name, StringRef ExternalName, UniqueID UID,
sys::TimeValue MTime, uint32_t User, uint32_t Group,
uint64_t Size, file_type Type, perms Perms)
: Name(Name), UID(UID), MTime(MTime), User(User), Group(Group), Size(Size),
- Type(Type), Perms(Perms) {}
+ Type(Type), Perms(Perms), IsVFSMapped(false) {}
bool Status::equivalent(const Status &Other) const {
return getUniqueID() == Other.getUniqueID();
assert(!S || S->getName() == F->getExternalContentsPath());
if (S && !F->useExternalName(UseExternalNames))
S->setName(PathStr);
+ if (S)
+ S->IsVFSMapped = true;
return S;
} else { // directory
DirectoryEntry *DE = cast<DirectoryEntry>(*Result);
header "not_real.h"
export *
}
+module import_some_frame {
+ header "import_some_frame.h"
+ export *
+}
--- /dev/null
+#import <SomeFramework/public_header.h>
+#import <SomeFramework/public_header2.h>
+#import <SomeFramework/public_header2.h>
void from_framework(void);
--- /dev/null
+// public_header2.h
--- /dev/null
+framework module SomeFramework {
+ umbrella header "public_header.h"
+ export *
+ module * { export * }
+}
{ 'name': 'not_real.h', 'type': 'file',
'external-contents': 'INPUT_DIR/actual_header.h'
},
+ { 'name': 'import_some_frame.h', 'type': 'file',
+ 'external-contents': 'INPUT_DIR/import_some_frame.h'
+ },
{ 'name': 'module.map', 'type': 'file',
'external-contents': 'INPUT_DIR/actual_module.map'
},
{ 'name': 'include_real.h', 'type': 'file',
'external-contents': 'INPUT_DIR/include_real.h'
},
- { 'name': 'SomeFramework.framework/Headers/public_header.h', 'type': 'file',
- 'external-contents': 'INPUT_DIR/public_header.h'
+ { 'name': 'SomeFramework.framework', 'type': 'directory',
+ 'contents': [
+ { 'name': 'Headers', 'type': 'directory',
+ 'contents': [
+ { 'name': 'public_header.h', 'type': 'file',
+ 'external-contents': 'INPUT_DIR/public_header.h' },
+ { 'name': 'public_header2.h', 'type': 'file',
+ 'external-contents': 'INPUT_DIR/public_header2.h' }
+ ]
+ }
+ ]
},
{ 'name': 'Foo.framework/Headers/Foo.h', 'type': 'file',
'external-contents': 'INPUT_DIR/Foo.h'
--- /dev/null
+// This test is for cases where we lookup a file by its 'real' path before we
+// use its VFS-mapped path. If we accidentally use the real path in header
+// search, we will not find a module for the headers. To test that we
+// intentionally rebuild modules, since the precompiled module file refers to
+// the dependency files by real path.
+
+// REQUIRES: shell
+// RUN: rm -rf %t %t-cache %t.pch
+// RUN: mkdir -p %t/SomeFramework.framework/Modules
+// RUN: cp %S/Inputs/some_frame_module.map %t/SomeFramework.framework/Modules/module.modulemap
+// RUN: sed -e "s:INPUT_DIR:%S/Inputs:g" -e "s:OUT_DIR:%t:g" %S/Inputs/vfsoverlay.yaml > %t.yaml
+
+// Build
+// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t-cache -F %t \
+// RUN: -ivfsoverlay %t.yaml -fsyntax-only %s -verify -Wauto-import \
+// RUN: -Werror=non-modular-include-in-framework-module
+
+// Rebuild
+// RUN: echo ' ' >> %t/SomeFramework.framework/Modules/module.modulemap
+// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t-cache -F %t \
+// RUN: -ivfsoverlay %t.yaml -fsyntax-only %s -verify -Wauto-import \
+// RUN: -Werror=non-modular-include-in-framework-module
+
+// Load from PCH
+// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t-cache -F %t \
+// RUN: -ivfsoverlay %t.yaml -emit-pch %s -o %t.pch \
+// RUN: -Werror=non-modular-include-in-framework-module \
+// RUN: -fmodules-ignore-macro=WITH_PREFIX
+// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t-cache -F %t \
+// RUN: -ivfsoverlay %t.yaml -include-pch %t.pch -fsyntax-only %s \
+// RUN: -Werror=non-modular-include-in-framework-module -DWITH_PREFIX \
+// RUN: -fmodules-ignore-macro=WITH_PREFIX
+
+// While indexing
+// RUN: c-index-test -index-file %s -fmodules -fmodules-cache-path=%t-cache -F %t \
+// RUN: -ivfsoverlay %t.yaml -fsyntax-only -Wauto-import \
+// RUN: -Werror=non-modular-include-in-framework-module | FileCheck %s
+// RUN: echo ' ' >> %t/SomeFramework.framework/Modules/module.modulemap
+// RUN: c-index-test -index-file %s -fmodules -fmodules-cache-path=%t-cache -F %t \
+// RUN: -ivfsoverlay %t.yaml -fsyntax-only -Wauto-import \
+// RUN: -Werror=non-modular-include-in-framework-module | FileCheck %s
+// CHECK: warning: treating
+// CHECK-NOT: error
+
+// With a VFS-mapped module map file
+// RUN: mv %t/SomeFramework.framework/Modules/module.modulemap %t/hide_module.map
+// RUN: echo "{ 'version': 0, 'roots': [ { " > %t2.yaml
+// RUN: echo "'name': '%t/SomeFramework.framework/Modules/module.modulemap'," >> %t2.yaml
+// RUN: echo "'type': 'file', 'external-contents': '%t/hide_module.map' } ] }" >> %t2.yaml
+
+// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t-cache -F %t \
+// RUN: -ivfsoverlay %t.yaml -ivfsoverlay %t2.yaml -fsyntax-only %s -verify \
+// RUN: -Wauto-import -Werror=non-modular-include-in-framework-module
+// RUN: echo ' ' >> %t/hide_module.map
+// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t-cache -F %t \
+// RUN: -ivfsoverlay %t.yaml -ivfsoverlay %t2.yaml -fsyntax-only %s -verify \
+// RUN: -Wauto-import -Werror=non-modular-include-in-framework-module
+
+// Within a module build
+// RUN: echo '@import import_some_frame;' | \
+// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t-cache -F %t \
+// RUN: -ivfsoverlay %t.yaml -ivfsoverlay %t2.yaml -fsyntax-only - \
+// RUN: -Werror=non-modular-include-in-framework-module -x objective-c -I %t
+// RUN: echo ' ' >> %t/hide_module.map
+// RUN: echo '@import import_some_frame;' | \
+// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t-cache -F %t \
+// RUN: -ivfsoverlay %t.yaml -ivfsoverlay %t2.yaml -fsyntax-only - \
+// RUN: -Werror=non-modular-include-in-framework-module -x objective-c -I %t
+
+#ifndef WITH_PREFIX
+#import <SomeFramework/public_header.h> // expected-warning{{treating}}
+#import <SomeFramework/public_header2.h> // expected-warning{{treating}}
+@import SomeFramework.public_header2;
+#endif