| //===--- ClangImporter.cpp - Import Clang Modules -------------------------===// |
| // |
| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors |
| // Licensed under Apache License v2.0 with Runtime Library Exception |
| // |
| // See https://swift.org/LICENSE.txt for license information |
| // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file implements support for loading Clang modules into Swift. |
| // |
| //===----------------------------------------------------------------------===// |
| #include "swift/ClangImporter/ClangImporter.h" |
| #include "swift/ClangImporter/ClangModule.h" |
| #include "IAMInference.h" |
| #include "ImporterImpl.h" |
| #include "ClangDiagnosticConsumer.h" |
| #include "swift/Subsystems.h" |
| #include "swift/AST/ASTContext.h" |
| #include "swift/AST/DiagnosticEngine.h" |
| #include "swift/AST/DiagnosticsClangImporter.h" |
| #include "swift/AST/IRGenOptions.h" |
| #include "swift/AST/LinkLibrary.h" |
| #include "swift/AST/Module.h" |
| #include "swift/AST/NameLookup.h" |
| #include "swift/AST/Types.h" |
| #include "swift/Basic/Defer.h" |
| #include "swift/Basic/Platform.h" |
| #include "swift/Basic/Range.h" |
| #include "swift/Basic/StringExtras.h" |
| #include "swift/Basic/Version.h" |
| #include "swift/ClangImporter/ClangImporterOptions.h" |
| #include "swift/Parse/Lexer.h" |
| #include "swift/Parse/Parser.h" |
| #include "swift/Config.h" |
| #include "clang/AST/ASTContext.h" |
| #include "clang/AST/Mangle.h" |
| #include "clang/Basic/CharInfo.h" |
| #include "clang/Basic/IdentifierTable.h" |
| #include "clang/Basic/Module.h" |
| #include "clang/Basic/TargetInfo.h" |
| #include "clang/Basic/Version.h" |
| #include "clang/CodeGen/ObjectFilePCHContainerOperations.h" |
| #include "clang/Frontend/FrontendActions.h" |
| #include "clang/Frontend/Utils.h" |
| #include "clang/Index/IndexingAction.h" |
| #include "clang/Serialization/ASTReader.h" |
| #include "clang/Serialization/ASTWriter.h" |
| #include "clang/Lex/Preprocessor.h" |
| #include "clang/Lex/PreprocessorOptions.h" |
| #include "clang/Parse/Parser.h" |
| #include "clang/Rewrite/Frontend/FrontendActions.h" |
| #include "clang/Rewrite/Frontend/Rewriters.h" |
| #include "clang/Sema/Lookup.h" |
| #include "clang/Sema/Sema.h" |
| #include "llvm/ADT/STLExtras.h" |
| #include "llvm/ADT/StringExtras.h" |
| #include "llvm/Support/CrashRecoveryContext.h" |
| #include "llvm/Support/Memory.h" |
| #include "llvm/Support/Path.h" |
| #include <algorithm> |
| #include <memory> |
| |
| using namespace swift; |
| using namespace importer; |
| |
| // Commonly-used Clang classes. |
| using clang::CompilerInstance; |
| using clang::CompilerInvocation; |
| |
| #pragma mark Internal data structures |
| |
| namespace { |
| class HeaderImportCallbacks : public clang::PPCallbacks { |
| ClangImporter::Implementation &Impl; |
| public: |
| HeaderImportCallbacks(ClangImporter::Implementation &impl) |
| : Impl(impl) {} |
| |
| void handleImport(const clang::Module *imported) { |
| if (!imported) |
| return; |
| Impl.ImportedHeaderExports.push_back( |
| const_cast<clang::Module *>(imported)); |
| } |
| |
| void InclusionDirective(clang::SourceLocation HashLoc, |
| const clang::Token &IncludeTok, |
| StringRef FileName, |
| bool IsAngled, |
| clang::CharSourceRange FilenameRange, |
| const clang::FileEntry *File, |
| StringRef SearchPath, |
| StringRef RelativePath, |
| const clang::Module *Imported, |
| clang::SrcMgr::CharacteristicKind FileType) override { |
| handleImport(Imported); |
| } |
| |
| void moduleImport(clang::SourceLocation ImportLoc, |
| clang::ModuleIdPath Path, |
| const clang::Module *Imported) override { |
| handleImport(Imported); |
| } |
| }; |
| |
| class PCHDeserializationCallbacks : public clang::ASTDeserializationListener { |
| ClangImporter::Implementation &Impl; |
| public: |
| explicit PCHDeserializationCallbacks(ClangImporter::Implementation &impl) |
| : Impl(impl) {} |
| void ModuleImportRead(clang::serialization::SubmoduleID ID, |
| clang::SourceLocation ImportLoc) override { |
| if (Impl.IsReadingBridgingPCH) { |
| Impl.PCHImportedSubmodules.push_back(ID); |
| } |
| } |
| }; |
| |
| class HeaderParsingASTConsumer : public clang::ASTConsumer { |
| SmallVector<clang::DeclGroupRef, 4> DeclGroups; |
| PCHDeserializationCallbacks PCHCallbacks; |
| public: |
| explicit HeaderParsingASTConsumer(ClangImporter::Implementation &impl) |
| : PCHCallbacks(impl) {} |
| void |
| HandleTopLevelDeclInObjCContainer(clang::DeclGroupRef decls) override { |
| DeclGroups.push_back(decls); |
| } |
| |
| ArrayRef<clang::DeclGroupRef> getAdditionalParsedDecls() { |
| return DeclGroups; |
| } |
| |
| clang::ASTDeserializationListener *GetASTDeserializationListener() override { |
| return &PCHCallbacks; |
| } |
| |
| void reset() { |
| DeclGroups.clear(); |
| } |
| }; |
| |
| class ParsingAction : public clang::ASTFrontendAction { |
| ASTContext &Ctx; |
| ClangImporter &Importer; |
| ClangImporter::Implementation &Impl; |
| const ClangImporterOptions &ImporterOpts; |
| std::string SwiftPCHHash; |
| public: |
| explicit ParsingAction(ASTContext &ctx, |
| ClangImporter &importer, |
| ClangImporter::Implementation &impl, |
| const ClangImporterOptions &importerOpts, |
| std::string swiftPCHHash) |
| : Ctx(ctx), Importer(importer), Impl(impl), ImporterOpts(importerOpts), |
| SwiftPCHHash(swiftPCHHash) {} |
| std::unique_ptr<clang::ASTConsumer> |
| CreateASTConsumer(clang::CompilerInstance &CI, StringRef InFile) override { |
| return llvm::make_unique<HeaderParsingASTConsumer>(Impl); |
| } |
| bool BeginSourceFileAction(clang::CompilerInstance &CI) override { |
| // Prefer frameworks over plain headers. |
| // We add search paths here instead of when building the initial invocation |
| // so that (a) we use the same code as search paths for imported modules, |
| // and (b) search paths are always added after -Xcc options. |
| SearchPathOptions &searchPathOpts = Ctx.SearchPathOpts; |
| for (const auto &framepath : searchPathOpts.FrameworkSearchPaths) { |
| Importer.addSearchPath(framepath.Path, /*isFramework*/true, |
| framepath.IsSystem); |
| } |
| |
| for (auto path : searchPathOpts.ImportSearchPaths) { |
| Importer.addSearchPath(path, /*isFramework*/false, /*isSystem=*/false); |
| } |
| |
| auto PCH = Importer.getOrCreatePCH(ImporterOpts, SwiftPCHHash); |
| if (PCH.hasValue()) { |
| Impl.getClangInstance()->getPreprocessorOpts().ImplicitPCHInclude = |
| PCH.getValue(); |
| Impl.IsReadingBridgingPCH = true; |
| Impl.setSinglePCHImport(PCH.getValue()); |
| } |
| |
| return true; |
| } |
| }; |
| |
| class StdStringMemBuffer : public llvm::MemoryBuffer { |
| const std::string storage; |
| const std::string name; |
| public: |
| StdStringMemBuffer(std::string &&source, StringRef name) |
| : storage(std::move(source)), name(name.str()) { |
| init(storage.data(), storage.data() + storage.size(), |
| /*null-terminated=*/true); |
| } |
| |
| StringRef getBufferIdentifier() const override { |
| return name; |
| } |
| |
| BufferKind getBufferKind() const override { |
| return MemoryBuffer_Malloc; |
| } |
| }; |
| |
| class ZeroFilledMemoryBuffer : public llvm::MemoryBuffer { |
| const std::string name; |
| public: |
| explicit ZeroFilledMemoryBuffer(size_t size, StringRef name) |
| : name(name.str()) { |
| assert(size > 0); |
| std::error_code error; |
| llvm::sys::MemoryBlock memory = |
| llvm::sys::Memory::allocateMappedMemory(size, nullptr, |
| llvm::sys::Memory::MF_READ, |
| error); |
| assert(!error && "failed to allocated read-only zero-filled memory"); |
| init(static_cast<char *>(memory.base()), |
| static_cast<char *>(memory.base()) + memory.size() - 1, |
| /*null-terminated*/true); |
| } |
| |
| ~ZeroFilledMemoryBuffer() override { |
| llvm::sys::MemoryBlock memory{const_cast<char *>(getBufferStart()), |
| getBufferSize()}; |
| std::error_code error = llvm::sys::Memory::releaseMappedMemory(memory); |
| assert(!error && "failed to deallocate read-only zero-filled memory"); |
| (void)error; |
| } |
| |
| ZeroFilledMemoryBuffer(const ZeroFilledMemoryBuffer &) = delete; |
| ZeroFilledMemoryBuffer(ZeroFilledMemoryBuffer &&) = delete; |
| void operator=(const ZeroFilledMemoryBuffer &) = delete; |
| void operator=(ZeroFilledMemoryBuffer &&) = delete; |
| |
| StringRef getBufferIdentifier() const override { |
| return name; |
| } |
| BufferKind getBufferKind() const override { |
| return MemoryBuffer_MMap; |
| } |
| }; |
| } // end anonymous namespace |
| |
| namespace { |
| class BridgingPPTracker : public clang::PPCallbacks { |
| ClangImporter::Implementation &Impl; |
| |
| public: |
| BridgingPPTracker(ClangImporter::Implementation &Impl) |
| : Impl(Impl) {} |
| |
| private: |
| static unsigned getNumModuleIdentifiers(const clang::Module *Mod) { |
| unsigned Result = 1; |
| while (Mod->Parent) { |
| Mod = Mod->Parent; |
| ++Result; |
| } |
| return Result; |
| } |
| |
| void InclusionDirective(clang::SourceLocation HashLoc, |
| const clang::Token &IncludeTok, |
| StringRef FileName, |
| bool IsAngled, |
| clang::CharSourceRange FilenameRange, |
| const clang::FileEntry *File, |
| StringRef SearchPath, |
| StringRef RelativePath, |
| const clang::Module *Imported, |
| clang::SrcMgr::CharacteristicKind FileType) override{ |
| if (!Imported) { |
| if (File) |
| Impl.BridgeHeaderFiles.insert(File); |
| return; |
| } |
| // Synthesize identifier locations. |
| SmallVector<clang::SourceLocation, 4> IdLocs; |
| for (unsigned I = 0, E = getNumModuleIdentifiers(Imported); I != E; ++I) |
| IdLocs.push_back(HashLoc); |
| handleImport(HashLoc, IdLocs, Imported); |
| } |
| |
| void moduleImport(clang::SourceLocation ImportLoc, |
| clang::ModuleIdPath Path, |
| const clang::Module *Imported) override { |
| if (!Imported) |
| return; |
| SmallVector<clang::SourceLocation, 4> IdLocs; |
| for (auto &P : Path) |
| IdLocs.push_back(P.second); |
| handleImport(ImportLoc, IdLocs, Imported); |
| } |
| |
| void handleImport(clang::SourceLocation ImportLoc, |
| ArrayRef<clang::SourceLocation> IdLocs, |
| const clang::Module *Imported) { |
| clang::ASTContext &ClangCtx = Impl.getClangASTContext(); |
| clang::ImportDecl *ClangImport = clang::ImportDecl::Create(ClangCtx, |
| ClangCtx.getTranslationUnitDecl(), |
| ImportLoc, |
| const_cast<clang::Module*>(Imported), |
| IdLocs); |
| Impl.BridgeHeaderTopLevelImports.push_back(ClangImport); |
| } |
| |
| void MacroDefined(const clang::Token &MacroNameTok, |
| const clang::MacroDirective *MD) override { |
| Impl.BridgeHeaderMacros.push_back(MacroNameTok.getIdentifierInfo()); |
| } |
| }; |
| |
| class ClangImporterDependencyCollector : public clang::DependencyCollector |
| { |
| llvm::StringSet<> ExcludedPaths; |
| const bool TrackSystemDeps; |
| |
| public: |
| ClangImporterDependencyCollector(bool TrackSystemDeps) |
| : TrackSystemDeps(TrackSystemDeps) {} |
| |
| void excludePath(StringRef filename) { |
| ExcludedPaths.insert(filename); |
| } |
| |
| bool isClangImporterSpecialName(StringRef Filename) { |
| using ImporterImpl = ClangImporter::Implementation; |
| return (Filename == ImporterImpl::moduleImportBufferName |
| || Filename == ImporterImpl::bridgingHeaderBufferName); |
| } |
| |
| bool needSystemDependencies() override { return TrackSystemDeps; } |
| |
| bool sawDependency(StringRef Filename, bool FromClangModule, |
| bool IsSystem, bool IsClangModuleFile, |
| bool IsMissing) override { |
| if (!clang::DependencyCollector::sawDependency(Filename, FromClangModule, |
| IsSystem, IsClangModuleFile, |
| IsMissing)) |
| return false; |
| // Currently preserving older ClangImporter behavior of ignoring .pcm |
| // file dependencies, but possibly revisit? |
| if (IsClangModuleFile |
| || isClangImporterSpecialName(Filename) |
| || ExcludedPaths.count(Filename)) |
| return false; |
| return true; |
| } |
| }; |
| } // end anonymous namespace |
| |
| std::shared_ptr<clang::DependencyCollector> |
| ClangImporter::createDependencyCollector(bool TrackSystemDeps) |
| { |
| return std::make_shared<ClangImporterDependencyCollector>(TrackSystemDeps); |
| } |
| |
| void ClangImporter::Implementation::addBridgeHeaderTopLevelDecls( |
| clang::Decl *D) { |
| if (shouldIgnoreBridgeHeaderTopLevelDecl(D)) |
| return; |
| |
| BridgeHeaderTopLevelDecls.push_back(D); |
| } |
| |
| bool ClangImporter::Implementation::shouldIgnoreBridgeHeaderTopLevelDecl( |
| clang::Decl *D) { |
| // Ignore forward references; |
| if (auto *ID = dyn_cast<clang::ObjCInterfaceDecl>(D)) { |
| if (!ID->isThisDeclarationADefinition()) |
| return true; |
| } else if (auto PD = dyn_cast<clang::ObjCProtocolDecl>(D)) { |
| if (!PD->isThisDeclarationADefinition()) |
| return true; |
| } else if (auto TD = dyn_cast<clang::TagDecl>(D)) { |
| if (!TD->isThisDeclarationADefinition()) |
| return true; |
| } |
| return false; |
| } |
| |
| ClangImporter::ClangImporter(ASTContext &ctx, |
| const ClangImporterOptions &clangImporterOpts, |
| DependencyTracker *tracker) |
| : ClangModuleLoader(tracker), |
| Impl(*new Implementation(ctx, clangImporterOpts)) |
| { |
| } |
| |
| ClangImporter::~ClangImporter() { |
| delete &Impl; |
| } |
| |
| void ClangImporter::setTypeResolver(LazyResolver &resolver) { |
| Impl.setTypeResolver(&resolver); |
| } |
| |
| void ClangImporter::clearTypeResolver() { |
| Impl.setTypeResolver(nullptr); |
| } |
| |
| #pragma mark Module loading |
| |
| /// Finds the glibc.modulemap file relative to the provided resource dir. |
| /// |
| /// Note that the module map used for Glibc depends on the target we're |
| /// compiling for, and is not included in the resource directory with the other |
| /// implicit module maps. It's at {freebsd|linux}/{arch}/glibc.modulemap. |
| static Optional<StringRef> |
| getGlibcModuleMapPath(StringRef resourceDir, llvm::Triple triple, |
| SmallVectorImpl<char> &scratch) { |
| if (resourceDir.empty()) |
| return None; |
| |
| scratch.append(resourceDir.begin(), resourceDir.end()); |
| llvm::sys::path::append( |
| scratch, |
| swift::getPlatformNameForTriple(triple), |
| swift::getMajorArchitectureName(triple), |
| "glibc.modulemap"); |
| |
| // Only specify the module map if that file actually exists. |
| // It may not--for example in the case that |
| // `swiftc -target x86_64-unknown-linux-gnu -emit-ir` is invoked using |
| // a Swift compiler not built for Linux targets. |
| if (llvm::sys::fs::exists(scratch)) { |
| return StringRef(scratch.data(), scratch.size()); |
| } else { |
| return None; |
| } |
| } |
| |
| static void |
| getNormalInvocationArguments(std::vector<std::string> &invocationArgStrs, |
| ASTContext &ctx, |
| const ClangImporterOptions &importerOpts) { |
| const auto &LangOpts = ctx.LangOpts; |
| const llvm::Triple &triple = LangOpts.Target; |
| SearchPathOptions &searchPathOpts = ctx.SearchPathOpts; |
| |
| auto languageVersion = ctx.LangOpts.EffectiveLanguageVersion; |
| |
| if (llvm::sys::path::extension(importerOpts.BridgingHeader) |
| .endswith(file_types::getExtension(file_types::TY_PCH))) { |
| invocationArgStrs.insert(invocationArgStrs.end(), { |
| "-include-pch", importerOpts.BridgingHeader |
| }); |
| } |
| |
| // If there are no shims in the resource dir, add a search path in the SDK. |
| SmallString<128> shimsPath(searchPathOpts.RuntimeResourcePath); |
| llvm::sys::path::append(shimsPath, "shims"); |
| if (!llvm::sys::fs::exists(shimsPath)) { |
| shimsPath = searchPathOpts.SDKPath; |
| llvm::sys::path::append(shimsPath, "usr", "lib", "swift", "shims"); |
| invocationArgStrs.insert(invocationArgStrs.end(), { |
| "-isystem", shimsPath.str() |
| }); |
| } |
| |
| // Construct the invocation arguments for the current target. |
| // Add target-independent options first. |
| invocationArgStrs.insert(invocationArgStrs.end(), { |
| // Don't emit LLVM IR. |
| "-fsyntax-only", |
| |
| // Enable block support. |
| "-fblocks", |
| |
| languageVersion.preprocessorDefinition("__swift__", {10000, 100, 1}), |
| |
| "-fretain-comments-from-system-headers", |
| |
| "-isystem", searchPathOpts.RuntimeResourcePath, |
| }); |
| |
| // Enable Position Independence. `-fPIC` is not supported on Windows, which |
| // is implicitly position independent. |
| if (!triple.isOSWindows()) |
| invocationArgStrs.insert(invocationArgStrs.end(), {"-fPIC"}); |
| |
| // Enable modules. |
| invocationArgStrs.insert(invocationArgStrs.end(), { |
| "-fmodules", |
| "-Xclang", "-fmodule-feature", "-Xclang", "swift" |
| }); |
| // Don't enforce strict rules when inside the debugger to work around search |
| // path problems caused by a module existing in both the build/install |
| // directory and the source directory. |
| if (!importerOpts.DebuggerSupport) |
| invocationArgStrs.push_back( |
| "-Werror=non-modular-include-in-framework-module"); |
| |
| if (LangOpts.EnableObjCInterop) { |
| invocationArgStrs.insert(invocationArgStrs.end(), |
| {"-x", "objective-c", "-std=gnu11", "-fobjc-arc"}); |
| // TODO: Investigate whether 7.0 is a suitable default version. |
| if (!triple.isOSDarwin()) |
| invocationArgStrs.insert(invocationArgStrs.end(), |
| {"-fobjc-runtime=ios-7.0"}); |
| |
| // Define macros that Swift bridging headers use. |
| invocationArgStrs.insert(invocationArgStrs.end(), { |
| "-DSWIFT_CLASS_EXTRA=__attribute__((__annotate__(" |
| "\"" SWIFT_NATIVE_ANNOTATION_STRING "\")))", |
| "-DSWIFT_PROTOCOL_EXTRA=__attribute__((__annotate__(" |
| "\"" SWIFT_NATIVE_ANNOTATION_STRING "\")))", |
| "-DSWIFT_EXTENSION_EXTRA=__attribute__((__annotate__(" |
| "\"" SWIFT_NATIVE_ANNOTATION_STRING "\")))", |
| "-DSWIFT_ENUM_EXTRA=__attribute__((__annotate__(" |
| "\"" SWIFT_NATIVE_ANNOTATION_STRING "\")))", |
| }); |
| |
| } else { |
| invocationArgStrs.insert(invocationArgStrs.end(), {"-x", "c", "-std=gnu11"}); |
| } |
| |
| // Set C language options. |
| if (triple.isOSDarwin()) { |
| invocationArgStrs.insert(invocationArgStrs.end(), { |
| // Avoid including the iso646.h header because some headers from OS X |
| // frameworks are broken by it. |
| "-D_ISO646_H_", "-D__ISO646_H", |
| |
| // Request new APIs from AppKit. |
| "-DSWIFT_SDK_OVERLAY_APPKIT_EPOCH=2", |
| |
| // Request new APIs from Foundation. |
| "-DSWIFT_SDK_OVERLAY_FOUNDATION_EPOCH=8", |
| |
| // Request new APIs from SceneKit. |
| "-DSWIFT_SDK_OVERLAY2_SCENEKIT_EPOCH=3", |
| |
| // Request new APIs from GameplayKit. |
| "-DSWIFT_SDK_OVERLAY_GAMEPLAYKIT_EPOCH=1", |
| |
| // Request new APIs from SpriteKit. |
| "-DSWIFT_SDK_OVERLAY_SPRITEKIT_EPOCH=1", |
| |
| // Request new APIs from CoreImage. |
| "-DSWIFT_SDK_OVERLAY_COREIMAGE_EPOCH=2", |
| |
| // Request new APIs from libdispatch. |
| "-DSWIFT_SDK_OVERLAY_DISPATCH_EPOCH=2", |
| |
| // Request new APIs from libpthread |
| "-DSWIFT_SDK_OVERLAY_PTHREAD_EPOCH=1", |
| |
| // Request new APIs from CoreGraphics. |
| "-DSWIFT_SDK_OVERLAY_COREGRAPHICS_EPOCH=0", |
| |
| // Request new APIs from UIKit. |
| "-DSWIFT_SDK_OVERLAY_UIKIT_EPOCH=2", |
| }); |
| |
| // Get the version of this compiler and pass it to C/Objective-C |
| // declarations. |
| auto V = version::Version::getCurrentCompilerVersion(); |
| if (!V.empty()) { |
| invocationArgStrs.insert(invocationArgStrs.end(), { |
| V.preprocessorDefinition("__SWIFT_COMPILER_VERSION", |
| {1000000000, /*ignored*/ 0, 1000000, 1000, 1}), |
| }); |
| } |
| } else { |
| // Ideally we should turn this on for all Glibc targets that are actually |
| // using Glibc or a libc that respects that flag. This will cause some |
| // source breakage however (specifically with strerror_r()) on Linux |
| // without a workaround. |
| if (triple.isOSFuchsia() || triple.isAndroid()) { |
| // Many of the modern libc features are hidden behind feature macros like |
| // _GNU_SOURCE or _XOPEN_SOURCE. |
| invocationArgStrs.insert(invocationArgStrs.end(), { |
| "-D_GNU_SOURCE", |
| }); |
| } |
| |
| if (triple.isOSWindows()) { |
| switch (triple.getArch()) { |
| default: llvm_unreachable("unsupported Windows architecture"); |
| case llvm::Triple::arm: |
| case llvm::Triple::thumb: |
| invocationArgStrs.insert(invocationArgStrs.end(), {"-D_ARM_"}); |
| break; |
| case llvm::Triple::aarch64: |
| invocationArgStrs.insert(invocationArgStrs.end(), {"-D_ARM64_"}); |
| break; |
| case llvm::Triple::x86: |
| invocationArgStrs.insert(invocationArgStrs.end(), {"-D_X86_"}); |
| break; |
| case llvm::Triple::x86_64: |
| invocationArgStrs.insert(invocationArgStrs.end(), {"-D_AMD64_"}); |
| break; |
| } |
| } |
| |
| SmallString<128> GlibcModuleMapPath; |
| if (auto path = getGlibcModuleMapPath(searchPathOpts.RuntimeResourcePath, |
| triple, GlibcModuleMapPath)) { |
| invocationArgStrs.push_back((Twine("-fmodule-map-file=") + *path).str()); |
| } else { |
| // FIXME: Emit a warning of some kind. |
| } |
| } |
| |
| if (searchPathOpts.SDKPath.empty()) { |
| invocationArgStrs.push_back("-Xclang"); |
| invocationArgStrs.push_back("-nostdsysteminc"); |
| } else { |
| if (triple.isWindowsMSVCEnvironment()) { |
| llvm::SmallString<261> path; // MAX_PATH + 1 |
| path = searchPathOpts.SDKPath; |
| llvm::sys::path::append(path, "usr", "include"); |
| llvm::sys::path::native(path); |
| |
| invocationArgStrs.push_back("-isystem"); |
| invocationArgStrs.push_back(path.str()); |
| } else { |
| // On Darwin, Clang uses -isysroot to specify the include |
| // system root. On other targets, it seems to use --sysroot. |
| invocationArgStrs.push_back(triple.isOSDarwin() ? "-isysroot" |
| : "--sysroot"); |
| invocationArgStrs.push_back(searchPathOpts.SDKPath); |
| } |
| } |
| |
| const std::string &moduleCachePath = importerOpts.ModuleCachePath; |
| if (!moduleCachePath.empty()) { |
| invocationArgStrs.push_back("-fmodules-cache-path="); |
| invocationArgStrs.back().append(moduleCachePath); |
| } |
| |
| if (!importerOpts.DisableModulesValidateSystemHeaders) { |
| invocationArgStrs.push_back("-fmodules-validate-system-headers"); |
| } |
| |
| if (importerOpts.DetailedPreprocessingRecord) { |
| invocationArgStrs.insert(invocationArgStrs.end(), { |
| "-Xclang", "-detailed-preprocessing-record", |
| "-Xclang", "-fmodule-format=raw", |
| }); |
| } else { |
| invocationArgStrs.insert(invocationArgStrs.end(), { |
| "-Xclang", "-fmodule-format=obj", |
| }); |
| } |
| |
| // Enable API notes alongside headers/in frameworks. |
| invocationArgStrs.push_back("-fapinotes-modules"); |
| invocationArgStrs.push_back("-fapinotes-swift-version=" + |
| languageVersion.asAPINotesVersionString()); |
| invocationArgStrs.push_back("-iapinotes-modules"); |
| invocationArgStrs.push_back((llvm::Twine(searchPathOpts.RuntimeResourcePath) + |
| llvm::sys::path::get_separator() + |
| "apinotes").str()); |
| } |
| |
| static void |
| getEmbedBitcodeInvocationArguments(std::vector<std::string> &invocationArgStrs, |
| ASTContext &ctx, |
| const ClangImporterOptions &importerOpts) { |
| invocationArgStrs.insert(invocationArgStrs.end(), { |
| // Backend mode. |
| "-fembed-bitcode", |
| |
| // ...but Clang isn't doing the emission. |
| "-fsyntax-only", |
| |
| "-x", "ir", |
| }); |
| } |
| |
| static void |
| addCommonInvocationArguments(std::vector<std::string> &invocationArgStrs, |
| ASTContext &ctx, |
| const ClangImporterOptions &importerOpts) { |
| using ImporterImpl = ClangImporter::Implementation; |
| const llvm::Triple &triple = ctx.LangOpts.Target; |
| SearchPathOptions &searchPathOpts = ctx.SearchPathOpts; |
| |
| invocationArgStrs.push_back("-target"); |
| invocationArgStrs.push_back(triple.str()); |
| |
| invocationArgStrs.push_back(ImporterImpl::moduleImportBufferName); |
| |
| if (ctx.LangOpts.EnableAppExtensionRestrictions) { |
| invocationArgStrs.push_back("-fapplication-extension"); |
| } |
| |
| if (!importerOpts.TargetCPU.empty()) { |
| invocationArgStrs.push_back("-mcpu=" + importerOpts.TargetCPU); |
| |
| } else if (triple.isOSDarwin()) { |
| // Special case: arm64 defaults to the "cyclone" CPU for Darwin, |
| // but Clang only detects this if we use -arch. |
| if (triple.getArch() == llvm::Triple::aarch64 || |
| triple.getArch() == llvm::Triple::aarch64_be) { |
| invocationArgStrs.push_back("-mcpu=cyclone"); |
| } |
| } else if (triple.getArch() == llvm::Triple::systemz) { |
| invocationArgStrs.push_back("-march=z196"); |
| } |
| |
| if (!importerOpts.Optimization.empty()) { |
| invocationArgStrs.push_back(importerOpts.Optimization); |
| } |
| |
| const std::string &overrideResourceDir = importerOpts.OverrideResourceDir; |
| if (overrideResourceDir.empty()) { |
| llvm::SmallString<128> resourceDir(searchPathOpts.RuntimeResourcePath); |
| |
| // Adjust the path to refer to our copy of the Clang resource directory |
| // under 'lib/swift/clang', which is either a real resource directory or a |
| // symlink to one inside of a full Clang installation. |
| // |
| // The rationale for looking under the Swift resource directory and not |
| // assuming that the Clang resource directory is located next to it is that |
| // Swift, when installed separately, should not need to install files in |
| // directories that are not "owned" by it. |
| llvm::sys::path::append(resourceDir, "clang"); |
| |
| // Set the Clang resource directory to the path we computed. |
| invocationArgStrs.push_back("-resource-dir"); |
| invocationArgStrs.push_back(resourceDir.str()); |
| } else { |
| invocationArgStrs.push_back("-resource-dir"); |
| invocationArgStrs.push_back(overrideResourceDir); |
| } |
| |
| if (!importerOpts.IndexStorePath.empty()) { |
| invocationArgStrs.push_back("-index-store-path"); |
| invocationArgStrs.push_back(importerOpts.IndexStorePath); |
| } |
| |
| invocationArgStrs.push_back("-fansi-escape-codes"); |
| |
| for (auto extraArg : importerOpts.ExtraArgs) { |
| invocationArgStrs.push_back(extraArg); |
| } |
| } |
| |
| bool ClangImporter::canReadPCH(StringRef PCHFilename) { |
| if (!llvm::sys::fs::exists(PCHFilename)) |
| return false; |
| |
| // FIXME: The following attempts to do an initial ReadAST invocation to verify |
| // the PCH, without affecting the existing CompilerInstance. |
| // Look into combining creating the ASTReader along with verification + update |
| // if necessary, so that we can create and use one ASTReader in the common case |
| // when there is no need for update. |
| |
| CompilerInstance &CI = *Impl.Instance; |
| auto clangDiags = CompilerInstance::createDiagnostics( |
| new clang::DiagnosticOptions()); |
| |
| // Note: Reusing the file manager is safe; this is a component that's already |
| // reused when building PCM files for the module cache. |
| clang::SourceManager clangSrcMgr(*clangDiags, CI.getFileManager()); |
| auto FID = clangSrcMgr.createFileID( |
| llvm::make_unique<ZeroFilledMemoryBuffer>(1, "<main>")); |
| clangSrcMgr.setMainFileID(FID); |
| |
| // Note: Reusing the real HeaderSearch is dangerous, but it's not easy to |
| // copy. We put in some effort to reset it to the way it was below. |
| clang::HeaderSearch &headerSearchInfo = |
| CI.getPreprocessor().getHeaderSearchInfo(); |
| assert(headerSearchInfo.getExternalLookup() == nullptr && |
| "already configured an ASTReader"); |
| |
| // Note: Reusing the PCM cache is safe because that's already shared when |
| // building PCM files for the module cache. Using the top-level compiler |
| // instance as a module loader does seem a little dangerous but does not |
| // appear to cause problems at the moment. |
| clang::Preprocessor PP(CI.getInvocation().getPreprocessorOptsPtr(), |
| *clangDiags, |
| CI.getLangOpts(), |
| clangSrcMgr, |
| headerSearchInfo, |
| (clang::ModuleLoader &)CI, |
| /*IILookup=*/nullptr, |
| /*OwnsHeaderSearch=*/false); |
| PP.Initialize(CI.getTarget()); |
| clang::ASTContext ctx(CI.getLangOpts(), clangSrcMgr, |
| PP.getIdentifierTable(), PP.getSelectorTable(), |
| PP.getBuiltinInfo()); |
| |
| // Note: Reusing the PCHContainerReader or ModuleFileExtensions could be |
| // dangerous. |
| std::unique_ptr<clang::ASTReader> Reader(new clang::ASTReader( |
| PP, CI.getModuleCache(), &ctx, CI.getPCHContainerReader(), |
| CI.getFrontendOpts().ModuleFileExtensions, |
| CI.getHeaderSearchOpts().Sysroot, |
| /*DisableValidation*/ false, |
| /*AllowPCHWithCompilerErrors*/ false, |
| /*AllowConfigurationMismatch*/ false, |
| /*ValidateSystemInputs*/ true)); |
| SWIFT_DEFER { |
| assert(headerSearchInfo.getExternalLookup() == Reader.get() || |
| headerSearchInfo.getExternalLookup() == nullptr); |
| headerSearchInfo.SetExternalLookup(nullptr); |
| headerSearchInfo.SetExternalSource(nullptr); |
| }; |
| ctx.InitBuiltinTypes(CI.getTarget()); |
| |
| auto result = Reader->ReadAST(PCHFilename, |
| clang::serialization::MK_PCH, |
| clang::SourceLocation(), |
| clang::ASTReader::ARR_None); |
| switch (result) { |
| case clang::ASTReader::Success: |
| return true; |
| case clang::ASTReader::Failure: |
| case clang::ASTReader::Missing: |
| case clang::ASTReader::OutOfDate: |
| case clang::ASTReader::VersionMismatch: |
| return false; |
| case clang::ASTReader::ConfigurationMismatch: |
| case clang::ASTReader::HadErrors: |
| assert(0 && "unexpected ASTReader failure for PCH validation"); |
| return false; |
| } |
| llvm_unreachable("unhandled result"); |
| } |
| |
| Optional<std::string> |
| ClangImporter::getPCHFilename(const ClangImporterOptions &ImporterOptions, |
| StringRef SwiftPCHHash, bool &isExplicit) { |
| if (llvm::sys::path::extension(ImporterOptions.BridgingHeader) |
| .endswith(file_types::getExtension(file_types::TY_PCH))) { |
| isExplicit = true; |
| return ImporterOptions.BridgingHeader; |
| } |
| isExplicit = false; |
| |
| const auto &BridgingHeader = ImporterOptions.BridgingHeader; |
| const auto &PCHOutputDir = ImporterOptions.PrecompiledHeaderOutputDir; |
| if (SwiftPCHHash.empty() || BridgingHeader.empty() || PCHOutputDir.empty()) { |
| return None; |
| } |
| |
| SmallString<256> PCHBasename { llvm::sys::path::filename(BridgingHeader) }; |
| llvm::sys::path::replace_extension(PCHBasename, ""); |
| PCHBasename.append("-swift_"); |
| PCHBasename.append(SwiftPCHHash); |
| PCHBasename.append("-clang_"); |
| PCHBasename.append(getClangModuleHash()); |
| PCHBasename.append(".pch"); |
| SmallString<256> PCHFilename { PCHOutputDir }; |
| llvm::sys::path::append(PCHFilename, PCHBasename); |
| return PCHFilename.str().str(); |
| } |
| |
| |
| Optional<std::string> |
| ClangImporter::getOrCreatePCH(const ClangImporterOptions &ImporterOptions, |
| StringRef SwiftPCHHash) { |
| bool isExplicit; |
| auto PCHFilename = getPCHFilename(ImporterOptions, SwiftPCHHash, |
| isExplicit); |
| if (!PCHFilename.hasValue()) { |
| return None; |
| } |
| if (!isExplicit && !ImporterOptions.PCHDisableValidation && |
| !canReadPCH(PCHFilename.getValue())) { |
| StringRef parentDir = llvm::sys::path::parent_path(PCHFilename.getValue()); |
| std::error_code EC = llvm::sys::fs::create_directories(parentDir); |
| if (EC) { |
| llvm::errs() << "failed to create directory '" << parentDir << "': " |
| << EC.message(); |
| return None; |
| } |
| auto FailedToEmit = emitBridgingPCH(ImporterOptions.BridgingHeader, |
| PCHFilename.getValue()); |
| if (FailedToEmit) { |
| return None; |
| } |
| } |
| |
| return PCHFilename.getValue(); |
| } |
| |
| std::unique_ptr<ClangImporter> |
| ClangImporter::create(ASTContext &ctx, |
| const ClangImporterOptions &importerOpts, |
| std::string swiftPCHHash, |
| DependencyTracker *tracker) { |
| std::unique_ptr<ClangImporter> importer{ |
| new ClangImporter(ctx, importerOpts, tracker) |
| }; |
| |
| std::vector<std::string> invocationArgStrs; |
| |
| // Clang expects this to be like an actual command line. So we need to pass in |
| // "clang" for argv[0] |
| invocationArgStrs.push_back("clang"); |
| switch (importerOpts.Mode) { |
| case ClangImporterOptions::Modes::Normal: |
| getNormalInvocationArguments(invocationArgStrs, ctx, importerOpts); |
| break; |
| case ClangImporterOptions::Modes::EmbedBitcode: |
| getEmbedBitcodeInvocationArguments(invocationArgStrs, ctx, importerOpts); |
| break; |
| } |
| addCommonInvocationArguments(invocationArgStrs, ctx, importerOpts); |
| |
| if (importerOpts.DumpClangDiagnostics) { |
| llvm::errs() << "'"; |
| interleave(invocationArgStrs, |
| [](StringRef arg) { llvm::errs() << arg; }, |
| [] { llvm::errs() << "' '"; }); |
| llvm::errs() << "'\n"; |
| } |
| |
| std::vector<const char *> invocationArgs; |
| invocationArgs.reserve(invocationArgStrs.size()); |
| for (auto &argStr : invocationArgStrs) |
| invocationArgs.push_back(argStr.c_str()); |
| |
| if (llvm::sys::path::extension(importerOpts.BridgingHeader) |
| .endswith(file_types::getExtension(file_types::TY_PCH))) { |
| importer->Impl.setSinglePCHImport(importerOpts.BridgingHeader); |
| importer->Impl.IsReadingBridgingPCH = true; |
| if (tracker) { |
| // Currently ignoring dependency on bridging .pch files because they are |
| // temporaries; if and when they are no longer temporaries, this condition |
| // should be removed. |
| auto &coll = static_cast<ClangImporterDependencyCollector &>( |
| *tracker->getClangCollector()); |
| coll.excludePath(importerOpts.BridgingHeader); |
| } |
| } |
| |
| // Create a new Clang compiler invocation. |
| { |
| // Set up a temporary diagnostic client to report errors from parsing the |
| // command line, which may be important for Swift clients if, for example, |
| // they're using -Xcc options. Unfortunately this diagnostic engine has to |
| // use the default options because the /actual/ options haven't been parsed |
| // yet. |
| // |
| // The long-term client for Clang diagnostics is set up below, after the |
| // clang::CompilerInstance is created. |
| llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> tempDiagOpts{ |
| new clang::DiagnosticOptions |
| }; |
| |
| ClangDiagnosticConsumer tempDiagClient{importer->Impl, *tempDiagOpts, |
| importerOpts.DumpClangDiagnostics}; |
| llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> tempClangDiags = |
| clang::CompilerInstance::createDiagnostics(tempDiagOpts.get(), |
| &tempDiagClient, |
| /*owned*/false); |
| |
| importer->Impl.Invocation = |
| clang::createInvocationFromCommandLine(invocationArgs, tempClangDiags); |
| if (!importer->Impl.Invocation) |
| return nullptr; |
| } |
| |
| { |
| // Create an almost-empty memory buffer. |
| auto sourceBuffer = llvm::MemoryBuffer::getMemBuffer( |
| "extern int __swift __attribute__((unavailable));", |
| Implementation::moduleImportBufferName); |
| clang::PreprocessorOptions &ppOpts = |
| importer->Impl.Invocation->getPreprocessorOpts(); |
| ppOpts.addRemappedFile(Implementation::moduleImportBufferName, |
| sourceBuffer.release()); |
| } |
| |
| // Install a Clang module file extension to build Swift name lookup tables. |
| importer->Impl.Invocation->getFrontendOpts().ModuleFileExtensions.push_back( |
| std::make_shared<SwiftNameLookupExtension>( |
| importer->Impl.BridgingHeaderLookupTable, |
| importer->Impl.LookupTables, importer->Impl.SwiftContext, |
| importer->Impl.platformAvailability, |
| importer->Impl.InferImportAsMember)); |
| |
| // Create a compiler instance. |
| { |
| auto PCHContainerOperations = |
| std::make_shared<clang::PCHContainerOperations>(); |
| PCHContainerOperations->registerWriter( |
| llvm::make_unique<clang::ObjectFilePCHContainerWriter>()); |
| PCHContainerOperations->registerReader( |
| llvm::make_unique<clang::ObjectFilePCHContainerReader>()); |
| importer->Impl.Instance.reset( |
| new clang::CompilerInstance(std::move(PCHContainerOperations))); |
| } |
| auto &instance = *importer->Impl.Instance; |
| instance.setInvocation(importer->Impl.Invocation); |
| |
| if (tracker) |
| instance.addDependencyCollector(tracker->getClangCollector()); |
| |
| { |
| // Now set up the real client for Clang diagnostics---configured with proper |
| // options---as opposed to the temporary one we made above. |
| auto actualDiagClient = llvm::make_unique<ClangDiagnosticConsumer>( |
| importer->Impl, instance.getDiagnosticOpts(), |
| importerOpts.DumpClangDiagnostics); |
| instance.createDiagnostics(actualDiagClient.release()); |
| } |
| |
| // Set up the file manager. |
| { |
| if (!ctx.SearchPathOpts.VFSOverlayFiles.empty()) { |
| // If the clang instance has overlays it means the user has provided |
| // -ivfsoverlay options and swift -vfsoverlay options. We're going to |
| // clobber their file system with our own, so warn about it. |
| if (!instance.getHeaderSearchOpts().VFSOverlayFiles.empty()) { |
| ctx.Diags.diagnose(SourceLoc(), diag::clang_vfs_overlay_is_ignored); |
| } |
| instance.setVirtualFileSystem(ctx.SourceMgr.getFileSystem()); |
| } |
| instance.createFileManager(); |
| } |
| |
| // Don't stop emitting messages if we ever can't load a module. |
| // FIXME: This is actually a general problem: any "fatal" error could mess up |
| // the CompilerInvocation when we're not in "show diagnostics after fatal |
| // error" mode. |
| clang::DiagnosticsEngine &clangDiags = instance.getDiagnostics(); |
| clangDiags.setSeverity(clang::diag::err_module_not_found, |
| clang::diag::Severity::Error, |
| clang::SourceLocation()); |
| clangDiags.setSeverity(clang::diag::err_module_not_built, |
| clang::diag::Severity::Error, |
| clang::SourceLocation()); |
| clangDiags.setSuppressAfterFatalError( |
| !ctx.Diags.getShowDiagnosticsAfterFatalError()); |
| |
| |
| // Create the associated action. |
| importer->Impl.Action.reset(new ParsingAction(ctx, *importer, |
| importer->Impl, |
| importerOpts, |
| swiftPCHHash)); |
| auto *action = importer->Impl.Action.get(); |
| |
| // Execute the action. We effectively inline most of |
| // CompilerInstance::ExecuteAction here, because we need to leave the AST |
| // open for future module loading. |
| // FIXME: This has to be cleaned up on the Clang side before we can improve |
| // things here. |
| |
| // Create the target instance. |
| instance.setTarget( |
| clang::TargetInfo::CreateTargetInfo(clangDiags, |
| instance.getInvocation().TargetOpts)); |
| if (!instance.hasTarget()) |
| return nullptr; |
| |
| // Inform the target of the language options. |
| // |
| // FIXME: We shouldn't need to do this, the target should be immutable once |
| // created. This complexity should be lifted elsewhere. |
| instance.getTarget().adjust(instance.getLangOpts()); |
| |
| if (importerOpts.Mode == ClangImporterOptions::Modes::EmbedBitcode) |
| return importer; |
| |
| instance.getLangOpts().NeededByPCHOrCompilationUsesPCH = true; |
| bool canBegin = action->BeginSourceFile(instance, |
| instance.getFrontendOpts().Inputs[0]); |
| if (!canBegin) |
| return nullptr; // there was an error related to the compiler arguments. |
| |
| clang::Preprocessor &clangPP = instance.getPreprocessor(); |
| clangPP.enableIncrementalProcessing(); |
| |
| // Setup Preprocessor callbacks before initialing the parser to make sure |
| // we catch implicit includes. |
| auto ppTracker = llvm::make_unique<BridgingPPTracker>(importer->Impl); |
| clangPP.addPPCallbacks(std::move(ppTracker)); |
| |
| instance.createModuleManager(); |
| |
| // Manually run the action, so that the TU stays open for additional parsing. |
| instance.createSema(action->getTranslationUnitKind(), nullptr); |
| importer->Impl.Parser.reset(new clang::Parser(clangPP, instance.getSema(), |
| /*SkipFunctionBodies=*/false)); |
| |
| clangPP.EnterMainSourceFile(); |
| importer->Impl.Parser->Initialize(); |
| |
| importer->Impl.nameImporter.reset(new NameImporter( |
| importer->Impl.SwiftContext, importer->Impl.platformAvailability, |
| importer->Impl.getClangSema(), importer->Impl.InferImportAsMember)); |
| |
| // FIXME: These decls are not being parsed correctly since (a) some of the |
| // callbacks are still being added, and (b) the logic to parse them has |
| // changed. |
| clang::Parser::DeclGroupPtrTy parsed; |
| while (!importer->Impl.Parser->ParseTopLevelDecl(parsed)) { |
| for (auto *D : parsed.get()) { |
| importer->Impl.addBridgeHeaderTopLevelDecls(D); |
| |
| if (auto named = dyn_cast<clang::NamedDecl>(D)) { |
| addEntryToLookupTable(*importer->Impl.BridgingHeaderLookupTable, named, |
| *importer->Impl.nameImporter); |
| } |
| } |
| } |
| |
| // FIXME: This is missing implicit includes. |
| auto *CB = new HeaderImportCallbacks(importer->Impl); |
| clangPP.addPPCallbacks(std::unique_ptr<clang::PPCallbacks>(CB)); |
| |
| // Create the selectors we'll be looking for. |
| auto &clangContext = importer->Impl.Instance->getASTContext(); |
| importer->Impl.objectAtIndexedSubscript |
| = clangContext.Selectors.getUnarySelector( |
| &clangContext.Idents.get("objectAtIndexedSubscript")); |
| clang::IdentifierInfo *setObjectAtIndexedSubscriptIdents[2] = { |
| &clangContext.Idents.get("setObject"), |
| &clangContext.Idents.get("atIndexedSubscript") |
| }; |
| importer->Impl.setObjectAtIndexedSubscript |
| = clangContext.Selectors.getSelector(2, setObjectAtIndexedSubscriptIdents); |
| importer->Impl.objectForKeyedSubscript |
| = clangContext.Selectors.getUnarySelector( |
| &clangContext.Idents.get("objectForKeyedSubscript")); |
| clang::IdentifierInfo *setObjectForKeyedSubscriptIdents[2] = { |
| &clangContext.Idents.get("setObject"), |
| &clangContext.Idents.get("forKeyedSubscript") |
| }; |
| importer->Impl.setObjectForKeyedSubscript |
| = clangContext.Selectors.getSelector(2, setObjectForKeyedSubscriptIdents); |
| |
| // Set up the imported header module. |
| auto *importedHeaderModule = ModuleDecl::create(ctx.getIdentifier("__ObjC"), ctx); |
| importer->Impl.ImportedHeaderUnit = |
| new (ctx) ClangModuleUnit(*importedHeaderModule, importer->Impl, nullptr); |
| importedHeaderModule->addFile(*importer->Impl.ImportedHeaderUnit); |
| importedHeaderModule->setHasResolvedImports(); |
| |
| importer->Impl.IsReadingBridgingPCH = false; |
| |
| return importer; |
| } |
| |
| bool ClangImporter::addSearchPath(StringRef newSearchPath, bool isFramework, |
| bool isSystem) { |
| clang::FileManager &fileMgr = Impl.Instance->getFileManager(); |
| const clang::DirectoryEntry *entry = fileMgr.getDirectory(newSearchPath); |
| if (!entry) |
| return true; |
| |
| auto &headerSearchInfo = Impl.getClangPreprocessor().getHeaderSearchInfo(); |
| auto exists = std::any_of(headerSearchInfo.search_dir_begin(), |
| headerSearchInfo.search_dir_end(), |
| [&](const clang::DirectoryLookup &lookup) -> bool { |
| if (isFramework) |
| return lookup.getFrameworkDir() == entry; |
| return lookup.getDir() == entry; |
| }); |
| if (exists) { |
| // Don't bother adding a search path that's already there. Clang would have |
| // removed it via deduplication at the time the search path info gets built. |
| return false; |
| } |
| |
| auto kind = isSystem ? clang::SrcMgr::C_System : clang::SrcMgr::C_User; |
| headerSearchInfo.AddSearchPath({entry, kind, isFramework}, |
| /*isAngled=*/true); |
| |
| // In addition to changing the current preprocessor directly, we still need |
| // to change the options structure for future module-building. |
| Impl.Instance->getHeaderSearchOpts().AddPath(newSearchPath, |
| isSystem ? clang::frontend::System : clang::frontend::Angled, |
| isFramework, |
| /*IgnoreSysRoot=*/true); |
| return false; |
| } |
| |
| clang::SourceLocation |
| ClangImporter::Implementation::getNextIncludeLoc() { |
| clang::SourceManager &srcMgr = getClangInstance()->getSourceManager(); |
| |
| if (!DummyIncludeBuffer.isValid()) { |
| clang::SourceLocation includeLoc = |
| srcMgr.getLocForStartOfFile(srcMgr.getMainFileID()); |
| // Picking the beginning of the main FileID as include location is also what |
| // the clang PCH mechanism is doing (see |
| // clang::ASTReader::getImportLocation()). Choose the next source location |
| // here to avoid having the exact same import location as the clang PCH. |
| // Otherwise, if we are using a PCH for bridging header, we'll have |
| // problems with source order comparisons of clang source locations not |
| // being deterministic. |
| includeLoc = includeLoc.getLocWithOffset(1); |
| DummyIncludeBuffer = srcMgr.createFileID( |
| llvm::make_unique<ZeroFilledMemoryBuffer>( |
| 256*1024, StringRef(moduleImportBufferName)), |
| clang::SrcMgr::C_User, /*LoadedID*/0, /*LoadedOffset*/0, includeLoc); |
| } |
| |
| clang::SourceLocation clangImportLoc = |
| srcMgr.getLocForStartOfFile(DummyIncludeBuffer) |
| .getLocWithOffset(IncludeCounter++); |
| assert(srcMgr.isInFileID(clangImportLoc, DummyIncludeBuffer) && |
| "confused Clang's source manager with our fake locations"); |
| return clangImportLoc; |
| } |
| |
| bool ClangImporter::Implementation::importHeader( |
| ModuleDecl *adapter, StringRef headerName, SourceLoc diagLoc, |
| bool trackParsedSymbols, |
| std::unique_ptr<llvm::MemoryBuffer> sourceBuffer, |
| bool implicitImport) { |
| |
| // Don't even try to load the bridging header if the Clang AST is in a bad |
| // state. It could cause a crash. |
| auto &clangDiags = getClangASTContext().getDiagnostics(); |
| if (clangDiags.hasUnrecoverableErrorOccurred()) |
| return true; |
| |
| assert(adapter); |
| ImportedHeaderOwners.push_back(adapter); |
| |
| bool hadError = clangDiags.hasErrorOccurred(); |
| |
| clang::SourceManager &sourceMgr = getClangInstance()->getSourceManager(); |
| clang::FileID bufferID = sourceMgr.createFileID(std::move(sourceBuffer), |
| clang::SrcMgr::C_User, |
| /*LoadedID=*/0, |
| /*LoadedOffset=*/0, |
| getNextIncludeLoc()); |
| auto &consumer = |
| static_cast<HeaderParsingASTConsumer &>(Instance->getASTConsumer()); |
| consumer.reset(); |
| |
| clang::Preprocessor &pp = getClangPreprocessor(); |
| pp.EnterSourceFile(bufferID, /*Dir=*/nullptr, /*Loc=*/{}); |
| // Force the import to occur. |
| pp.LookAhead(0); |
| |
| SmallVector<clang::DeclGroupRef, 16> allParsedDecls; |
| auto handleParsed = [&](clang::DeclGroupRef parsed) { |
| if (trackParsedSymbols) { |
| for (auto *D : parsed) { |
| addBridgeHeaderTopLevelDecls(D); |
| } |
| } |
| |
| allParsedDecls.push_back(parsed); |
| }; |
| |
| clang::Parser::DeclGroupPtrTy parsed; |
| while (!Parser->ParseTopLevelDecl(parsed)) { |
| if (parsed) |
| handleParsed(parsed.get()); |
| for (auto additionalParsedGroup : consumer.getAdditionalParsedDecls()) |
| handleParsed(additionalParsedGroup); |
| consumer.reset(); |
| } |
| |
| // We're trying to discourage (and eventually deprecate) the use of implicit |
| // bridging-header imports triggered by IMPORTED_HEADER blocks in |
| // modules. There are two sub-cases to consider: |
| // |
| // #1 The implicit import actually occurred. |
| // |
| // #2 The user explicitly -import-objc-header'ed some header or PCH that |
| // makes the implicit import redundant. |
| // |
| // It's not obvious how to exactly differentiate these cases given the |
| // interface clang gives us, but we only want to warn on case #1, and the |
| // non-emptiness of allParsedDecls is a _definite_ sign that we're in case |
| // #1. So we treat that as an approximation of the condition we're after, and |
| // accept that we might fail to warn in the odd case where "the import |
| // occurred" but didn't introduce any new decls. |
| // |
| // We also want to limit (for now) the warning in case #1 to invocations that |
| // requested an explicit bridging header, because otherwise the warning will |
| // complain in a very common scenario (unit test w/o bridging header imports |
| // application w/ bridging header) that we don't yet have Xcode automation |
| // to correct. The fix would be explicitly importing on the command line. |
| if (implicitImport && !allParsedDecls.empty() && |
| BridgingHeaderExplicitlyRequested) { |
| SwiftContext.Diags.diagnose( |
| diagLoc, diag::implicit_bridging_header_imported_from_module, |
| llvm::sys::path::filename(headerName), adapter->getName()); |
| } |
| |
| // We can't do this as we're parsing because we may want to resolve naming |
| // conflicts between the things we've parsed. |
| for (auto group : allParsedDecls) |
| for (auto *D : group) |
| if (auto named = dyn_cast<clang::NamedDecl>(D)) |
| addEntryToLookupTable(*BridgingHeaderLookupTable, named, |
| getNameImporter()); |
| |
| pp.EndSourceFile(); |
| bumpGeneration(); |
| |
| // Add any defined macros to the bridging header lookup table. |
| addMacrosToLookupTable(*BridgingHeaderLookupTable, getNameImporter()); |
| |
| // Finish loading any extra modules that were (transitively) imported. |
| handleDeferredImports(); |
| |
| // Wrap all Clang imports under a Swift import decl. |
| for (auto &Import : BridgeHeaderTopLevelImports) { |
| if (auto *ClangImport = Import.dyn_cast<clang::ImportDecl*>()) { |
| Import = createImportDecl(SwiftContext, adapter, ClangImport, {}); |
| } |
| } |
| |
| // Finalize the lookup table, which may fail. |
| finalizeLookupTable(*BridgingHeaderLookupTable, getNameImporter()); |
| |
| // FIXME: What do we do if there was already an error? |
| if (!hadError && clangDiags.hasErrorOccurred()) { |
| SwiftContext.Diags.diagnose(diagLoc, diag::bridging_header_error, |
| headerName); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool ClangImporter::importHeader(StringRef header, ModuleDecl *adapter, |
| off_t expectedSize, time_t expectedModTime, |
| StringRef cachedContents, SourceLoc diagLoc) { |
| clang::FileManager &fileManager = Impl.Instance->getFileManager(); |
| const clang::FileEntry *headerFile = fileManager.getFile(header, |
| /*OpenFile=*/true); |
| if (headerFile && headerFile->getSize() == expectedSize && |
| headerFile->getModificationTime() == expectedModTime) { |
| return importBridgingHeader(header, adapter, diagLoc, false, true); |
| } |
| |
| // If we've made it to here, this is some header other than the bridging |
| // header, which means we can no longer rely on one file's modification time |
| // to invalid code completion caches. :-( |
| Impl.setSinglePCHImport(None); |
| |
| if (!cachedContents.empty() && cachedContents.back() == '\0') |
| cachedContents = cachedContents.drop_back(); |
| std::unique_ptr<llvm::MemoryBuffer> sourceBuffer{ |
| llvm::MemoryBuffer::getMemBuffer(cachedContents, header) |
| }; |
| return Impl.importHeader(adapter, header, diagLoc, /*trackParsedSymbols=*/false, |
| std::move(sourceBuffer), true); |
| } |
| |
| bool ClangImporter::importBridgingHeader(StringRef header, ModuleDecl *adapter, |
| SourceLoc diagLoc, |
| bool trackParsedSymbols, |
| bool implicitImport) { |
| if (llvm::sys::path::extension(header) |
| .endswith(file_types::getExtension(file_types::TY_PCH))) { |
| Impl.ImportedHeaderOwners.push_back(adapter); |
| // We already imported this with -include-pch above, so we should have |
| // collected a bunch of PCH-encoded module imports that we just need to |
| // replay in handleDeferredImports. |
| Impl.handleDeferredImports(); |
| return false; |
| } |
| |
| clang::FileManager &fileManager = Impl.Instance->getFileManager(); |
| const clang::FileEntry *headerFile = fileManager.getFile(header, |
| /*OpenFile=*/true); |
| if (!headerFile) { |
| Impl.SwiftContext.Diags.diagnose(diagLoc, diag::bridging_header_missing, |
| header); |
| return true; |
| } |
| |
| llvm::SmallString<128> importLine; |
| if (Impl.SwiftContext.LangOpts.EnableObjCInterop) |
| importLine = "#import \""; |
| else |
| importLine = "#include \""; |
| |
| importLine += header; |
| importLine += "\"\n"; |
| |
| std::unique_ptr<llvm::MemoryBuffer> sourceBuffer{ |
| llvm::MemoryBuffer::getMemBufferCopy( |
| importLine, Implementation::bridgingHeaderBufferName) |
| }; |
| return Impl.importHeader(adapter, header, diagLoc, trackParsedSymbols, |
| std::move(sourceBuffer), implicitImport); |
| } |
| |
| std::string ClangImporter::getBridgingHeaderContents(StringRef headerPath, |
| off_t &fileSize, |
| time_t &fileModTime) { |
| auto invocation = |
| std::make_shared<clang::CompilerInvocation>(*Impl.Invocation); |
| |
| invocation->getFrontendOpts().DisableFree = false; |
| invocation->getFrontendOpts().Inputs.clear(); |
| invocation->getFrontendOpts().Inputs.push_back( |
| clang::FrontendInputFile(headerPath, clang::InputKind::ObjC)); |
| |
| invocation->getPreprocessorOpts().resetNonModularOptions(); |
| |
| clang::CompilerInstance rewriteInstance( |
| Impl.Instance->getPCHContainerOperations(), |
| &Impl.Instance->getModuleCache()); |
| rewriteInstance.setInvocation(invocation); |
| rewriteInstance.createDiagnostics(new clang::IgnoringDiagConsumer); |
| |
| clang::FileManager &fileManager = Impl.Instance->getFileManager(); |
| rewriteInstance.setFileManager(&fileManager); |
| rewriteInstance.createSourceManager(fileManager); |
| rewriteInstance.setTarget(&Impl.Instance->getTarget()); |
| |
| std::string result; |
| bool success = llvm::CrashRecoveryContext().RunSafelyOnThread([&] { |
| // A much simpler version of clang::RewriteIncludesAction that lets us |
| // write to an in-memory buffer. |
| class RewriteIncludesAction : public clang::PreprocessorFrontendAction { |
| raw_ostream &OS; |
| |
| void ExecuteAction() override { |
| clang::CompilerInstance &compiler = getCompilerInstance(); |
| clang::RewriteIncludesInInput(compiler.getPreprocessor(), &OS, |
| compiler.getPreprocessorOutputOpts()); |
| } |
| public: |
| explicit RewriteIncludesAction(raw_ostream &os) : OS(os) {} |
| }; |
| |
| llvm::raw_string_ostream os(result); |
| RewriteIncludesAction action(os); |
| rewriteInstance.ExecuteAction(action); |
| }); |
| |
| success |= !rewriteInstance.getDiagnostics().hasErrorOccurred(); |
| if (!success) { |
| Impl.SwiftContext.Diags.diagnose({}, |
| diag::could_not_rewrite_bridging_header); |
| return ""; |
| } |
| |
| const clang::FileEntry *fileInfo = fileManager.getFile(headerPath); |
| fileSize = fileInfo->getSize(); |
| fileModTime = fileInfo->getModificationTime(); |
| return result; |
| } |
| |
| bool |
| ClangImporter::emitBridgingPCH(StringRef headerPath, |
| StringRef outputPCHPath) { |
| auto invocation = std::make_shared<clang::CompilerInvocation> |
| (clang::CompilerInvocation(*Impl.Invocation)); |
| invocation->getFrontendOpts().DisableFree = false; |
| invocation->getFrontendOpts().Inputs.clear(); |
| invocation->getFrontendOpts().Inputs.push_back( |
| clang::FrontendInputFile(headerPath, clang::InputKind::ObjC)); |
| invocation->getFrontendOpts().OutputFile = outputPCHPath; |
| invocation->getFrontendOpts().ProgramAction = clang::frontend::GeneratePCH; |
| invocation->getPreprocessorOpts().resetNonModularOptions(); |
| invocation->getLangOpts()->NeededByPCHOrCompilationUsesPCH = true; |
| invocation->getLangOpts()->CacheGeneratedPCH = true; |
| |
| clang::CompilerInstance emitInstance( |
| Impl.Instance->getPCHContainerOperations(), |
| &Impl.Instance->getModuleCache()); |
| emitInstance.setInvocation(std::move(invocation)); |
| emitInstance.createDiagnostics(&Impl.Instance->getDiagnosticClient(), |
| /*ShouldOwnClient=*/false); |
| |
| clang::FileManager &fileManager = Impl.Instance->getFileManager(); |
| emitInstance.setFileManager(&fileManager); |
| emitInstance.createSourceManager(fileManager); |
| emitInstance.setTarget(&Impl.Instance->getTarget()); |
| |
| std::unique_ptr<clang::FrontendAction> action; |
| action.reset(new clang::GeneratePCHAction()); |
| if (!emitInstance.getFrontendOpts().IndexStorePath.empty()) { |
| action = clang::index:: |
| createIndexDataRecordingAction(emitInstance.getFrontendOpts(), |
| std::move(action)); |
| } |
| emitInstance.ExecuteAction(*action); |
| |
| if (emitInstance.getDiagnostics().hasErrorOccurred()) { |
| Impl.SwiftContext.Diags.diagnose({}, |
| diag::bridging_header_pch_error, |
| outputPCHPath, headerPath); |
| return true; |
| } |
| return false; |
| } |
| |
| void ClangImporter::collectSubModuleNames( |
| ArrayRef<std::pair<Identifier, SourceLoc>> path, |
| std::vector<std::string> &names) { |
| auto &clangHeaderSearch = Impl.getClangPreprocessor().getHeaderSearchInfo(); |
| |
| // Look up the top-level module first. |
| clang::Module *clangModule = clangHeaderSearch.lookupModule( |
| path.front().first.str(), /*AllowSearch=*/true, |
| /*AllowExtraModuleMapSearch=*/true); |
| if (!clangModule) |
| return; |
| clang::Module *submodule = clangModule; |
| for (auto component : path.slice(1)) { |
| submodule = submodule->findSubmodule(component.first.str()); |
| if (!submodule) |
| return; |
| } |
| for (auto sub : submodule->submodules()) |
| names.push_back(sub->Name); |
| } |
| |
| bool ClangImporter::isModuleImported(const clang::Module *M) { |
| return M->NameVisibility == clang::Module::NameVisibilityKind::AllVisible; |
| } |
| |
| bool ClangImporter::canImportModule(std::pair<Identifier, SourceLoc> moduleID) { |
| // Look up the top-level module to see if it exists. |
| // FIXME: This only works with top-level modules. |
| auto &clangHeaderSearch = Impl.getClangPreprocessor().getHeaderSearchInfo(); |
| clang::Module *clangModule = |
| clangHeaderSearch.lookupModule(moduleID.first.str(), /*AllowSearch=*/true, |
| /*AllowExtraModuleMapSearch=*/true); |
| if (!clangModule) { |
| return false; |
| } |
| |
| clang::Module::Requirement r; |
| clang::Module::UnresolvedHeaderDirective mh; |
| clang::Module *m; |
| auto &ctx = Impl.getClangASTContext(); |
| return clangModule->isAvailable(ctx.getLangOpts(), getTargetInfo(), r, mh, m); |
| } |
| |
| ModuleDecl *ClangImporter::loadModule( |
| SourceLoc importLoc, |
| ArrayRef<std::pair<Identifier, SourceLoc>> path) { |
| auto &clangContext = Impl.getClangASTContext(); |
| auto &clangHeaderSearch = Impl.getClangPreprocessor().getHeaderSearchInfo(); |
| |
| // Look up the top-level module first, to see if it exists at all. |
| clang::Module *clangModule = clangHeaderSearch.lookupModule( |
| path.front().first.str(), /*AllowSearch=*/true, |
| /*AllowExtraModuleMapSearch=*/true); |
| if (!clangModule) |
| return nullptr; |
| |
| // Convert the Swift import path over to a Clang import path. |
| SmallVector<std::pair<clang::IdentifierInfo *, clang::SourceLocation>, 4> |
| clangPath; |
| for (auto component : path) { |
| clangPath.push_back({ &clangContext.Idents.get(component.first.str()), |
| Impl.exportSourceLoc(component.second) } ); |
| } |
| |
| auto &rawDiagClient = Impl.Instance->getDiagnosticClient(); |
| auto &diagClient = static_cast<ClangDiagnosticConsumer &>(rawDiagClient); |
| |
| auto loadModule = [&](clang::ModuleIdPath path, |
| bool makeVisible) -> clang::ModuleLoadResult { |
| clang::Module::NameVisibilityKind visibility = |
| makeVisible ? clang::Module::AllVisible : clang::Module::Hidden; |
| |
| auto importRAII = diagClient.handleImport(clangPath.front().first, |
| importLoc); |
| |
| std::string preservedIndexStorePathOption; |
| auto &clangFEOpts = Impl.Instance->getFrontendOpts(); |
| if (!clangFEOpts.IndexStorePath.empty()) { |
| StringRef moduleName = path[0].first->getName(); |
| // Ignore the SwiftShims module for the index data. |
| if (moduleName == Impl.SwiftContext.SwiftShimsModuleName.str()) { |
| preservedIndexStorePathOption = clangFEOpts.IndexStorePath; |
| clangFEOpts.IndexStorePath.clear(); |
| } |
| } |
| |
| clang::SourceLocation clangImportLoc = Impl.getNextIncludeLoc(); |
| |
| clang::ModuleLoadResult result = |
| Impl.Instance->loadModule(clangImportLoc, path, visibility, |
| /*IsInclusionDirective=*/false); |
| |
| if (!preservedIndexStorePathOption.empty()) { |
| // Restore the -index-store-path option. |
| clangFEOpts.IndexStorePath = preservedIndexStorePathOption; |
| } |
| |
| if (result && makeVisible) |
| Impl.getClangPreprocessor().makeModuleVisible(result, clangImportLoc); |
| return result; |
| }; |
| |
| // Now load the top-level module, so that we can check if the submodule |
| // exists without triggering a fatal error. |
| clangModule = loadModule(clangPath.front(), false); |
| if (!clangModule) |
| return nullptr; |
| |
| // Verify that the submodule exists. |
| clang::Module *submodule = clangModule; |
| for (auto &component : path.slice(1)) { |
| submodule = submodule->findSubmodule(component.first.str()); |
| |
| // Special case: a submodule named "Foo.Private" can be moved to a top-level |
| // module named "Foo_Private". Clang has special support for this. |
| // We're limiting this to just submodules named "Private" because this will |
| // put the Clang AST in a fatal error state if it /doesn't/ exist. |
| if (!submodule && component.first.str() == "Private" && |
| (&component) == (&path[1])) { |
| submodule = loadModule(llvm::makeArrayRef(clangPath).slice(0, 2), false); |
| } |
| |
| if (!submodule) { |
| // FIXME: Specialize the error for a missing submodule? |
| return nullptr; |
| } |
| } |
| |
| // Finally, load the submodule and make it visible. |
| clangModule = loadModule(clangPath, true); |
| if (!clangModule) |
| return nullptr; |
| |
| return Impl.finishLoadingClangModule(clangModule, |
| /*preferAdapter=*/false); |
| } |
| |
| ModuleDecl *ClangImporter::Implementation::finishLoadingClangModule( |
| const clang::Module *clangModule, |
| bool findAdapter) { |
| assert(clangModule); |
| |
| // Bump the generation count. |
| bumpGeneration(); |
| |
| auto &cacheEntry = ModuleWrappers[clangModule]; |
| ModuleDecl *result; |
| ClangModuleUnit *wrapperUnit; |
| if ((wrapperUnit = cacheEntry.getPointer())) { |
| result = wrapperUnit->getParentModule(); |
| if (!cacheEntry.getInt()) { |
| // Force load adapter modules for all imported modules. |
| // FIXME: This forces the creation of wrapper modules for all imports as |
| // well, and may do unnecessary work. |
| cacheEntry.setInt(true); |
| result->forAllVisibleModules({}, [&](ModuleDecl::ImportedModule import) {}); |
| } |
| } else { |
| // Build the representation of the Clang module in Swift. |
| // FIXME: The name of this module could end up as a key in the ASTContext, |
| // but that's not correct for submodules. |
| Identifier name = SwiftContext.getIdentifier((*clangModule).Name); |
| result = ModuleDecl::create(name, SwiftContext); |
| // Silence error messages about testably importing a Clang module. |
| result->setTestingEnabled(); |
| result->setHasResolvedImports(); |
| |
| wrapperUnit = |
| new (SwiftContext) ClangModuleUnit(*result, *this, clangModule); |
| result->addFile(*wrapperUnit); |
| cacheEntry.setPointerAndInt(wrapperUnit, true); |
| |
| // Force load adapter modules for all imported modules. |
| // FIXME: This forces the creation of wrapper modules for all imports as |
| // well, and may do unnecessary work. |
| result->forAllVisibleModules({}, [](ModuleDecl::ImportedModule import) {}); |
| } |
| |
| if (clangModule->isSubModule()) { |
| finishLoadingClangModule(clangModule->getTopLevelModule(), true); |
| } else { |
| ModuleDecl *&loaded = SwiftContext.LoadedModules[result->getName()]; |
| if (!loaded) |
| loaded = result; |
| } |
| |
| if (findAdapter) |
| if (ModuleDecl *adapter = wrapperUnit->getAdapterModule()) |
| result = adapter; |
| |
| return result; |
| } |
| |
| // Run through the set of deferred imports -- either those referenced by |
| // submodule ID from a bridging PCH, or those already loaded as clang::Modules |
| // in response to an import directive in a bridging header -- and call |
| // finishLoadingClangModule on each. |
| void ClangImporter::Implementation::handleDeferredImports() |
| { |
| clang::ASTReader &R = *Instance->getModuleManager(); |
| llvm::SmallSet<clang::serialization::SubmoduleID, 32> seenSubmodules; |
| for (clang::serialization::SubmoduleID ID : PCHImportedSubmodules) { |
| if (!seenSubmodules.insert(ID).second) |
| continue; |
| ImportedHeaderExports.push_back(R.getSubmodule(ID)); |
| } |
| PCHImportedSubmodules.clear(); |
| for (const clang::Module *M : ImportedHeaderExports) |
| (void)finishLoadingClangModule(M, /*preferAdapter=*/true); |
| } |
| |
| ModuleDecl *ClangImporter::getImportedHeaderModule() const { |
| return Impl.ImportedHeaderUnit->getParentModule(); |
| } |
| |
| PlatformAvailability::PlatformAvailability(LangOptions &langOpts) |
| : platformKind(targetPlatform(langOpts)) { |
| switch (platformKind) { |
| case PlatformKind::iOS: |
| case PlatformKind::iOSApplicationExtension: |
| case PlatformKind::tvOS: |
| case PlatformKind::tvOSApplicationExtension: |
| deprecatedAsUnavailableMessage = |
| "APIs deprecated as of iOS 7 and earlier are unavailable in Swift"; |
| break; |
| |
| case PlatformKind::watchOS: |
| case PlatformKind::watchOSApplicationExtension: |
| deprecatedAsUnavailableMessage = ""; |
| break; |
| |
| case PlatformKind::OSX: |
| case PlatformKind::OSXApplicationExtension: |
| deprecatedAsUnavailableMessage = |
| "APIs deprecated as of macOS 10.9 and earlier are unavailable in Swift"; |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| bool PlatformAvailability::isPlatformRelevant(StringRef name) const { |
| switch (platformKind) { |
| case PlatformKind::OSX: |
| return name == "macos"; |
| case PlatformKind::OSXApplicationExtension: |
| return name == "macos" || name == "macos_app_extension"; |
| |
| case PlatformKind::iOS: |
| return name == "ios"; |
| case PlatformKind::iOSApplicationExtension: |
| return name == "ios" || name == "ios_app_extension"; |
| |
| case PlatformKind::tvOS: |
| return name == "tvos"; |
| case PlatformKind::tvOSApplicationExtension: |
| return name == "tvos" || name == "tvos_app_extension"; |
| |
| case PlatformKind::watchOS: |
| return name == "watchos"; |
| case PlatformKind::watchOSApplicationExtension: |
| return name == "watchos" || name == "watchos_app_extension"; |
| |
| case PlatformKind::none: |
| return false; |
| } |
| |
| llvm_unreachable("Unexpected platform"); |
| } |
| |
| bool PlatformAvailability::treatDeprecatedAsUnavailable( |
| const clang::Decl *clangDecl, const llvm::VersionTuple &version) const { |
| assert(!version.empty() && "Must provide version when deprecated"); |
| unsigned major = version.getMajor(); |
| Optional<unsigned> minor = version.getMinor(); |
| |
| switch (platformKind) { |
| case PlatformKind::OSX: |
| // Anything deprecated in OSX 10.9.x and earlier is unavailable in Swift. |
| return major < 10 || |
| (major == 10 && (!minor.hasValue() || minor.getValue() <= 9)); |
| |
| case PlatformKind::iOS: |
| case PlatformKind::iOSApplicationExtension: |
| case PlatformKind::tvOS: |
| case PlatformKind::tvOSApplicationExtension: |
| // Anything deprecated in iOS 7.x and earlier is unavailable in Swift. |
| return major <= 7; |
| |
| case PlatformKind::watchOS: |
| case PlatformKind::watchOSApplicationExtension: |
| // No deprecation filter on watchOS |
| return false; |
| |
| default: |
| return false; |
| } |
| } |
| |
| ClangImporter::Implementation::Implementation(ASTContext &ctx, |
| const ClangImporterOptions &opts) |
| : SwiftContext(ctx), |
| ImportForwardDeclarations(opts.ImportForwardDeclarations), |
| InferImportAsMember(opts.InferImportAsMember), |
| DisableSwiftBridgeAttr(opts.DisableSwiftBridgeAttr), |
| BridgingHeaderExplicitlyRequested(!opts.BridgingHeader.empty()), |
| DisableAdapterModules(opts.DisableAdapterModules), |
| IsReadingBridgingPCH(false), |
| CurrentVersion(ImportNameVersion::fromOptions(ctx.LangOpts)), |
| BridgingHeaderLookupTable(new SwiftLookupTable(nullptr)), |
| platformAvailability(ctx.LangOpts), |
| nameImporter() {} |
| |
| ClangImporter::Implementation::~Implementation() { |
| #ifndef NDEBUG |
| SwiftContext.SourceMgr.verifyAllBuffers(); |
| #endif |
| } |
| |
| ClangModuleUnit *ClangImporter::Implementation::getWrapperForModule( |
| const clang::Module *underlying) { |
| auto &cacheEntry = ModuleWrappers[underlying]; |
| if (ClangModuleUnit *cached = cacheEntry.getPointer()) |
| return cached; |
| |
| // FIXME: Handle hierarchical names better. |
| Identifier name = SwiftContext.getIdentifier(underlying->Name); |
| auto wrapper = ModuleDecl::create(name, SwiftContext); |
| // Silence error messages about testably importing a Clang module. |
| wrapper->setTestingEnabled(); |
| wrapper->setHasResolvedImports(); |
| |
| auto file = new (SwiftContext) ClangModuleUnit(*wrapper, *this, |
| underlying); |
| wrapper->addFile(*file); |
| cacheEntry.setPointer(file); |
| |
| return file; |
| } |
| |
| ClangModuleUnit *ClangImporter::Implementation::getClangModuleForDecl( |
| const clang::Decl *D, |
| bool allowForwardDeclaration) { |
| auto maybeModule = getClangSubmoduleForDecl(D, allowForwardDeclaration); |
| if (!maybeModule) |
| return nullptr; |
| if (!maybeModule.getValue()) |
| return ImportedHeaderUnit; |
| |
| // Get the parent module because currently we don't represent submodules with |
| // ClangModuleUnit. |
| auto *M = maybeModule.getValue()->getTopLevelModule(); |
| |
| return getWrapperForModule(M); |
| } |
| |
| #pragma mark Source locations |
| clang::SourceLocation |
| ClangImporter::Implementation::exportSourceLoc(SourceLoc loc) { |
| // FIXME: Implement! |
| return clang::SourceLocation(); |
| } |
| |
| SourceLoc |
| ClangImporter::Implementation::importSourceLoc(clang::SourceLocation loc) { |
| // FIXME: Implement! |
| return SourceLoc(); |
| } |
| |
| SourceRange |
| ClangImporter::Implementation::importSourceRange(clang::SourceRange loc) { |
| // FIXME: Implement! |
| return SourceRange(); |
| } |
| |
| #pragma mark Importing names |
| |
| clang::DeclarationName |
| ClangImporter::Implementation::exportName(Identifier name) { |
| // FIXME: When we start dealing with C++, we can map over some operator |
| // names. |
| if (name.empty() || name.isOperator()) |
| return clang::DeclarationName(); |
| |
| // Map the identifier. If it's some kind of keyword, it can't be mapped. |
| auto ident = &Instance->getASTContext().Idents.get(name.str()); |
| if (ident->getTokenID() != clang::tok::identifier) |
| return clang::DeclarationName(); |
| |
| return ident; |
| } |
| |
| Identifier |
| ClangImporter::Implementation::importIdentifier( |
| const clang::IdentifierInfo *identifier, |
| StringRef removePrefix) |
| { |
| if (!identifier) return Identifier(); |
| |
| StringRef name = identifier->getName(); |
| // Remove the prefix, if any. |
| if (!removePrefix.empty()) { |
| if (name.startswith(removePrefix)) { |
| name = name.slice(removePrefix.size(), name.size()); |
| } |
| } |
| |
| // Get the Swift identifier. |
| return SwiftContext.getIdentifier(name); |
| } |
| |
| ObjCSelector ClangImporter::Implementation::importSelector( |
| clang::Selector selector) { |
| auto &ctx = SwiftContext; |
| |
| // Handle zero-argument selectors directly. |
| if (selector.isUnarySelector()) { |
| Identifier name; |
| if (auto id = selector.getIdentifierInfoForSlot(0)) |
| name = ctx.getIdentifier(id->getName()); |
| return ObjCSelector(ctx, 0, name); |
| } |
| |
| SmallVector<Identifier, 2> pieces; |
| for (auto i = 0u, n = selector.getNumArgs(); i != n; ++i) { |
| Identifier piece; |
| if (auto id = selector.getIdentifierInfoForSlot(i)) |
| piece = ctx.getIdentifier(id->getName()); |
| pieces.push_back(piece); |
| } |
| |
| return ObjCSelector(ctx, pieces.size(), pieces); |
| } |
| |
| clang::Selector |
| ClangImporter::Implementation::exportSelector(DeclName name, |
| bool allowSimpleName) { |
| if (!allowSimpleName && name.isSimpleName()) |
| return {}; |
| |
| clang::ASTContext &ctx = getClangASTContext(); |
| |
| SmallVector<clang::IdentifierInfo *, 8> pieces; |
| pieces.push_back(exportName(name.getBaseIdentifier()).getAsIdentifierInfo()); |
| |
| auto argNames = name.getArgumentNames(); |
| if (argNames.empty()) |
| return ctx.Selectors.getNullarySelector(pieces.front()); |
| |
| if (!argNames.front().empty()) |
| return {}; |
| argNames = argNames.slice(1); |
| |
| for (Identifier argName : argNames) |
| pieces.push_back(exportName(argName).getAsIdentifierInfo()); |
| |
| return ctx.Selectors.getSelector(pieces.size(), pieces.data()); |
| } |
| |
| clang::Selector |
| ClangImporter::Implementation::exportSelector(ObjCSelector selector) { |
| SmallVector<clang::IdentifierInfo *, 4> pieces; |
| for (auto piece : selector.getSelectorPieces()) |
| pieces.push_back(exportName(piece).getAsIdentifierInfo()); |
| return getClangASTContext().Selectors.getSelector(selector.getNumArgs(), |
| pieces.data()); |
| } |
| |
| /// Determine whether the given method potentially conflicts with the |
| /// setter for a property in the given protocol. |
| static bool |
| isPotentiallyConflictingSetter(const clang::ObjCProtocolDecl *proto, |
| const clang::ObjCMethodDecl *method) { |
| auto sel = method->getSelector(); |
| if (sel.getNumArgs() != 1) |
| return false; |
| |
| clang::IdentifierInfo *setterID = sel.getIdentifierInfoForSlot(0); |
| if (!setterID || !setterID->getName().startswith("set")) |
| return false; |
| |
| for (auto *prop : proto->properties()) { |
| if (prop->getSetterName() == sel) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool importer::shouldSuppressDeclImport(const clang::Decl *decl) { |
| if (auto objcMethod = dyn_cast<clang::ObjCMethodDecl>(decl)) { |
| // First check if we're actually in a Swift class. |
| auto dc = decl->getDeclContext(); |
| if (hasNativeSwiftDecl(cast<clang::ObjCContainerDecl>(dc))) |
| return true; |
| |
| // If this member is a method that is a getter or setter for a |
| // property, don't add it into the table. property names and |
| // getter names (by choosing to only have a property). |
| // |
| // Note that this is suppressed for certain accessibility declarations, |
| // which are imported as getter/setter pairs and not properties. |
| if (objcMethod->isPropertyAccessor()) { |
| // Suppress the import of this method when the corresponding |
| // property is not suppressed. |
| return !shouldSuppressDeclImport( |
| objcMethod->findPropertyDecl(/*CheckOverrides=*/false)); |
| } |
| |
| // If the method was declared within a protocol, check that it |
| // does not conflict with the setter of a property. |
| if (auto proto = dyn_cast<clang::ObjCProtocolDecl>(dc)) |
| return isPotentiallyConflictingSetter(proto, objcMethod); |
| |
| |
| return false; |
| } |
| |
| if (auto objcProperty = dyn_cast<clang::ObjCPropertyDecl>(decl)) { |
| // First check if we're actually in a Swift class. |
| auto dc = objcProperty->getDeclContext(); |
| if (hasNativeSwiftDecl(cast<clang::ObjCContainerDecl>(dc))) |
| return true; |
| |
| // Suppress certain properties; import them as getter/setter pairs instead. |
| if (shouldImportPropertyAsAccessors(objcProperty)) |
| return true; |
| |
| // Check whether there is a superclass method for the getter that |
| // is *not* suppressed, in which case we will need to suppress |
| // this property. |
| auto objcClass = dyn_cast<clang::ObjCInterfaceDecl>(dc); |
| if (!objcClass) { |
| if (auto objcCategory = dyn_cast<clang::ObjCCategoryDecl>(dc)) { |
| // If the enclosing category is invalid, suppress this declaration. |
| if (objcCategory->isInvalidDecl()) return true; |
| |
| objcClass = objcCategory->getClassInterface(); |
| } |
| } |
| |
| if (objcClass) { |
| if (auto objcSuperclass = objcClass->getSuperClass()) { |
| auto getterMethod = |
| objcSuperclass->lookupMethod(objcProperty->getGetterName(), |
| objcProperty->isInstanceProperty()); |
| if (getterMethod && !shouldSuppressDeclImport(getterMethod)) |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| return false; |
| } |
| |
| #pragma mark Name lookup |
| const clang::TypedefNameDecl * |
| ClangImporter::Implementation::lookupTypedef(clang::DeclarationName name) { |
| clang::Sema &sema = Instance->getSema(); |
| clang::LookupResult lookupResult(sema, name, |
| clang::SourceLocation(), |
| clang::Sema::LookupOrdinaryName); |
| |
| if (sema.LookupName(lookupResult, /*scope=*/nullptr)) { |
| for (auto decl : lookupResult) { |
| if (auto typedefDecl = |
| dyn_cast<clang::TypedefNameDecl>(decl->getUnderlyingDecl())) |
| return typedefDecl; |
| } |
| } |
| |
| return nullptr; |
| } |
| |
| static bool isDeclaredInModule(const ClangModuleUnit *ModuleFilter, |
| const Decl *VD) { |
| auto ContainingUnit = VD->getDeclContext()->getModuleScopeContext(); |
| return ModuleFilter == ContainingUnit; |
| } |
| |
| static const clang::Module * |
| getClangOwningModule(ClangNode Node, const clang::ASTContext &ClangCtx) { |
| assert(!Node.getAsModule() && "not implemented for modules"); |
| |
| if (const clang::Decl *D = Node.getAsDecl()) { |
| auto ExtSource = ClangCtx.getExternalSource(); |
| assert(ExtSource); |
| return ExtSource->getModule(D->getOwningModuleID()); |
| } |
| |
| if (const clang::ModuleMacro *M = Node.getAsModuleMacro()) |
| return M->getOwningModule(); |
| |
| // A locally-defined MacroInfo does not have an owning module. |
| assert(Node.getAsMacroInfo()); |
| return nullptr; |
| } |
| |
| static const clang::Module * |
| getClangTopLevelOwningModule(ClangNode Node, |
| const clang::ASTContext &ClangCtx) { |
| const clang::Module *OwningModule = getClangOwningModule(Node, ClangCtx); |
| if (!OwningModule) |
| return nullptr; |
| return OwningModule->getTopLevelModule(); |
| } |
| |
| static bool isVisibleFromModule(const ClangModuleUnit *ModuleFilter, |
| ValueDecl *VD) { |
| assert(ModuleFilter); |
| |
| auto ContainingUnit = VD->getDeclContext()->getModuleScopeContext(); |
| if (ModuleFilter == ContainingUnit) |
| return true; |
| |
| // The rest of this function is looking to see if the Clang entity that |
| // caused VD to be imported has redeclarations in the filter module. |
| auto Wrapper = dyn_cast<ClangModuleUnit>(ContainingUnit); |
| if (!Wrapper) |
| return false; |
| |
| auto ClangNode = VD->getClangNode(); |
| if (!ClangNode) { |
| // If we synthesized a ValueDecl, it won't have a Clang node. Find the |
| // associated declaration that /does/ have a Clang node, and use that. |
| auto *SynthesizedTypeAttr = |
| VD->getAttrs().getAttribute<ClangImporterSynthesizedTypeAttr>(); |
| assert(SynthesizedTypeAttr); |
| |
| switch (SynthesizedTypeAttr->getKind()) { |
| case ClangImporterSynthesizedTypeAttr::Kind::NSErrorWrapper: |
| case ClangImporterSynthesizedTypeAttr::Kind::NSErrorWrapperAnon: { |
| ASTContext &Ctx = ContainingUnit->getASTContext(); |
| auto LookupFlags = |
| NominalTypeDecl::LookupDirectFlags::IgnoreNewExtensions; |
| auto WrapperStruct = cast<StructDecl>(VD); |
| TinyPtrVector<ValueDecl *> LookupResults = |
| WrapperStruct->lookupDirect(Ctx.Id_Code, LookupFlags); |
| assert(!LookupResults.empty() && "imported error enum without Code"); |
| |
| auto CodeEnumIter = llvm::find_if(LookupResults, |
| [&](ValueDecl *Member) -> bool { |
| return Member->getDeclContext() == WrapperStruct; |
| }); |
| assert(CodeEnumIter != LookupResults.end() && |
| "could not find Code enum in wrapper struct"); |
| assert((*CodeEnumIter)->hasClangNode()); |
| ClangNode = (*CodeEnumIter)->getClangNode(); |
| break; |
| } |
| } |
| } |
| |
| // Macros can be "redeclared" by putting an equivalent definition in two |
| // different modules. (We don't actually check the equivalence.) |
| // FIXME: We're also not checking if the redeclaration is in /this/ module. |
| if (ClangNode.getAsMacro()) |
| return true; |
| |
| const clang::Decl *D = ClangNode.castAsDecl(); |
| auto &ClangASTContext = ModuleFilter->getClangASTContext(); |
| |
| // We don't handle Clang submodules; pop everything up to the top-level |
| // module. |
| auto OwningClangModule = getClangTopLevelOwningModule(ClangNode, |
| ClangASTContext); |
| if (OwningClangModule == ModuleFilter->getClangModule()) |
| return true; |
| |
| // Handle redeclarable Clang decls by checking each redeclaration. |
| bool IsTagDecl = isa<clang::TagDecl>(D); |
| if (!(IsTagDecl || isa<clang::FunctionDecl>(D) || isa<clang::VarDecl>(D) || |
| isa<clang::TypedefNameDecl>(D))) { |
| return false; |
| } |
| |
| for (auto Redeclaration : D->redecls()) { |
| if (Redeclaration == D) |
| continue; |
| |
| // For enums, structs, and unions, only count definitions when looking to |
| // see what other modules they appear in. |
| if (IsTagDecl) |
| if (!cast<clang::TagDecl>(Redeclaration)->isCompleteDefinition()) |
| continue; |
| |
| auto OwningClangModule = getClangTopLevelOwningModule(Redeclaration, |
| ClangASTContext); |
| if (OwningClangModule == ModuleFilter->getClangModule()) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| |
| namespace { |
| class ClangVectorDeclConsumer : public clang::VisibleDeclConsumer { |
| std::vector<clang::NamedDecl *> results; |
| public: |
| ClangVectorDeclConsumer() = default; |
| |
| void FoundDecl(clang::NamedDecl *ND, clang::NamedDecl *Hiding, |
| clang::DeclContext *Ctx, bool InBaseClass) override { |
| if (!ND->getIdentifier()) |
| return; |
| |
| if (ND->isModulePrivate()) |
| return; |
| |
| results.push_back(ND); |
| } |
| |
| llvm::MutableArrayRef<clang::NamedDecl *> getResults() { |
| return results; |
| } |
| }; |
| |
| class FilteringVisibleDeclConsumer : public swift::VisibleDeclConsumer { |
| swift::VisibleDeclConsumer &NextConsumer; |
| const ClangModuleUnit *ModuleFilter; |
| |
| public: |
| FilteringVisibleDeclConsumer(swift::VisibleDeclConsumer &consumer, |
| const ClangModuleUnit *CMU) |
| : NextConsumer(consumer), ModuleFilter(CMU) { |
| assert(CMU); |
| } |
| |
| void foundDecl(ValueDecl *VD, DeclVisibilityKind Reason) override { |
| if (isVisibleFromModule(ModuleFilter, VD)) |
| NextConsumer.foundDecl(VD, Reason); |
| } |
| }; |
| |
| class FilteringDeclaredDeclConsumer : public swift::VisibleDeclConsumer { |
| swift::VisibleDeclConsumer &NextConsumer; |
| const ClangModuleUnit *ModuleFilter; |
| |
| public: |
| FilteringDeclaredDeclConsumer(swift::VisibleDeclConsumer &consumer, |
| const ClangModuleUnit *CMU) |
| : NextConsumer(consumer), ModuleFilter(CMU) { |
| assert(CMU); |
| } |
| |
| void foundDecl(ValueDecl *VD, DeclVisibilityKind Reason) override { |
| if (isDeclaredInModule(ModuleFilter, VD)) |
| NextConsumer.foundDecl(VD, Reason); |
| } |
| }; |
| |
| /// A hack to hide particular types in the "Darwin" module on Apple platforms. |
| class DarwinLegacyFilterDeclConsumer : public swift::VisibleDeclConsumer { |
| swift::VisibleDeclConsumer &NextConsumer; |
| clang::ASTContext &ClangASTContext; |
| |
| bool shouldDiscard(ValueDecl *VD) { |
| if (!VD->hasClangNode()) |
| return false; |
| |
| const clang::Module *clangModule = getClangOwningModule(VD->getClangNode(), |
| ClangASTContext); |
| if (!clangModule) |
| return false; |
| |
| if (clangModule->Name == "MacTypes") { |
| if (!VD->hasName() || VD->getBaseName().isSpecial()) |
| return true; |
| return llvm::StringSwitch<bool>(VD->getBaseName().getIdentifier().str()) |
| .Cases("OSErr", "OSStatus", "OptionBits", false) |
| .Cases("FourCharCode", "OSType", false) |
| .Case("Boolean", false) |
| .Case("kUnknownType", false) |
| .Cases("UTF32Char", "UniChar", "UTF16Char", "UTF8Char", false) |
| .Case("ProcessSerialNumber", false) |
| .Default(true); |
| } |
| |
| if (clangModule->Parent && |
| clangModule->Parent->Name == "CarbonCore") { |
| return llvm::StringSwitch<bool>(clangModule->Name) |
| .Cases("BackupCore", "DiskSpaceRecovery", "MacErrors", false) |
| .Case("UnicodeUtilities", false) |
| .Default(true); |
| } |
| |
| if (clangModule->Parent && |
| clangModule->Parent->Name == "OSServices") { |
| // Note that this is a list of things to /drop/ rather than to /keep/. |
| // We're more likely to see new, modern headers added to OSServices. |
| return llvm::StringSwitch<bool>(clangModule->Name) |
| .Cases("IconStorage", "KeychainCore", "Power", true) |
| .Cases("SecurityCore", "SystemSound", true) |
| .Cases("WSMethodInvocation", "WSProtocolHandler", "WSTypes", true) |
| .Default(false); |
| } |
| |
| return false; |
| } |
| |
| public: |
| DarwinLegacyFilterDeclConsumer(swift::VisibleDeclConsumer &consumer, |
| clang::ASTContext &clangASTContext) |
| : NextConsumer(consumer), ClangASTContext(clangASTContext) {} |
| |
| static bool needsFiltering(const clang::Module *topLevelModule) { |
| return topLevelModule && (topLevelModule->Name == "Darwin" || |
| topLevelModule->Name == "CoreServices"); |
| } |
| |
| void foundDecl(ValueDecl *VD, DeclVisibilityKind Reason) override { |
| if (!shouldDiscard(VD)) |
| NextConsumer.foundDecl(VD, Reason); |
| } |
| }; |
| |
| } // unnamed namespace |
| |
| /// Translate a MacroDefinition to a ClangNode, either a ModuleMacro for |
| /// a definition imported from a module or a MacroInfo for a macro defined |
| /// locally. |
| ClangNode getClangNodeForMacroDefinition(clang::MacroDefinition &M) { |
| if (!M.getModuleMacros().empty()) |
| return ClangNode(M.getModuleMacros().back()->getMacroInfo()); |
| if (auto *MD = M.getLocalDirective()) |
| return ClangNode(MD->getMacroInfo()); |
| return ClangNode(); |
| } |
| |
| void ClangImporter::lookupBridgingHeaderDecls( |
| llvm::function_ref<bool(ClangNode)> filter, |
| llvm::function_ref<void(Decl*)> receiver) const { |
| for (auto &Import : Impl.BridgeHeaderTopLevelImports) { |
| auto ImportD = Import.get<ImportDecl*>(); |
| if (filter(ImportD->getClangDecl())) |
| receiver(ImportD); |
| } |
| for (auto *ClangD : Impl.BridgeHeaderTopLevelDecls) { |
| if (filter(ClangD)) { |
| if (auto *ND = dyn_cast<clang::NamedDecl>(ClangD)) { |
| if (Decl *imported = Impl.importDeclReal(ND, Impl.CurrentVersion)) |
| receiver(imported); |
| } |
| } |
| } |
| |
| auto &ClangPP = Impl.getClangPreprocessor(); |
| for (clang::IdentifierInfo *II : Impl.BridgeHeaderMacros) { |
| auto MD = ClangPP.getMacroDefinition(II); |
| if (auto macroNode = getClangNodeForMacroDefinition(MD)) { |
| if (filter(macroNode)) { |
| auto MI = macroNode.getAsMacro(); |
| Identifier Name = Impl.getNameImporter().importMacroName(II, MI); |
| if (Decl *imported = Impl.importMacro(Name, macroNode)) |
| receiver(imported); |
| } |
| } |
| } |
| } |
| |
| bool ClangImporter::lookupDeclsFromHeader(StringRef Filename, |
| llvm::function_ref<bool(ClangNode)> filter, |
| llvm::function_ref<void(Decl*)> receiver) const { |
| const clang::FileEntry *File = |
| getClangPreprocessor().getFileManager().getFile(Filename); |
| if (!File) |
| return true; |
| |
| auto &ClangCtx = getClangASTContext(); |
| auto &ClangSM = ClangCtx.getSourceManager(); |
| auto &ClangPP = getClangPreprocessor(); |
| |
| // Look up the header in the includes of the bridging header. |
| if (Impl.BridgeHeaderFiles.count(File)) { |
| auto headerFilter = [&](ClangNode ClangN) -> bool { |
| if (ClangN.isNull()) |
| return false; |
| |
| auto ClangLoc = ClangSM.getFileLoc(ClangN.getLocation()); |
| if (ClangLoc.isInvalid()) |
| return false; |
| |
| if (ClangSM.getFileEntryForID(ClangSM.getFileID(ClangLoc)) != File) |
| return false; |
| |
| return filter(ClangN); |
| }; |
| |
| lookupBridgingHeaderDecls(headerFilter, receiver); |
| return false; |
| } |
| |
| clang::FileID FID = ClangSM.translateFile(File); |
| if (FID.isInvalid()) |
| return false; |
| |
| // Look up the header in the ASTReader. |
| if (ClangSM.isLoadedFileID(FID)) { |
| // Decls. |
| SmallVector<clang::Decl *, 32> Decls; |
| unsigned Length = ClangSM.getFileIDSize(FID); |
| ClangCtx.getExternalSource()->FindFileRegionDecls(FID, 0, Length, Decls); |
| for (auto *ClangD : Decls) { |
| if (Impl.shouldIgnoreBridgeHeaderTopLevelDecl(ClangD)) |
| continue; |
| if (filter(ClangD)) { |
| if (auto *ND = dyn_cast<clang::NamedDecl>(ClangD)) { |
| if (Decl *imported = Impl.importDeclReal(ND, Impl.CurrentVersion)) |
| receiver(imported); |
| } |
| } |
| } |
| |
| // Macros. |
| if (auto *ppRec = ClangPP.getPreprocessingRecord()) { |
| clang::SourceLocation B = ClangSM.getLocForStartOfFile(FID); |
| clang::SourceLocation E = ClangSM.getLocForEndOfFile(FID); |
| clang::SourceRange R(B, E); |
| const auto &Entities = ppRec->getPreprocessedEntitiesInRange(R); |
| for (auto I = Entities.begin(), E = Entities.end(); I != E; ++I) { |
| if (!ppRec->isEntityInFileID(I, FID)) |
| continue; |
| clang::PreprocessedEntity *PPE = *I; |
| if (!PPE) |
| continue; |
| if (auto *MDR = dyn_cast<clang::MacroDefinitionRecord>(PPE)) { |
| auto *II = const_cast<clang::IdentifierInfo*>(MDR->getName()); |
| auto MD = ClangPP.getMacroDefinition(II); |
| if (auto macroNode = getClangNodeForMacroDefinition(MD)) { |
| if (filter(macroNode)) { |
| auto MI = macroNode.getAsMacro(); |
| Identifier Name = Impl.getNameImporter().importMacroName(II, MI); |
| if (Decl *imported = Impl.importMacro(Name, macroNode)) |
| receiver(imported); |
| } |
| } |
| } |
| } |
| |
| // FIXME: Module imports inside that header. |
| } |
| return false; |
| } |
| |
| return true; // no info found about that header. |
| } |
| |
| void ClangImporter::lookupValue(DeclName name, VisibleDeclConsumer &consumer){ |
| Impl.forEachLookupTable([&](SwiftLookupTable &table) -> bool { |
| Impl.lookupValue(table, name, consumer); |
| return false; |
| }); |
| } |
| |
| void |
| ClangImporter::lookupTypeDecl(StringRef rawName, ClangTypeKind kind, |
| llvm::function_ref<void(TypeDecl*)> receiver) { |
| clang::DeclarationName clangName( |
| &Impl.Instance->getASTContext().Idents.get(rawName)); |
| |
| clang::Sema::LookupNameKind lookupKind; |
| switch (kind) { |
| case ClangTypeKind::Typedef: |
| lookupKind = clang::Sema::LookupOrdinaryName; |
| break; |
| case ClangTypeKind::Tag: |
| lookupKind = clang::Sema::LookupTagName; |
| break; |
| case ClangTypeKind::ObjCProtocol: |
| lookupKind = clang::Sema::LookupObjCProtocolName; |
| break; |
| } |
| |
| // Perform name lookup into the global scope. |
| auto &sema = Impl.Instance->getSema(); |
| clang::LookupResult lookupResult(sema, clangName, clang::SourceLocation(), |
| lookupKind); |
| if (sema.LookupName(lookupResult, /*Scope=*/nullptr)) { |
| for (auto clangDecl : lookupResult) { |
| if (!isa<clang::TypeDecl>(clangDecl) && |
| !isa<clang::ObjCContainerDecl>(clangDecl) && |
| !isa<clang::ObjCCompatibleAliasDecl>(clangDecl)) { |
| continue; |
| } |
| auto *imported = Impl.importDecl(clangDecl, Impl.CurrentVersion); |
| if (auto *importedType = dyn_cast_or_null<TypeDecl>(imported)) |
| receiver(importedType); |
| } |
| } |
| } |
| |
| void ClangImporter::lookupRelatedEntity( |
| StringRef rawName, ClangTypeKind kind, StringRef relatedEntityKind, |
| llvm::function_ref<void(TypeDecl*)> receiver) { |
| using CISTAttr = ClangImporterSynthesizedTypeAttr; |
| if (relatedEntityKind == |
| CISTAttr::manglingNameForKind(CISTAttr::Kind::NSErrorWrapper) || |
| relatedEntityKind == |
| CISTAttr::manglingNameForKind(CISTAttr::Kind::NSErrorWrapperAnon)) { |
| auto underlyingKind = ClangTypeKind::Tag; |
| if (relatedEntityKind == |
| CISTAttr::manglingNameForKind(CISTAttr::Kind::NSErrorWrapperAnon)) { |
| underlyingKind = ClangTypeKind::Typedef; |
| } |
| lookupTypeDecl(rawName, underlyingKind, |
| [this, receiver] (const TypeDecl *foundType) { |
| auto *enumDecl = |
| dyn_cast_or_null<clang::EnumDecl>(foundType->getClangDecl()); |
| if (!enumDecl) |
| return; |
| if (!Impl.getEnumInfo(enumDecl).isErrorEnum()) |
| return; |
| auto *enclosingType = |
| dyn_cast<NominalTypeDecl>(foundType->getDeclContext()); |
| if (!enclosingType) |
| return; |
| receiver(enclosingType); |
| }); |
| } |
| } |
| |
| void ClangModuleUnit::lookupVisibleDecls(ModuleDecl::AccessPathTy accessPath, |
| VisibleDeclConsumer &consumer, |
| NLKind lookupKind) const { |
| // FIXME: Ignore submodules, which are empty for now. |
| if (clangModule && clangModule->isSubModule()) |
| return; |
| |
| // FIXME: Respect the access path. |
| FilteringVisibleDeclConsumer filterConsumer(consumer, this); |
| |
| DarwinLegacyFilterDeclConsumer darwinFilterConsumer(filterConsumer, |
| getClangASTContext()); |
| |
| swift::VisibleDeclConsumer *actualConsumer = &filterConsumer; |
| if (lookupKind == NLKind::UnqualifiedLookup && |
| DarwinLegacyFilterDeclConsumer::needsFiltering(clangModule)) { |
| actualConsumer = &darwinFilterConsumer; |
| } |
| |
| // Find the corresponding lookup table. |
| if (auto lookupTable = owner.findLookupTable(clangModule)) { |
| // Search it. |
| owner.lookupVisibleDecls(*lookupTable, *actualConsumer); |
| } |
| } |
| |
| namespace { |
| class VectorDeclPtrConsumer : public swift::VisibleDeclConsumer { |
| public: |
| SmallVectorImpl<Decl *> &Results; |
| explicit VectorDeclPtrConsumer(SmallVectorImpl<Decl *> &Decls) |
| : Results(Decls) {} |
| |
| void foundDecl(ValueDecl *VD, DeclVisibilityKind Reason) override { |
| Results.push_back(VD); |
| } |
| }; |
| } // unnamed namespace |
| |
| void ClangModuleUnit::getTopLevelDecls(SmallVectorImpl<Decl*> &results) const { |
| VectorDeclPtrConsumer consumer(results); |
| FilteringDeclaredDeclConsumer filterConsumer(consumer, this); |
| DarwinLegacyFilterDeclConsumer darwinFilterConsumer(filterConsumer, |
| getClangASTContext()); |
| |
| const clang::Module *topLevelModule = |
| clangModule ? clangModule->getTopLevelModule() : nullptr; |
| |
| swift::VisibleDeclConsumer *actualConsumer = &filterConsumer; |
| if (DarwinLegacyFilterDeclConsumer::needsFiltering(topLevelModule)) |
| actualConsumer = &darwinFilterConsumer; |
| |
| // Find the corresponding lookup table. |
| if (auto lookupTable = owner.findLookupTable(topLevelModule)) { |
| // Search it. |
| owner.lookupVisibleDecls(*lookupTable, *actualConsumer); |
| |
| // Add the extensions produced by importing categories. |
| for (auto category : lookupTable->categories()) { |
| if (auto extension = cast_or_null<ExtensionDecl>( |
| owner.importDecl(category, owner.CurrentVersion))) |
| results.push_back(extension); |
| } |
| |
| auto findEnclosingExtension = [](Decl *importedDecl) -> ExtensionDecl * { |
| for (auto importedDC = importedDecl->getDeclContext(); |
| !importedDC->isModuleContext(); |
| importedDC = importedDC->getParent()) { |
| if (auto ext = dyn_cast<ExtensionDecl>(importedDC)) |
| return ext; |
| } |
| return nullptr; |
| }; |
| // Retrieve all of the globals that will be mapped to members. |
| |
| // FIXME: Since we don't represent Clang submodules as Swift |
| // modules, we're getting everything. |
| llvm::SmallPtrSet<ExtensionDecl *, 8> knownExtensions; |
| for (auto entry : lookupTable->allGlobalsAsMembers()) { |
| auto decl = entry.get<clang::NamedDecl *>(); |
| auto importedDecl = owner.importDecl(decl, owner.CurrentVersion); |
| if (!importedDecl) continue; |
| |
| // Find the enclosing extension, if there is one. |
| ExtensionDecl *ext = findEnclosingExtension(importedDecl); |
| if (ext && knownExtensions.insert(ext).second) |
| results.push_back(ext); |
| |
| // If this is a compatibility typealias, the canonical type declaration |
| // may exist in another extension. |
| auto alias = dyn_cast<TypeAliasDecl>(importedDecl); |
| if (!alias || !alias->isCompatibilityAlias()) continue; |
| |
| auto aliasedTy = alias->getUnderlyingTypeLoc().getType(); |
| ext = nullptr; |
| importedDecl = nullptr; |
| |
| // Note: We can't use getAnyGeneric() here because `aliasedTy` |
| // might be typealias. |
| if (auto Ty = dyn_cast<TypeAliasType>(aliasedTy.getPointer())) |
| importedDecl = Ty->getDecl(); |
| else if (auto Ty = dyn_cast<AnyGenericType>(aliasedTy.getPointer())) |
| importedDecl = Ty->getDecl(); |
| if (!importedDecl) continue; |
| |
| ext = findEnclosingExtension(importedDecl); |
| if (ext && knownExtensions.insert(ext).second) |
| results.push_back(ext); |
| } |
| } |
| } |
| |
| ImportDecl *swift::createImportDecl(ASTContext &Ctx, |
| DeclContext *DC, |
| ClangNode ClangN, |
| ArrayRef<clang::Module *> Exported) { |
| auto *ImportedMod = ClangN.getClangModule(); |
| assert(ImportedMod); |
| SmallVector<std::pair<swift::Identifier, swift::SourceLoc>, 4> AccessPath; |
| auto *TmpMod = ImportedMod; |
| while (TmpMod) { |
| AccessPath.push_back({ Ctx.getIdentifier(TmpMod->Name), SourceLoc() }); |
| TmpMod = TmpMod->Parent; |
| } |
| std::reverse(AccessPath.begin(), AccessPath.end()); |
| |
| bool IsExported = false; |
| for (auto *ExportedMod : Exported) { |
| if (ImportedMod == ExportedMod) { |
| IsExported = true; |
| break; |
| } |
| } |
| |
| auto *ID = ImportDecl::create(Ctx, DC, SourceLoc(), |
| ImportKind::Module, SourceLoc(), AccessPath, |
| ClangN); |
| if (IsExported) |
| ID->getAttrs().add(new (Ctx) ExportedAttr(/*IsImplicit=*/false)); |
| return ID; |
| } |
| |
| static void getImportDecls(ClangModuleUnit *ClangUnit, const clang::Module *M, |
| SmallVectorImpl<Decl *> &Results) { |
| assert(M); |
| SmallVector<clang::Module *, 1> Exported; |
| M->getExportedModules(Exported); |
| |
| ASTContext &Ctx = ClangUnit->getASTContext(); |
| |
| for (auto *ImportedMod : M->Imports) { |
| auto *ID = createImportDecl(Ctx, ClangUnit, ImportedMod, Exported); |
| Results.push_back(ID); |
| } |
| } |
| |
| void ClangModuleUnit::getDisplayDecls(SmallVectorImpl<Decl*> &results) const { |
| if (clangModule) |
| getImportDecls(const_cast<ClangModuleUnit *>(this), clangModule, results); |
| getTopLevelDecls(results); |
| } |
| |
| void ClangModuleUnit::lookupValue(ModuleDecl::AccessPathTy accessPath, |
| DeclName name, NLKind lookupKind, |
| SmallVectorImpl<ValueDecl*> &results) const { |
| if (!ModuleDecl::matchesAccessPath(accessPath, name)) |
| return; |
| |
| // FIXME: Ignore submodules, which are empty for now. |
| if (clangModule && clangModule->isSubModule()) |
| return; |
| |
| VectorDeclConsumer vectorWriter(results); |
| FilteringVisibleDeclConsumer filteringConsumer(vectorWriter, this); |
| |
| DarwinLegacyFilterDeclConsumer darwinFilterConsumer(filteringConsumer, |
| getClangASTContext()); |
| |
| swift::VisibleDeclConsumer *consumer = &filteringConsumer; |
| if (lookupKind == NLKind::UnqualifiedLookup && |
| DarwinLegacyFilterDeclConsumer::needsFiltering(clangModule)) { |
| consumer = &darwinFilterConsumer; |
| } |
| |
| // Find the corresponding lookup table. |
| if (auto lookupTable = owner.findLookupTable(clangModule)) { |
| // Search it. |
| owner.lookupValue(*lookupTable, name, *consumer); |
| } |
| } |
| |
| /// Determine whether the given Clang entry is visible. |
| /// |
| /// FIXME: this is an elaborate hack to badly reflect Clang's |
| /// submodule visibility into Swift. |
| static bool isVisibleClangEntry(clang::ASTContext &ctx, |
| SwiftLookupTable::SingleEntry entry) { |
| if (auto clangDecl = entry.dyn_cast<clang::NamedDecl *>()) { |
| // For a declaration, check whether the declaration is hidden. |
| if (!clangDecl->isHidden()) return true; |
| |
| // Is any redeclaration visible? |
| for (auto redecl : clangDecl->redecls()) { |
| if (!cast<clang::NamedDecl>(redecl)->isHidden()) return true; |
| } |
| |
| return false; |
| } |
| |
| // If it's a macro from a module, check whether the module has been imported. |
| if (auto moduleMacro = entry.dyn_cast<clang::ModuleMacro *>()) { |
| clang::Module *module = moduleMacro->getOwningModule(); |
| return module->NameVisibility == clang::Module::AllVisible; |
| } |
| |
| return true; |
| } |
| |
| TypeDecl * |
| ClangModuleUnit::lookupNestedType(Identifier name, |
| const NominalTypeDecl *baseType) const { |
| // Special case for error code enums: try looking directly into the struct |
| // first. But only if it looks like a synthesized error wrapped struct. |
| if (name == getASTContext().Id_Code && !baseType->hasClangNode() && |
| isa<StructDecl>(baseType) && !baseType->hasLazyMembers() && |
| baseType->isChildContextOf(this)) { |
| auto *mutableBase = const_cast<NominalTypeDecl *>(baseType); |
| auto flags = OptionSet<NominalTypeDecl::LookupDirectFlags>(); |
| flags |= NominalTypeDecl::LookupDirectFlags::IgnoreNewExtensions; |
| auto codeEnum = mutableBase->lookupDirect(name, flags); |
| // Double-check that we actually have a good result. It's possible what we |
| // found is /not/ a synthesized error struct, but just something that looks |
| // like it. But if we still found a good result we should return that. |
| if (codeEnum.size() == 1 && isa<TypeDecl>(codeEnum.front())) |
| return cast<TypeDecl>(codeEnum.front()); |
| if (codeEnum.size() > 1) |
| return nullptr; |
| // Otherwise, fall back and try via lookup table. |
| } |
| |
| auto lookupTable = owner.findLookupTable(clangModule); |
| if (!lookupTable) |
| return nullptr; |
| |
| auto baseTypeContext = owner.getEffectiveClangContext(baseType); |
| if (!baseTypeContext) |
| return nullptr; |
| |
| auto &clangCtx = owner.getClangASTContext(); |
| |
| // FIXME: This is very similar to what's in Implementation::lookupValue and |
| // Implementation::loadAllMembers. |
| SmallVector<TypeDecl *, 2> results; |
| for (auto entry : lookupTable->lookup(SerializedSwiftName(name.str()), |
| baseTypeContext)) { |
| // If the entry is not visible, skip it. |
| if (!isVisibleClangEntry(clangCtx, entry)) continue; |
| |
| auto clangDecl = entry.dyn_cast<clang::NamedDecl *>(); |
| auto clangTypeDecl = dyn_cast_or_null<clang::TypeDecl>(clangDecl); |
| if (!clangTypeDecl) |
| continue; |
| |
| clangTypeDecl = cast<clang::TypeDecl>(clangTypeDecl->getMostRecentDecl()); |
| |
| bool anyMatching = false; |
| TypeDecl *originalDecl = nullptr; |
| owner.forEachDistinctName(clangTypeDecl, |
| [&](ImportedName newName, |
| ImportNameVersion nameVersion) -> bool { |
| if (anyMatching) |
| return true; |
| if (!newName.getDeclName().isSimpleName(name)) |
| return true; |
| |
| auto decl = dyn_cast_or_null<TypeDecl>( |
| owner.importDeclReal(clangTypeDecl, nameVersion)); |
| if (!decl) |
| return false; |
| |
| if (!originalDecl) |
| originalDecl = decl; |
| else if (originalDecl == decl) |
| return true; |
| |
| auto *importedContext = decl->getDeclContext()->getSelfNominalTypeDecl(); |
| if (importedContext != baseType) |
| return true; |
| |
| assert(decl->getFullName().matchesRef(name) && |
| "importFullName behaved differently from importDecl"); |
| results.push_back(decl); |
| anyMatching = true; |
| return true; |
| }); |
| } |
| |
| if (results.size() != 1) { |
| // It's possible that two types were import-as-member'd onto the same base |
| // type with the same name. In this case, fall back to regular lookup. |
| return nullptr; |
| } |
| |
| return results.front(); |
| } |
| |
| void ClangImporter::loadExtensions(NominalTypeDecl *nominal, |
| unsigned previousGeneration) { |
| // Determine the effective Clang context for this Swift nominal type. |
| auto effectiveClangContext = Impl.getEffectiveClangContext(nominal); |
| if (!effectiveClangContext) return; |
| |
| // For an Objective-C class, import all of the visible categories. |
| if (auto objcClass = dyn_cast_or_null<clang::ObjCInterfaceDecl>( |
| effectiveClangContext.getAsDeclContext())) { |
| // Simply importing the categories adds them to the list of extensions. |
| for (auto I = objcClass->visible_categories_begin(), |
| E = objcClass->visible_categories_end(); |
| I != E; ++I) { |
| Impl.importDeclReal(*I, Impl.CurrentVersion); |
| } |
| } |
| |
| // Dig through each of the Swift lookup tables, creating extensions |
| // where needed. |
| auto &clangCtx = Impl.getClangASTContext(); |
| (void)Impl.forEachLookupTable([&](SwiftLookupTable &table) -> bool { |
| // FIXME: If we already looked at this for this generation, |
| // skip. |
| |
| for (auto entry : table.lookupGlobalsAsMembers(effectiveClangContext)) { |
| // If the entry is not visible, skip it. |
| if (!isVisibleClangEntry(clangCtx, entry)) continue; |
| |
| if (auto decl = entry.dyn_cast<clang::NamedDecl *>()) { |
| // Import the context of this declaration, which has the |
| // side effect of creating instantiations. |
| (void)Impl.importDeclContextOf(decl, effectiveClangContext); |
| } else { |
| llvm_unreachable("Macros cannot be imported as members."); |
| } |
| } |
| |
| return false; |
| }); |
| } |
| |
| void ClangImporter::loadObjCMethods( |
| ClassDecl *classDecl, |
| ObjCSelector selector, |
| bool isInstanceMethod, |
| unsigned previousGeneration, |
| llvm::TinyPtrVector<AbstractFunctionDecl *> &methods) { |
| // If we're currently looking for this selector, don't load any Objective-C |
| // methods. |
| if (Impl.ActiveSelectors.count({selector, isInstanceMethod})) |
| return; |
| |
| const auto *objcClass = |
| dyn_cast_or_null<clang::ObjCInterfaceDecl>(classDecl->getClangDecl()); |
| if (!objcClass) |
| return; |
| |
| // Collect the set of visible Objective-C methods with this selector. |
| clang::Selector clangSelector = Impl.exportSelector(selector); |
| SmallVector<clang::ObjCMethodDecl *, 4> objcMethods; |
| auto &sema = Impl.Instance->getSema(); |
| sema.CollectMultipleMethodsInGlobalPool(clangSelector, objcMethods, |
| isInstanceMethod, |
| /*CheckTheOther=*/false); |
| |
| // Check whether this method is in the class we care about. |
| SmallVector<AbstractFunctionDecl *, 4> foundMethods; |
| for (auto objcMethod : objcMethods) { |
| // Find the owner of this method and determine whether it is the class |
| // we're looking for. |
| if (objcMethod->getClassInterface() != objcClass) |
| continue; |
| |
| // If we found a property accessor, import the property. |
| if (objcMethod->isPropertyAccessor()) |
| (void)Impl.importDecl(objcMethod->findPropertyDecl(true), |
| Impl.CurrentVersion); |
| |
| if (auto method = dyn_cast_or_null<AbstractFunctionDecl>( |
| Impl.importDecl(objcMethod, Impl.CurrentVersion))) { |
| foundMethods.push_back(method); |
| } |
| } |
| |
| // If we didn't find anything, we're done. |
| if (foundMethods.empty()) |
| return; |
| |
| // If we did find something, it might be a duplicate of something we found |
| // earlier, because we aren't tracking generation counts for Clang modules. |
| // Filter out the duplicates. |
| // FIXME: We shouldn't need to do this. |
| llvm::SmallPtrSet<AbstractFunctionDecl *, 4> known; |
| known.insert(methods.begin(), methods.end()); |
| for (auto method : foundMethods) { |
| if (known.insert(method).second) |
| methods.push_back(method); |
| } |
| } |
| |
| void |
| ClangModuleUnit::lookupClassMember(ModuleDecl::AccessPathTy accessPath, |
| DeclName name, |
| SmallVectorImpl<ValueDecl*> &results) const { |
| // FIXME: Ignore submodules, which are empty for now. |
| if (clangModule && clangModule->isSubModule()) |
| return; |
| |
| VectorDeclConsumer consumer(results); |
| |
| // Find the corresponding lookup table. |
| if (auto lookupTable = owner.findLookupTable(clangModule)) { |
| // Search it. |
| owner.lookupObjCMembers(*lookupTable, name, consumer); |
| } |
| } |
| |
| void ClangModuleUnit::lookupClassMembers(ModuleDecl::AccessPathTy accessPath, |
| VisibleDeclConsumer &consumer) const { |
| // FIXME: Ignore submodules, which are empty for now. |
| if (clangModule && clangModule->isSubModule()) |
| return; |
| |
| // Find the corresponding lookup table. |
| if (auto lookupTable = owner.findLookupTable(clangModule)) { |
| // Search it. |
| owner.lookupAllObjCMembers(*lookupTable, consumer); |
| } |
| } |
| |
| void ClangModuleUnit::lookupObjCMethods( |
| ObjCSelector selector, |
| SmallVectorImpl<AbstractFunctionDecl *> &results) const { |
| // FIXME: Ignore submodules, which are empty for now. |
| if (clangModule && clangModule->isSubModule()) |
| return; |
| |
| // Map the selector into a Clang selector. |
| auto clangSelector = owner.exportSelector(selector); |
| if (clangSelector.isNull()) return; |
| |
| // Collect all of the Objective-C methods with this selector. |
| SmallVector<clang::ObjCMethodDecl *, 8> objcMethods; |
| auto &clangSema = owner.getClangSema(); |
| clangSema.CollectMultipleMethodsInGlobalPool(clangSelector, |
| objcMethods, |
| /*InstanceFirst=*/true, |
| /*CheckTheOther=*/false); |
| clangSema.CollectMultipleMethodsInGlobalPool(clangSelector, |
| objcMethods, |
| /*InstanceFirst=*/false, |
| /*CheckTheOther=*/false); |
| |
| // Import the methods. |
| auto &clangCtx = clangSema.getASTContext(); |
| for (auto objcMethod : objcMethods) { |
| // Verify that this method came from this module. |
| auto owningClangModule = getClangTopLevelOwningModule(objcMethod, clangCtx); |
| if (owningClangModule != clangModule) continue; |
| |
| // If we found a property accessor, import the property. |
| if (objcMethod->isPropertyAccessor()) |
| (void)owner.importDecl(objcMethod->findPropertyDecl(true), |
| owner.CurrentVersion); |
| |
| // Import it. |
| // FIXME: Retrying a failed import works around recursion bugs in the Clang |
| // importer. |
| auto imported = |
| owner.importDecl(objcMethod, owner.CurrentVersion); |
| if (!imported) |
| imported = owner.importDecl(objcMethod, owner.CurrentVersion); |
| if (!imported) continue; |
| |
| if (auto func = dyn_cast<AbstractFunctionDecl>(imported)) |
| results.push_back(func); |
| |
| // If there is an alternate declaration, also look at it. |
| for (auto alternate : owner.getAlternateDecls(imported)) { |
| if (auto func = dyn_cast<AbstractFunctionDecl>(alternate)) |
| results.push_back(func); |
| } |
| } |
| } |
| |
| void ClangModuleUnit::collectLinkLibraries( |
| ModuleDecl::LinkLibraryCallback callback) const { |
| if (!clangModule) |
| return; |
| |
| // Skip this lib name in favor of export_as name. |
| if (clangModule->UseExportAsModuleLinkName) |
| return; |
| |
| for (auto clangLinkLib : clangModule->LinkLibraries) { |
| LibraryKind kind; |
| if (clangLinkLib.IsFramework) |
| kind = LibraryKind::Framework; |
| else |
| kind = LibraryKind::Library; |
| |
| callback(LinkLibrary(clangLinkLib.Library, kind)); |
| } |
| } |
| |
| StringRef ClangModuleUnit::getFilename() const { |
| if (!clangModule) { |
| StringRef SinglePCH = owner.getSinglePCHImport(); |
| if (SinglePCH.empty()) |
| return "<imports>"; |
| else |
| return SinglePCH; |
| } |
| if (const clang::FileEntry *F = clangModule->getASTFile()) |
| if (!F->getName().empty()) |
| return F->getName(); |
| return StringRef(); |
| } |
| |
| clang::TargetInfo &ClangImporter::getTargetInfo() const { |
| return Impl.Instance->getTarget(); |
| } |
| |
| clang::ASTContext &ClangImporter::getClangASTContext() const { |
| return Impl.getClangASTContext(); |
| } |
| |
| clang::Preprocessor &ClangImporter::getClangPreprocessor() const { |
| return Impl.getClangPreprocessor(); |
| } |
| |
| const clang::CompilerInstance &ClangImporter::getClangInstance() const { |
| return *Impl.Instance; |
| } |
| |
| const clang::Module *ClangImporter::getClangOwningModule(ClangNode Node) const { |
| return Impl.getClangOwningModule(Node); |
| } |
| |
| const clang::Module * |
| ClangImporter::Implementation::getClangOwningModule(ClangNode Node) const { |
| return ::getClangOwningModule(Node, getClangASTContext()); |
| } |
| |
| bool ClangImporter::hasTypedef(const clang::Decl *typeDecl) const { |
| return Impl.DeclsWithSuperfluousTypedefs.count(typeDecl); |
| } |
| |
| clang::Sema &ClangImporter::getClangSema() const { |
| return Impl.getClangSema(); |
| } |
| |
| clang::CodeGenOptions &ClangImporter::getClangCodeGenOpts() const { |
| return Impl.getClangCodeGenOpts(); |
| } |
| |
| std::string ClangImporter::getClangModuleHash() const { |
| return Impl.Invocation->getModuleHash(Impl.Instance->getDiagnostics()); |
| } |
| |
| Decl *ClangImporter::importDeclCached(const clang::NamedDecl *ClangDecl) { |
| return Impl.importDeclCached(ClangDecl, Impl.CurrentVersion); |
| } |
| |
| void ClangImporter::printStatistics() const { |
| Impl.Instance->getModuleManager()->PrintStats(); |
| } |
| |
| void ClangImporter::verifyAllModules() { |
| #ifndef NDEBUG |
| if (Impl.VerifiedDeclsCounter == Impl.ImportedDecls.size()) |
| return; |
| |
| // Collect the Decls before verifying them; the act of verifying may cause |
| // more decls to be imported and modify the map while we are iterating it. |
| size_t verifiedCounter = Impl.ImportedDecls.size(); |
| SmallVector<Decl *, 8> Decls; |
| for (auto &I : Impl.ImportedDecls) |
| if (I.first.second == Impl.CurrentVersion) |
| if (Decl *D = I.second) |
| Decls.push_back(D); |
| |
| for (auto D : Decls) |
| verify(D); |
| |
| Impl.VerifiedDeclsCounter = verifiedCounter; |
| #endif |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // ClangModule Implementation |
| //===----------------------------------------------------------------------===// |
| |
| ClangModuleUnit::ClangModuleUnit(ModuleDecl &M, |
| ClangImporter::Implementation &owner, |
| const clang::Module *clangModule) |
| : LoadedFile(FileUnitKind::ClangModule, M), owner(owner), |
| clangModule(clangModule) { |
| // Capture the file metadata before it goes away. |
| if (clangModule) |
| ASTSourceDescriptor = {*clangModule}; |
| } |
| |
| Optional<clang::ExternalASTSource::ASTSourceDescriptor> |
| ClangModuleUnit::getASTSourceDescriptor() const { |
| if (clangModule) { |
| assert(ASTSourceDescriptor.getModuleOrNull() == clangModule); |
| return ASTSourceDescriptor; |
| } |
| return None; |
| } |
| |
| bool ClangModuleUnit::hasClangModule(ModuleDecl *M) { |
| for (auto F : M->getFiles()) { |
| if (isa<ClangModuleUnit>(F)) |
| return true; |
| } |
| return false; |
| } |
| |
| bool ClangModuleUnit::isTopLevel() const { |
| return !clangModule || !clangModule->isSubModule(); |
| } |
| |
| bool ClangModuleUnit::isSystemModule() const { |
| return clangModule && clangModule->IsSystem; |
| } |
| |
| clang::ASTContext &ClangModuleUnit::getClangASTContext() const { |
| return owner.getClangASTContext(); |
| } |
| |
| StringRef ClangModuleUnit::getExportedModuleName() const { |
| if (clangModule && !clangModule->ExportAsModule.empty()) |
| return clangModule->ExportAsModule; |
| |
| return getParentModule()->getName().str(); |
| } |
| |
| ModuleDecl *ClangModuleUnit::getAdapterModule() const { |
| if (!clangModule) |
| return nullptr; |
| |
| if (owner.DisableAdapterModules) |
| return nullptr; |
| |
| if (!isTopLevel()) { |
| // FIXME: Is this correct for submodules? |
| auto topLevel = clangModule->getTopLevelModule(); |
| auto wrapper = owner.getWrapperForModule(topLevel); |
| return wrapper->getAdapterModule(); |
| } |
| |
| if (!adapterModule.getInt()) { |
| // FIXME: Include proper source location. |
| ModuleDecl *M = getParentModule(); |
| ASTContext &Ctx = M->getASTContext(); |
| auto adapter = Ctx.getModule(ModuleDecl::AccessPathTy({M->getName(), |
| SourceLoc()})); |
| if (adapter == M) { |
| adapter = nullptr; |
| } else { |
| auto &sharedModuleRef = Ctx.LoadedModules[M->getName()]; |
| assert(!sharedModuleRef || sharedModuleRef == adapter || |
| sharedModuleRef == M); |
| sharedModuleRef = adapter; |
| } |
| |
| auto mutableThis = const_cast<ClangModuleUnit *>(this); |
| mutableThis->adapterModule.setPointerAndInt(adapter, true); |
| } |
| |
| return adapterModule.getPointer(); |
| } |
| |
| void ClangModuleUnit::getImportedModules( |
| SmallVectorImpl<ModuleDecl::ImportedModule> &imports, |
| ModuleDecl::ImportFilter filter) const { |
| // Bail out if we /only/ want ImplementationOnly imports; Clang modules never |
| // have any of these. |
| if (filter.containsOnly(ModuleDecl::ImportFilterKind::ImplementationOnly)) |
| return; |
| |
| if (filter.contains(ModuleDecl::ImportFilterKind::Private)) |
| if (auto stdlib = owner.getStdlibModule()) |
| imports.push_back({ModuleDecl::AccessPathTy(), stdlib}); |
| |
| SmallVector<clang::Module *, 8> imported; |
| if (!clangModule) { |
| // This is the special "imported headers" module. |
| if (filter.contains(ModuleDecl::ImportFilterKind::Public)) { |
| imported.append(owner.ImportedHeaderExports.begin(), |
| owner.ImportedHeaderExports.end()); |
| } |
| |
| } else { |
| clangModule->getExportedModules(imported); |
| |
| if (filter.contains(ModuleDecl::ImportFilterKind::Private)) { |
| // Copy in any modules that are imported but not exported. |
| llvm::SmallPtrSet<clang::Module *, 8> knownModules(imported.begin(), |
| imported.end()); |
| if (!filter.contains(ModuleDecl::ImportFilterKind::Public)) { |
| // Remove the exported ones now that we're done with them. |
| imported.clear(); |
| } |
| llvm::copy_if(clangModule->Imports, std::back_inserter(imported), |
| [&](clang::Module *mod) { |
| return !knownModules.insert(mod).second; |
| }); |
| |
| // FIXME: The parent module isn't exactly a private import, but it is |
| // needed for link dependencies. |
| if (clangModule->Parent) |
| imported.push_back(clangModule->Parent); |
| } |
| } |
| |
| auto topLevelAdapter = getAdapterModule(); |
| for (auto importMod : imported) { |
| auto wrapper = owner.getWrapperForModule(importMod); |
| |
| auto actualMod = wrapper->getAdapterModule(); |
| if (!actualMod) { |
| // HACK: Deal with imports of submodules by importing the top-level module |
| // as well. |
| auto importTopLevel = importMod->getTopLevelModule(); |
| if (importTopLevel != importMod) { |
| if (!clangModule || importTopLevel != clangModule->getTopLevelModule()){ |
| auto topLevelWrapper = owner.getWrapperForModule(importTopLevel); |
| imports.push_back({ ModuleDecl::AccessPathTy(), |
| topLevelWrapper->getParentModule() }); |
| } |
| } |
| actualMod = wrapper->getParentModule(); |
| } else if (actualMod == topLevelAdapter) { |
| actualMod = wrapper->getParentModule(); |
| } |
| |
| assert(actualMod && "Missing imported adapter module"); |
| imports.push_back({ModuleDecl::AccessPathTy(), actualMod}); |
| } |
| } |
| |
| void ClangModuleUnit::getImportedModulesForLookup( |
| SmallVectorImpl<ModuleDecl::ImportedModule> &imports) const { |
| |
| // Reuse our cached list of imports if we have one. |
| if (!importedModulesForLookup.empty()) { |
| imports.append(importedModulesForLookup.begin(), |
| importedModulesForLookup.end()); |
| return; |
| } |
| |
| size_t firstImport = imports.size(); |
| |
| SmallVector<clang::Module *, 8> imported; |
| const clang::Module *topLevel; |
| ModuleDecl *topLevelAdapter = getAdapterModule(); |
| if (!clangModule) { |
| // This is the special "imported headers" module. |
| imported.append(owner.ImportedHeaderExports.begin(), |
| owner.ImportedHeaderExports.end()); |
| topLevel = nullptr; |
| } else { |
| clangModule->getExportedModules(imported); |
| topLevel = clangModule->getTopLevelModule(); |
| } |
| |
| if (imported.empty()) |
| return; |
| |
| SmallPtrSet<clang::Module *, 32> seen{imported.begin(), imported.end()}; |
| SmallVector<clang::Module *, 8> tmpBuf; |
| llvm::SmallSetVector<clang::Module *, 8> topLevelImported; |
| |
| // Get the transitive set of top-level imports. That is, if a particular |
| // import is a top-level import, add it. Otherwise, keep searching. |
| while (!imported.empty()) { |
| clang::Module *next = imported.pop_back_val(); |
| |
| // HACK: Deal with imports of submodules by importing the top-level module |
| // as well, unless it's the top-level module we're currently in. |
| clang::Module *nextTopLevel = next->getTopLevelModule(); |
| if (nextTopLevel != topLevel) { |
| topLevelImported.insert(nextTopLevel); |
| |
| // Don't continue looking through submodules of modules that have |
| // overlays. The overlay might shadow things. |
| auto wrapper = owner.getWrapperForModule(nextTopLevel); |
| if (wrapper->getAdapterModule()) |
| continue; |
| } |
| |
| // Only look through the current module if it's not top-level. |
| if (nextTopLevel == next) |
| continue; |
| |
| next->getExportedModules(tmpBuf); |
| for (clang::Module *nextImported : tmpBuf) { |
| if (seen.insert(nextImported).second) |
| imported.push_back(nextImported); |
| } |
| tmpBuf.clear(); |
| } |
| |
| for (auto importMod : topLevelImported) { |
| auto wrapper = owner.getWrapperForModule(importMod); |
| |
| auto actualMod = wrapper->getAdapterModule(); |
| if (!actualMod || actualMod == topLevelAdapter) |
| actualMod = wrapper->getParentModule(); |
| |
| assert(actualMod && "Missing imported adapter module"); |
| imports.push_back({ModuleDecl::AccessPathTy(), actualMod}); |
| } |
| |
| // Cache our results for use next time. |
| auto importsToCache = llvm::makeArrayRef(imports).slice(firstImport); |
| importedModulesForLookup = getASTContext().AllocateCopy(importsToCache); |
| } |
| |
| void ClangImporter::getMangledName(raw_ostream &os, |
| const clang::NamedDecl *clangDecl) const { |
| if (!Impl.Mangler) |
| Impl.Mangler.reset(Impl.getClangASTContext().createMangleContext()); |
| |
| Impl.Mangler->mangleName(clangDecl, os); |
| } |
| |
| // --------------------------------------------------------------------------- |
| // Swift lookup tables |
| // --------------------------------------------------------------------------- |
| |
| SwiftLookupTable *ClangImporter::Implementation::findLookupTable( |
| const clang::Module *clangModule) { |
| // If the Clang module is null, use the bridging header lookup table. |
| if (!clangModule) |
| return BridgingHeaderLookupTable.get(); |
| |
| // Submodules share lookup tables with their parents. |
| if (clangModule->isSubModule()) |
| return findLookupTable(clangModule->getTopLevelModule()); |
| |
| // Look for a Clang module with this name. |
| auto known = LookupTables.find(clangModule->Name); |
| if (known == LookupTables.end()) return nullptr; |
| |
| return known->second.get(); |
| } |
| |
| bool ClangImporter::Implementation::forEachLookupTable( |
| llvm::function_ref<bool(SwiftLookupTable &table)> fn) { |
| // Visit the bridging header's lookup table. |
| if (fn(*BridgingHeaderLookupTable)) return true; |
| |
| // Collect and sort the set of module names. |
| SmallVector<StringRef, 4> moduleNames; |
| for (const auto &entry : LookupTables) { |
| moduleNames.push_back(entry.first); |
| } |
| llvm::array_pod_sort(moduleNames.begin(), moduleNames.end()); |
| |
| // Visit the lookup tables. |
| for (auto moduleName : moduleNames) { |
| if (fn(*LookupTables[moduleName])) return true; |
| } |
| |
| return false; |
| } |
| |
| void ClangImporter::Implementation::lookupValue( |
| SwiftLookupTable &table, DeclName name, |
| VisibleDeclConsumer &consumer) { |
| auto &clangCtx = getClangASTContext(); |
| auto clangTU = clangCtx.getTranslationUnitDecl(); |
| |
| for (auto entry : table.lookup(name.getBaseName(), clangTU)) { |
| // If the entry is not visible, skip it. |
| if (!isVisibleClangEntry(clangCtx, entry)) continue; |
| |
| ValueDecl *decl; |
| // If it's a Clang declaration, try to import it. |
| if (auto clangDecl = entry.dyn_cast<clang::NamedDecl *>()) { |
| decl = cast_or_null<ValueDecl>( |
| importDeclReal(clangDecl->getMostRecentDecl(), CurrentVersion)); |
| if (!decl) continue; |
| } else if (!name.isSpecial()) { |
| // Try to import a macro. |
| if (auto modMacro = entry.dyn_cast<clang::ModuleMacro *>()) |
| decl = importMacro(name.getBaseIdentifier(), modMacro); |
| else if (auto clangMacro = entry.dyn_cast<clang::MacroInfo *>()) |
| decl = importMacro(name.getBaseIdentifier(), clangMacro); |
| else |
| llvm_unreachable("new kind of lookup table entry"); |
| if (!decl) continue; |
| } else { |
| continue; |
| } |
| |
| // If we found a declaration from the standard library, make sure |
| // it does not show up in the lookup results for the imported |
| // module. |
| if (decl->getDeclContext()->isModuleScopeContext() && |
| decl->getModuleContext() == getStdlibModule()) |
| continue; |
| |
| // If the name matched, report this result. |
| bool anyMatching = false; |
| if (decl->getFullName().matchesRef(name) && |
| decl->getDeclContext()->isModuleScopeContext()) { |
| consumer.foundDecl(decl, DeclVisibilityKind::VisibleAtTopLevel); |
| anyMatching = true; |
| } |
| |
| // If there is an alternate declaration and the name matches, |
| // report this result. |
| for (auto alternate : getAlternateDecls(decl)) { |
| if (alternate->getFullName().matchesRef(name) && |
| alternate->getDeclContext()->isModuleScopeContext()) { |
| consumer.foundDecl(alternate, DeclVisibilityKind::VisibleAtTopLevel); |
| anyMatching = true; |
| } |
| } |
| |
| // If we have a declaration and nothing matched so far, try the names used |
| // in other versions of Swift. |
| if (!anyMatching) { |
| if (auto clangDecl = entry.dyn_cast<clang::NamedDecl *>()) { |
| const clang::NamedDecl *recentClangDecl = |
| clangDecl->getMostRecentDecl(); |
| |
| CurrentVersion.forEachOtherImportNameVersion( |
| [&](ImportNameVersion nameVersion) { |
| if (anyMatching) |
| return; |
| |
| // Check to see if the name and context match what we expect. |
| ImportedName newName = importFullName(recentClangDecl, nameVersion); |
| if (!newName.getDeclName().matchesRef(name)) |
| return; |
| |
| const clang::DeclContext *clangDC = |
| newName.getEffectiveContext().getAsDeclContext(); |
| if (!clangDC || !clangDC->isFileContext()) |
| return; |
| |
| // Then try to import the decl under the alternate name. |
| auto alternateNamedDecl = |
| cast_or_null<ValueDecl>(importDeclReal(recentClangDecl, |
| nameVersion)); |
| if (!alternateNamedDecl || alternateNamedDecl == decl) |
| return; |
| assert(alternateNamedDecl->getFullName().matchesRef(name) && |
| "importFullName behaved differently from importDecl"); |
| if (alternateNamedDecl->getDeclContext()->isModuleScopeContext()) { |
| consumer.foundDecl(alternateNamedDecl, |
| DeclVisibilityKind::VisibleAtTopLevel); |
| anyMatching = true; |
| } |
| }); |
| } |
| } |
| } |
| } |
| |
| void ClangImporter::Implementation::lookupVisibleDecls( |
| SwiftLookupTable &table, |
| VisibleDeclConsumer &consumer) { |
| // Retrieve and sort all of the base names in this particular table. |
| auto baseNames = table.allBaseNames(); |
| llvm::array_pod_sort(baseNames.begin(), baseNames.end()); |
| |
| // Look for namespace-scope entities with each base name. |
| for (auto baseName : baseNames) { |
| lookupValue(table, baseName.toDeclBaseName(SwiftContext), consumer); |
| } |
| } |
| |
| void ClangImporter::Implementation::lookupObjCMembers( |
| SwiftLookupTable &table, |
| DeclName name, |
| VisibleDeclConsumer &consumer) { |
| auto &clangCtx = getClangASTContext(); |
| |
| for (auto clangDecl : table.lookupObjCMembers(name.getBaseName())) { |
| // If the entry is not visible, skip it. |
| if (!isVisibleClangEntry(clangCtx, clangDecl)) continue; |
| |
| forEachDistinctName(clangDecl, |
| [&](ImportedName importedName, |
| ImportNameVersion nameVersion) -> bool { |
| // Import the declaration. |
| auto decl = |
| cast_or_null<ValueDecl>(importDeclReal(clangDecl, nameVersion)); |
| if (!decl) |
| return false; |
| |
| // If the name we found matches, report the declaration. |
| // FIXME: If we didn't need to check alternate decls here, we could avoid |
| // importing the member at all by checking importedName ahead of time. |
| if (decl->getFullName().matchesRef(name)) { |
| consumer.foundDecl(decl, DeclVisibilityKind::DynamicLookup); |
| } |
| |
| // Check for an alternate declaration; if its name matches, |
| // report it. |
| for (auto alternate : getAlternateDecls(decl)) { |
| if (alternate->getFullName().matchesRef(name)) { |
| consumer.foundDecl(alternate, DeclVisibilityKind::DynamicLookup); |
| } |
| } |
| return true; |
| }); |
| } |
| } |
| |
| void ClangImporter::Implementation::lookupAllObjCMembers( |
| SwiftLookupTable &table, |
| VisibleDeclConsumer &consumer) { |
| // Retrieve and sort all of the base names in this particular table. |
| auto baseNames = table.allBaseNames(); |
| llvm::array_pod_sort(baseNames.begin(), baseNames.end()); |
| |
| // Look for Objective-C members with each base name. |
| for (auto baseName : baseNames) { |
| lookupObjCMembers(table, baseName.toDeclBaseName(SwiftContext), consumer); |
| } |
| } |
| |
| Optional<TinyPtrVector<ValueDecl *>> |
| ClangImporter::Implementation::loadNamedMembers( |
| const IterableDeclContext *IDC, DeclBaseName N, uint64_t contextData) { |
| |
| auto *D = IDC->getDecl(); |
| auto *DC = cast<DeclContext>(D); |
| auto *CD = D->getClangDecl(); |
| auto *CDC = cast<clang::DeclContext>(CD); |
| assert(CD && "loadNamedMembers on a Decl without a clangDecl"); |
| |
| auto *nominal = DC->getSelfNominalTypeDecl(); |
| auto effectiveClangContext = getEffectiveClangContext(nominal); |
| |
| // FIXME: The legacy of mirroring protocol members rears its ugly head, |
| // and as a result we have to bail on any @interface or @category that |
| // has a declared protocol conformance. |
| if (auto *ID = dyn_cast<clang::ObjCInterfaceDecl>(CD)) { |
| if (ID->protocol_begin() != ID->protocol_end()) |
| return None; |
| } |
| if (auto *CCD = dyn_cast<clang::ObjCCategoryDecl>(CD)) { |
| if (CCD->protocol_begin() != CCD->protocol_end()) |
| return None; |
| } |
| |
| // Also bail out if there are any global-as-member mappings for this type; we |
| // can support some of them lazily but the full set of idioms seems |
| // prohibitively complex (also they're not stored in by-name lookup, for |
| // reasons unclear). |
| if (forEachLookupTable([&](SwiftLookupTable &table) -> bool { |
| return (!table.lookupGlobalsAsMembers(effectiveClangContext).empty()); |
| })) |
| return None; |
| |
| // There are 3 cases: |
| // |
| // - The decl is from a bridging header, CMO is Some(nullptr) |
| // which denotes the __ObjC Swift module and its associated |
| // BridgingHeaderLookupTable. |
| // |
| // - The decl is from a clang module, CMO is Some(M) for non-null |
| // M and we can use the table for that module. |
| // |
| // - The decl is a forward declaration, CMO is None, which should |
| // never be the case if we got here (someone is asking for members). |
| // |
| // findLookupTable, below, handles the first two cases; we assert on the |
| // third. |
| |
| auto CMO = getClangSubmoduleForDecl(CD); |
| assert(CMO && "loadNamedMembers on a forward-declared Decl"); |
| |
| auto table = findLookupTable(*CMO); |
| assert(table && "clang module without lookup table"); |
| |
| clang::ASTContext &clangCtx = getClangASTContext(); |
| |
| assert(isa<clang::ObjCContainerDecl>(CD)); |
| |
| TinyPtrVector<ValueDecl *> Members; |
| for (auto entry : table->lookup(SerializedSwiftName(N), |
| effectiveClangContext)) { |
| if (!entry.is<clang::NamedDecl *>()) continue; |
| auto member = entry.get<clang::NamedDecl *>(); |
| if (!isVisibleClangEntry(clangCtx, member)) continue; |
| |
| // Skip Decls from different clang::DeclContexts |
| if (member->getDeclContext() != CDC) continue; |
| |
| SmallVector<Decl*, 4> tmp; |
| insertMembersAndAlternates(member, tmp); |
| for (auto *TD : tmp) { |
| if (auto *V = dyn_cast<ValueDecl>(TD)) { |
| // Skip ValueDecls if they import under different names. |
| if (V->getBaseName() == N) { |
| Members.push_back(V); |
| } |
| } |
| } |
| } |
| return Members; |
| } |
| |
| |
| EffectiveClangContext ClangImporter::Implementation::getEffectiveClangContext( |
| const NominalTypeDecl *nominal) { |
| // If we have a Clang declaration, look at it to determine the |
| // effective Clang context. |
| if (auto constClangDecl = nominal->getClangDecl()) { |
| auto clangDecl = const_cast<clang::Decl *>(constClangDecl); |
| if (auto dc = dyn_cast<clang::DeclContext>(clangDecl)) |
| return EffectiveClangContext(dc); |
| if (auto typedefName = dyn_cast<clang::TypedefNameDecl>(clangDecl)) |
| return EffectiveClangContext(typedefName); |
| |
| return EffectiveClangContext(); |
| } |
| |
| // If it's an @objc entity, go look for it. |
| // Note that we're stepping lightly here to avoid computing isObjC() |
| // too early. |
| if (isa<ClassDecl>(nominal) && |
| (nominal->getAttrs().hasAttribute<ObjCAttr>() || |
| (!nominal->getParentSourceFile() && nominal->isObjC()))) { |
| // Map the name. If we can't represent the Swift name in Clang. |
| // FIXME: We should be using the Objective-C name here! |
| auto clangName = exportName(nominal->getName()); |
| if (!clangName) |
| return EffectiveClangContext(); |
| |
| // Perform name lookup into the global scope. |
| auto &sema = Instance->getSema(); |
| clang::LookupResult lookupResult(sema, clangName, |
| clang::SourceLocation(), |
| clang::Sema::LookupOrdinaryName); |
| if (sema.LookupName(lookupResult, /*Scope=*/nullptr)) { |
| // FIXME: Filter based on access path? C++ access control? |
| for (auto clangDecl : lookupResult) { |
| if (auto objcClass = dyn_cast<clang::ObjCInterfaceDecl>(clangDecl)) |
| return EffectiveClangContext(objcClass); |
| |
| /// FIXME: Other type declarations should also be okay? |
| } |
| } |
| } |
| |
| return EffectiveClangContext(); |
| } |
| |
| void ClangImporter::dumpSwiftLookupTables() { |
| Impl.dumpSwiftLookupTables(); |
| } |
| |
| void ClangImporter::Implementation::dumpSwiftLookupTables() { |
| // Sort the module names so we can print in a deterministic order. |
| SmallVector<StringRef, 4> moduleNames; |
| for (const auto &lookupTable : LookupTables) { |
| moduleNames.push_back(lookupTable.first); |
| } |
| array_pod_sort(moduleNames.begin(), moduleNames.end()); |
| |
| // Print out the lookup tables for the various modules. |
| for (auto moduleName : moduleNames) { |
| llvm::errs() << "<<" << moduleName << " lookup table>>\n"; |
| LookupTables[moduleName]->deserializeAll(); |
| LookupTables[moduleName]->dump(); |
| } |
| |
| llvm::errs() << "<<Bridging header lookup table>>\n"; |
| BridgingHeaderLookupTable->dump(); |
| } |
| |
| DeclName ClangImporter:: |
| importName(const clang::NamedDecl *D, |
| clang::DeclarationName preferredName) { |
| return Impl.importFullName(D, Impl.CurrentVersion, preferredName). |
| getDeclName(); |
| } |
| |
| bool ClangImporter::isInOverlayModuleForImportedModule( |
| const DeclContext *overlayDC, |
| const DeclContext *importedDC) { |
| overlayDC = overlayDC->getModuleScopeContext(); |
| importedDC = importedDC->getModuleScopeContext(); |
| |
| auto importedClangModuleUnit = dyn_cast<ClangModuleUnit>(importedDC); |
| if (!importedClangModuleUnit || !importedClangModuleUnit->getClangModule()) |
| return false; |
| |
| auto overlayModule = overlayDC->getParentModule(); |
| if (overlayModule == importedClangModuleUnit->getAdapterModule()) |
| return true; |
| |
| // Is this a private module that's re-exported to the public (overlay) name? |
| auto clangModule = |
| importedClangModuleUnit->getClangModule()->getTopLevelModule(); |
| return !clangModule->ExportAsModule.empty() && |
| clangModule->ExportAsModule == overlayModule->getName().str(); |
| } |