| //===--- ClangImporter.cpp - Import Clang Modules -------------------------===// |
| // |
| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors |
| // Licensed under Apache License v2.0 with Runtime Library Exception |
| // |
| // See http://swift.org/LICENSE.txt for license information |
| // See http://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 "ImporterImpl.h" |
| #include "ClangDiagnosticConsumer.h" |
| #include "swift/Subsystems.h" |
| #include "swift/AST/ASTContext.h" |
| #include "swift/AST/Decl.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/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/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/Serialization/ASTReader.h" |
| #include "clang/Serialization/ASTWriter.h" |
| #include "clang/Lex/Preprocessor.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/Support/CrashRecoveryContext.h" |
| #include "llvm/Support/Path.h" |
| #include <algorithm> |
| #include <memory> |
| |
| using namespace swift; |
| |
| // Commonly-used Clang classes. |
| using clang::CompilerInstance; |
| using clang::CompilerInvocation; |
| |
| #pragma mark Internal data structures |
| |
| namespace { |
| class HeaderImportCallbacks : public clang::PPCallbacks { |
| ClangImporter &Importer; |
| ClangImporter::Implementation &Impl; |
| public: |
| HeaderImportCallbacks(ClangImporter &importer, |
| ClangImporter::Implementation &impl) |
| : Importer(importer), Impl(impl) {} |
| |
| void handleImport(const clang::Module *imported) { |
| if (!imported) |
| return; |
| Module *nativeImported = Impl.finishLoadingClangModule(Importer, imported, |
| /*adapter=*/true); |
| Impl.ImportedHeaderExports.push_back({ /*filter=*/{}, nativeImported }); |
| } |
| |
| virtual 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) override { |
| handleImport(Imported); |
| if (!Imported && File) |
| Importer.addDependency(File->getName()); |
| } |
| |
| virtual void moduleImport(clang::SourceLocation ImportLoc, |
| clang::ModuleIdPath Path, |
| const clang::Module *Imported) override { |
| handleImport(Imported); |
| } |
| }; |
| |
| class ASTReaderCallbacks : public clang::ASTReaderListener { |
| ClangImporter &Importer; |
| public: |
| explicit ASTReaderCallbacks(ClangImporter &importer) : Importer(importer) {} |
| |
| bool needsInputFileVisitation() override { return true; } |
| |
| bool visitInputFile(StringRef file, bool isSystem, |
| bool isOverridden, bool isExplicitModule) override { |
| if (!isOverridden) |
| Importer.addDependency(file); |
| 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); |
| } |
| |
| const char *getBufferIdentifier() const override { |
| return name.c_str(); |
| } |
| |
| BufferKind getBufferKind() const override { |
| return MemoryBuffer_Malloc; |
| } |
| }; |
| } |
| |
| 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) 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()); |
| } |
| }; |
| } |
| |
| 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)) |
| { |
| Impl.Retain(); |
| } |
| |
| ClangImporter::~ClangImporter() { |
| Impl.Release(); |
| } |
| |
| void ClangImporter::setTypeResolver(LazyResolver &resolver) { |
| Impl.setTypeResolver(&resolver); |
| } |
| |
| void ClangImporter::clearTypeResolver() { |
| Impl.setTypeResolver(nullptr); |
| } |
| |
| #pragma mark Module loading |
| |
| #ifdef NDEBUG |
| #define SHIMS_INCLUDE_FLAG "-isystem" |
| #else |
| #define SHIMS_INCLUDE_FLAG "-I" |
| #endif |
| |
| static void |
| getNormalInvocationArguments(std::vector<std::string> &invocationArgStrs, |
| ASTContext &ctx, |
| const ClangImporterOptions &importerOpts) { |
| const llvm::Triple &triple = ctx.LangOpts.Target; |
| SearchPathOptions &searchPathOpts = ctx.SearchPathOpts; |
| |
| // Construct the invocation arguments for the current target. |
| // Add target-independent options first. |
| invocationArgStrs.insert(invocationArgStrs.end(), { |
| // Enable modules. |
| "-fmodules", |
| |
| // Don't emit LLVM IR. |
| "-fsyntax-only", |
| |
| "-femit-all-decls", |
| SHIMS_INCLUDE_FLAG, searchPathOpts.RuntimeResourcePath, |
| "-fretain-comments-from-system-headers", |
| "-fmodules-validate-system-headers", |
| "-Werror=non-modular-include-in-framework-module", |
| "-Xclang", "-fmodule-feature", "-Xclang", "swift", |
| }); |
| |
| // Set C language options. |
| if (triple.isOSDarwin()) { |
| invocationArgStrs.insert(invocationArgStrs.end(), { |
| // Darwin uses Objective-C ARC. |
| "-x", "objective-c", "-std=gnu11", "-fobjc-arc", "-fblocks", |
| |
| // Define macros that Swift bridging headers use. |
| "-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 "\")))", |
| |
| // 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 Foundation. |
| "-DSWIFT_SDK_OVERLAY_FOUNDATION_EPOCH=5", |
| |
| // Request new APIs from SceneKit. |
| "-DSWIFT_SDK_OVERLAY2_SCENEKIT_EPOCH=1", |
| |
| // Request new APIs from SpriteKit. |
| "-DSWIFT_SDK_OVERLAY2_SPRITEKIT_EPOCH=1", |
| |
| // Request new APIs from CoreImage. |
| "-DSWIFT_SDK_OVERLAY_COREIMAGE_EPOCH=1", |
| }); |
| |
| // 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(), |
| }); |
| } |
| } else { |
| invocationArgStrs.insert(invocationArgStrs.end(), { |
| // Non-Darwin platforms don't use the Objective-C runtime, so they can |
| // not import Objective-C modules. |
| // |
| // Just use the most feature-rich C language mode. |
| "-x", "c", "-std=gnu11", |
| }); |
| } |
| |
| if (triple.isOSDarwin()) { |
| std::string minVersionBuf; |
| llvm::raw_string_ostream minVersionOpt{minVersionBuf}; |
| unsigned major, minor, micro; |
| if (triple.isiOS()) { |
| bool isiOSSimulator = swift::tripleIsiOSSimulator(triple); |
| if (triple.isTvOS()) { |
| if (isiOSSimulator) |
| minVersionOpt << "-mtvos-simulator-version-min="; |
| else |
| minVersionOpt << "-mtvos-version-min="; |
| } else { |
| if (isiOSSimulator) |
| minVersionOpt << "-mios-simulator-version-min="; |
| else |
| minVersionOpt << "-mios-version-min="; |
| } |
| |
| triple.getiOSVersion(major, minor, micro); |
| } else if(triple.isWatchOS()) { |
| if (tripleIsWatchSimulator(triple)) |
| minVersionOpt << "-mwatchos-simulator-version-min="; |
| else |
| minVersionOpt << "-mwatchos-version-min="; |
| triple.getOSVersion(major, minor, micro); |
| } else { |
| assert(triple.isMacOSX()); |
| minVersionOpt << "-mmacosx-version-min="; |
| triple.getMacOSXVersion(major, minor, micro); |
| } |
| minVersionOpt << clang::VersionTuple(major, minor, micro); |
| invocationArgStrs.push_back(std::move(minVersionOpt.str())); |
| } |
| |
| if (searchPathOpts.SDKPath.empty()) { |
| invocationArgStrs.push_back("-Xclang"); |
| invocationArgStrs.push_back("-nostdsysteminc"); |
| } else { |
| // On Darwin, Clang uses -isysroot to specify the include |
| // system root. On other targets, it seems to use --sysroot. |
| if (triple.isOSDarwin()) { |
| invocationArgStrs.push_back("-isysroot"); |
| } else { |
| invocationArgStrs.push_back("--sysroot"); |
| } |
| invocationArgStrs.push_back(searchPathOpts.SDKPath); |
| } |
| |
| const std::string &moduleCachePath = importerOpts.ModuleCachePath; |
| |
| // Set the module cache path. |
| if (!moduleCachePath.empty()) { |
| invocationArgStrs.push_back("-fmodules-cache-path="); |
| invocationArgStrs.back().append(moduleCachePath); |
| } |
| |
| if (importerOpts.DetailedPreprocessingRecord) { |
| invocationArgStrs.insert(invocationArgStrs.end(), { |
| "-Xclang", "-detailed-preprocessing-record", |
| "-Xclang", "-fmodule-format=raw", |
| }); |
| } else { |
| invocationArgStrs.insert(invocationArgStrs.end(), { |
| "-Xclang", "-fmodule-format=obj", |
| }); |
| } |
| } |
| |
| 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"); |
| } |
| } |
| |
| 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); |
| } |
| |
| for (auto extraArg : importerOpts.ExtraArgs) { |
| invocationArgStrs.push_back(extraArg); |
| } |
| } |
| |
| std::unique_ptr<ClangImporter> |
| ClangImporter::create(ASTContext &ctx, |
| const ClangImporterOptions &importerOpts, |
| DependencyTracker *tracker) { |
| |
| std::unique_ptr<ClangImporter> importer{ |
| new ClangImporter(ctx, importerOpts, tracker) |
| }; |
| |
| std::vector<std::string> invocationArgStrs; |
| |
| 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() << "clang '"; |
| interleave(invocationArgStrs, |
| [](StringRef arg) { llvm::errs() << arg; }, |
| [] { llvm::errs() << "' '"; }); |
| llvm::errs() << "'\n"; |
| } |
| |
| std::vector<const char *> invocationArgs; |
| for (auto &argStr : invocationArgStrs) |
| invocationArgs.push_back(argStr.c_str()); |
| |
| // FIXME: These can't be controlled from the command line. |
| llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> diagnosticOpts{ |
| new clang::DiagnosticOptions |
| }; |
| |
| std::unique_ptr<ClangDiagnosticConsumer> diagClient{ |
| new ClangDiagnosticConsumer(importer->Impl, *diagnosticOpts, |
| importerOpts.DumpClangDiagnostics) |
| }; |
| auto clangDiags = CompilerInstance::createDiagnostics(diagnosticOpts.get(), |
| diagClient.release()); |
| |
| // Create a new Clang compiler invocation. |
| llvm::IntrusiveRefCntPtr<CompilerInvocation> invocation{ |
| clang::createInvocationFromCommandLine(invocationArgs, clangDiags) |
| }; |
| if (!invocation) |
| return nullptr; |
| importer->Impl.Invocation = invocation; |
| |
| // 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. |
| 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()); |
| |
| // Create an almost-empty memory buffer. |
| auto sourceBuffer = llvm::MemoryBuffer::getMemBuffer( |
| "extern int __swift __attribute__((unavailable));", |
| Implementation::moduleImportBufferName); |
| clang::PreprocessorOptions &ppOpts = invocation->getPreprocessorOpts(); |
| ppOpts.addRemappedFile(Implementation::moduleImportBufferName, |
| sourceBuffer.release()); |
| |
| // Install a Clang module file extension to build Swift name lookup tables. |
| invocation->getFrontendOpts().ModuleFileExtensions.push_back(&importer->Impl); |
| |
| // 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 CompilerInstance(PCHContainerOperations)); |
| auto &instance = *importer->Impl.Instance; |
| instance.setDiagnostics(&*clangDiags); |
| instance.setInvocation(&*invocation); |
| |
| // Create the associated action. |
| importer->Impl.Action.reset(new clang::SyntaxOnlyAction); |
| 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; |
| |
| 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(); |
| instance.getModuleManager()->addListener( |
| std::unique_ptr<clang::ASTReaderListener>( |
| new ASTReaderCallbacks(*importer))); |
| |
| // 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(); |
| |
| // 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 (auto path : searchPathOpts.FrameworkSearchPaths) |
| importer->addSearchPath(path, /*isFramework*/true); |
| for (auto path : searchPathOpts.ImportSearchPaths) |
| importer->addSearchPath(path, /*isFramework*/false); |
| |
| 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)) { |
| importer->Impl.addEntryToLookupTable( |
| instance.getSema(), |
| importer->Impl.BridgingHeaderLookupTable, |
| named); |
| } |
| } |
| } |
| |
| // FIXME: This is missing implicit includes. |
| auto *CB = new HeaderImportCallbacks(*importer, 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 = Module::create(ctx.getIdentifier("__ObjC"), ctx); |
| importer->Impl.ImportedHeaderUnit = |
| new (ctx) ClangModuleUnit(*importedHeaderModule, *importer, nullptr); |
| importedHeaderModule->addFile(*importer->Impl.ImportedHeaderUnit); |
| |
| return importer; |
| } |
| |
| bool ClangImporter::addSearchPath(StringRef newSearchPath, bool isFramework) { |
| clang::FileManager &fileMgr = Impl.Instance->getFileManager(); |
| const clang::DirectoryEntry *entry = fileMgr.getDirectory(newSearchPath); |
| if (!entry) |
| return true; |
| |
| auto &headerSearchInfo = Impl.getClangPreprocessor().getHeaderSearchInfo(); |
| headerSearchInfo.AddSearchPath({entry, clang::SrcMgr::C_User, 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, |
| clang::frontend::Angled, |
| isFramework, |
| /*ignoreSysroot=*/true); |
| return false; |
| } |
| |
| void ClangImporter::Implementation::addEntryToLookupTable( |
| clang::Sema &clangSema, |
| SwiftLookupTable &table, |
| clang::NamedDecl *named) |
| { |
| // Determine whether this declaration is suppressed in Swift. |
| if (shouldSuppressDeclImport(named)) return; |
| |
| // If we have a name to import as, add this entry to the table. |
| clang::DeclContext *effectiveContext; |
| if (auto importedName = importFullName(named, None, &effectiveContext, |
| &clangSema)) { |
| table.addEntry(importedName.Imported, named, effectiveContext); |
| |
| // Also add the alias, if needed. |
| if (importedName.Alias) |
| table.addEntry(importedName.Alias, named, effectiveContext); |
| |
| // Also add the subscript entry, if needed. |
| if (importedName.IsSubscriptAccessor) |
| table.addEntry(DeclName(SwiftContext, SwiftContext.Id_subscript, |
| ArrayRef<Identifier>()), |
| named, effectiveContext); |
| } else if (auto category = dyn_cast<clang::ObjCCategoryDecl>(named)) { |
| table.addCategory(category); |
| } |
| |
| // Walk the members of any context that can have nested members. |
| if (isa<clang::TagDecl>(named) || |
| isa<clang::ObjCInterfaceDecl>(named) || |
| isa<clang::ObjCProtocolDecl>(named) || |
| isa<clang::ObjCCategoryDecl>(named)) { |
| clang::DeclContext *dc = cast<clang::DeclContext>(named); |
| for (auto member : dc->decls()) { |
| if (auto namedMember = dyn_cast<clang::NamedDecl>(member)) |
| addEntryToLookupTable(clangSema, table, namedMember); |
| } |
| } |
| } |
| |
| void ClangImporter::Implementation::addMacrosToLookupTable( |
| clang::ASTContext &clangCtx, |
| clang::Preprocessor &pp, |
| SwiftLookupTable &table) { |
| for (const auto ¯o : pp.macros(false)) { |
| // Find the local history of this macro directive. |
| clang::MacroDirective *MD = pp.getLocalMacroDirectiveHistory(macro.first); |
| if (!MD) continue; |
| |
| // Import the name. |
| auto name = importIdentifier(macro.first); |
| if (name.empty()) continue; |
| |
| // Walk the history. |
| for (; MD; MD = MD->getPrevious()) { |
| // Check whether we have a macro defined in this module. |
| auto info = pp.getMacroInfo(macro.first); |
| if (!info || info->isFromASTFile() || info->isBuiltinMacro()) continue; |
| |
| // Only interested in macro definitions. |
| auto *defMD = dyn_cast<clang::DefMacroDirective>(MD); |
| if (!defMD) continue; |
| |
| // If we hit a builtin macro, we're done. |
| if (auto info = defMD->getInfo()) { |
| if (info->isBuiltinMacro()) break; |
| } |
| |
| // If we hit a macro with invalid or predefined location, we're done. |
| auto loc = defMD->getLocation(); |
| if (loc.isInvalid()) break; |
| if (pp.getSourceManager().getFileID(loc) == pp.getPredefinesFileID()) |
| break; |
| |
| // Add this entry. |
| table.addEntry(name, info, clangCtx.getTranslationUnitDecl()); |
| } |
| } |
| } |
| |
| bool ClangImporter::Implementation::importHeader( |
| Module *adapter, StringRef headerName, SourceLoc diagLoc, |
| bool trackParsedSymbols, |
| std::unique_ptr<llvm::MemoryBuffer> sourceBuffer) { |
| // 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.hasFatalErrorOccurred()) |
| return true; |
| |
| assert(adapter); |
| ImportedHeaderOwners.push_back(adapter); |
| |
| bool hadError = clangDiags.hasErrorOccurred(); |
| |
| clang::ASTContext &ClangCtx = getClangASTContext(); |
| clang::Preprocessor &pp = getClangPreprocessor(); |
| |
| clang::SourceManager &sourceMgr = ClangCtx.getSourceManager(); |
| |
| clang::SourceLocation includeLoc = |
| sourceMgr.getLocForStartOfFile(sourceMgr.getMainFileID()); |
| clang::FileID bufferID = sourceMgr.createFileID(std::move(sourceBuffer), |
| clang::SrcMgr::C_User, |
| /*LoadedID=*/0, |
| /*LoadedOffset=*/0, |
| includeLoc); |
| |
| pp.EnterSourceFile(bufferID, /*directoryLookup=*/nullptr, /*loc=*/{}); |
| // Force the import to occur. |
| pp.LookAhead(0); |
| |
| clang::Parser::DeclGroupPtrTy parsed; |
| while (!Parser->ParseTopLevelDecl(parsed)) { |
| if (!parsed) continue; |
| |
| for (auto *D : parsed.get()) { |
| if (trackParsedSymbols) |
| addBridgeHeaderTopLevelDecls(D); |
| |
| if (auto named = dyn_cast<clang::NamedDecl>(D)) { |
| addEntryToLookupTable(getClangSema(), BridgingHeaderLookupTable, |
| named); |
| } |
| } |
| } |
| pp.EndSourceFile(); |
| bumpGeneration(); |
| |
| // Add any defined macros to the bridging header lookup table. |
| addMacrosToLookupTable(getClangASTContext(), getClangPreprocessor(), |
| BridgingHeaderLookupTable); |
| |
| // 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, {}); |
| } |
| } |
| |
| // 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, Module *adapter, |
| off_t expectedSize, time_t expectedModTime, |
| StringRef cachedContents, SourceLoc diagLoc) { |
| clang::FileManager &fileManager = Impl.Instance->getFileManager(); |
| const clang::FileEntry *headerFile = fileManager.getFile(header, |
| /*open=*/true); |
| if (headerFile && headerFile->getSize() == expectedSize && |
| headerFile->getModificationTime() == expectedModTime) { |
| return importBridgingHeader(header, adapter, diagLoc); |
| } |
| |
| 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)); |
| } |
| |
| bool ClangImporter::importBridgingHeader(StringRef header, Module *adapter, |
| SourceLoc diagLoc, |
| bool trackParsedSymbols) { |
| clang::FileManager &fileManager = Impl.Instance->getFileManager(); |
| const clang::FileEntry *headerFile = fileManager.getFile(header, |
| /*open=*/true); |
| if (!headerFile) { |
| Impl.SwiftContext.Diags.diagnose(diagLoc, diag::bridging_header_missing, |
| header); |
| return true; |
| } |
| |
| llvm::SmallString<128> importLine{"#import \""}; |
| 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)); |
| } |
| |
| std::string ClangImporter::getBridgingHeaderContents(StringRef headerPath, |
| off_t &fileSize, |
| time_t &fileModTime) { |
| llvm::IntrusiveRefCntPtr<clang::CompilerInvocation> invocation{ |
| new clang::CompilerInvocation(*Impl.Invocation) |
| }; |
| invocation->getFrontendOpts().DisableFree = false; |
| invocation->getFrontendOpts().Inputs.clear(); |
| invocation->getFrontendOpts().Inputs.push_back( |
| clang::FrontendInputFile(headerPath, clang::IK_ObjC)); |
| |
| invocation->getPreprocessorOpts().resetNonModularOptions(); |
| |
| clang::CompilerInstance rewriteInstance( |
| Impl.Instance->getPCHContainerOperations()); |
| 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([&] { |
| clang::RewriteIncludesAction action; |
| action.BeginSourceFile(rewriteInstance, |
| invocation->getFrontendOpts().Inputs.front()); |
| llvm::raw_string_ostream os(result); |
| clang::RewriteIncludesInInput(rewriteInstance.getPreprocessor(), &os, |
| rewriteInstance.getPreprocessorOutputOpts()); |
| action.EndSourceFile(); |
| }); |
| |
| 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; |
| } |
| |
| void ClangImporter::collectSubModuleNamesAndVisibility( |
| ArrayRef<std::pair<Identifier, SourceLoc>> path, |
| std::vector<std::pair<std::string, bool>> &namesVisiblePairs) { |
| auto &clangHeaderSearch = Impl.getClangPreprocessor().getHeaderSearchInfo(); |
| |
| // Look up the top-level module first. |
| clang::Module *clangModule = |
| clangHeaderSearch.lookupModule(path.front().first.str()); |
| if (!clangModule) |
| return; |
| clang::Module *submodule = clangModule; |
| for (auto component : path.slice(1)) { |
| submodule = submodule->findSubmodule(component.first.str()); |
| if (!submodule) |
| return; |
| } |
| auto submoduleNameLength = submodule->getFullModuleName().length(); |
| for (auto sub : submodule->submodules()) { |
| StringRef full = sub->getFullModuleName(); |
| namesVisiblePairs.push_back( |
| std::make_pair(full.substr(submoduleNameLength + 1).str(), |
| isModuleImported(sub))); |
| } |
| } |
| |
| bool ClangImporter::isModuleImported(const clang::Module *M) { |
| return M->NameVisibility == clang::Module::NameVisibilityKind::AllVisible; |
| } |
| |
| Module *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()); |
| 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 &srcMgr = clangContext.getSourceManager(); |
| 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); |
| |
| // FIXME: The source location here is completely bogus. It can't be |
| // invalid, and it can't be the same thing twice in a row, so we just use |
| // a counter. Having real source locations would be far, far better. |
| clang::SourceLocation clangImportLoc |
| = srcMgr.getLocForStartOfFile(srcMgr.getMainFileID()) |
| .getLocWithOffset(Impl.ImportCounter++); |
| clang::ModuleLoadResult result = |
| Impl.Instance->loadModule(clangImportLoc, path, visibility, |
| /*IsInclusionDirective=*/false); |
| 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()); |
| // FIXME: Specialize the error for a missing submodule? |
| if (!submodule) |
| return nullptr; |
| } |
| |
| // Finally, load the submodule and make it visible. |
| clangModule = loadModule(clangPath, true); |
| if (!clangModule) |
| return nullptr; |
| |
| return Impl.finishLoadingClangModule(*this, clangModule, /*adapter=*/false); |
| } |
| |
| Module *ClangImporter::Implementation::finishLoadingClangModule( |
| ClangImporter &owner, |
| const clang::Module *clangModule, |
| bool findAdapter) { |
| assert(clangModule); |
| |
| // Bump the generation count. |
| bumpGeneration(); |
| |
| auto &cacheEntry = ModuleWrappers[clangModule]; |
| Module *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({}, [&](Module::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 = Module::create(name, SwiftContext); |
| // Silence error messages about testably importing a Clang module. |
| result->setTestingEnabled(); |
| |
| wrapperUnit = |
| new (SwiftContext) ClangModuleUnit(*result, owner, 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({}, [](Module::ImportedModule import) {}); |
| } |
| |
| // Try to load the API notes for this module. |
| (void)getAPINotesForModule(clangModule->getTopLevelModule()); |
| |
| if (clangModule->isSubModule()) { |
| finishLoadingClangModule(owner, clangModule->getTopLevelModule(), true); |
| } else { |
| Module *&loaded = SwiftContext.LoadedModules[result->getName()]; |
| if (!loaded) |
| loaded = result; |
| } |
| |
| if (findAdapter) |
| if (Module *adapter = wrapperUnit->getAdapterModule()) |
| result = adapter; |
| |
| return result; |
| } |
| |
| Module *ClangImporter::getImportedHeaderModule() const { |
| return Impl.ImportedHeaderUnit->getParentModule(); |
| } |
| |
| ClangImporter::Implementation::Implementation(ASTContext &ctx, |
| const ClangImporterOptions &opts) |
| : SwiftContext(ctx), |
| ImportForwardDeclarations(opts.ImportForwardDeclarations), |
| OmitNeedlessWords(opts.OmitNeedlessWords), |
| InferDefaultArguments(opts.InferDefaultArguments), |
| UseSwiftLookupTables(opts.UseSwiftLookupTables), |
| BridgingHeaderLookupTable(nullptr) |
| { |
| // Add filters to determine if a Clang availability attribute |
| // applies in Swift, and if so, what is the cutoff for deprecated |
| // declarations that are now considered unavailable in Swift. |
| |
| if (ctx.LangOpts.Target.isiOS() && !ctx.LangOpts.Target.isTvOS()) { |
| if (!ctx.LangOpts.EnableAppExtensionRestrictions) { |
| PlatformAvailabilityFilter = |
| [](StringRef Platform) { return Platform == "ios"; }; |
| } else { |
| PlatformAvailabilityFilter = |
| [](StringRef Platform) { |
| return Platform == "ios" || |
| Platform == "ios_app_extension"; }; |
| } |
| // Anything deprecated in iOS 7.x and earlier is unavailable in Swift. |
| DeprecatedAsUnavailableFilter = |
| [](unsigned major, llvm::Optional<unsigned> minor) { return major <= 7; }; |
| DeprecatedAsUnavailableMessage = |
| "APIs deprecated as of iOS 7 and earlier are unavailable in Swift"; |
| } else if (ctx.LangOpts.Target.isTvOS()) { |
| if (!ctx.LangOpts.EnableAppExtensionRestrictions) { |
| PlatformAvailabilityFilter = |
| [](StringRef Platform) { return Platform == "tvos"; }; |
| } else { |
| PlatformAvailabilityFilter = |
| [](StringRef Platform) { |
| return Platform == "tvos" || |
| Platform == "tvos_app_extension"; }; |
| } |
| // Anything deprecated in iOS 7.x and earlier is unavailable in Swift. |
| DeprecatedAsUnavailableFilter = |
| [](unsigned major, llvm::Optional<unsigned> minor) { return major <= 7; }; |
| DeprecatedAsUnavailableMessage = |
| "APIs deprecated as of iOS 7 and earlier are unavailable in Swift"; |
| } else if (ctx.LangOpts.Target.isWatchOS()) { |
| if (!ctx.LangOpts.EnableAppExtensionRestrictions) { |
| PlatformAvailabilityFilter = |
| [](StringRef Platform) { return Platform == "watchos"; }; |
| } else { |
| PlatformAvailabilityFilter = |
| [](StringRef Platform) { |
| return Platform == "watchos" || |
| Platform == "watchos_app_extension"; }; |
| } |
| // No deprecation filter on watchOS |
| DeprecatedAsUnavailableFilter = |
| [](unsigned major, llvm::Optional<unsigned> minor) { return false; }; |
| DeprecatedAsUnavailableMessage = ""; |
| } |
| else if (ctx.LangOpts.Target.isMacOSX()) { |
| if (!ctx.LangOpts.EnableAppExtensionRestrictions) { |
| PlatformAvailabilityFilter = |
| [](StringRef Platform) { return Platform == "macosx"; }; |
| } else { |
| PlatformAvailabilityFilter = |
| [](StringRef Platform) { |
| return Platform == "macosx" || |
| Platform == "macosx_app_extension"; }; |
| } |
| // Anything deprecated in OSX 10.9.x and earlier is unavailable in Swift. |
| DeprecatedAsUnavailableFilter = |
| [](unsigned major, llvm::Optional<unsigned> minor) { |
| return major < 10 || |
| (major == 10 && (!minor.hasValue() || minor.getValue() <= 9)); |
| }; |
| DeprecatedAsUnavailableMessage = |
| "APIs deprecated as of OS X 10.9 and earlier are unavailable in Swift"; |
| } |
| } |
| |
| |
| ClangImporter::Implementation::~Implementation() { |
| assert(NumCurrentImportingEntities == 0); |
| #ifndef NDEBUG |
| SwiftContext.SourceMgr.verifyAllBuffers(); |
| #endif |
| } |
| |
| ClangModuleUnit *ClangImporter::Implementation::getWrapperForModule( |
| ClangImporter &importer, |
| 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 = Module::create(name, SwiftContext); |
| // Silence error messages about testably importing a Clang module. |
| wrapper->setTestingEnabled(); |
| |
| auto file = new (SwiftContext) ClangModuleUnit(*wrapper, importer, |
| underlying); |
| wrapper->addFile(*file); |
| cacheEntry.setPointer(file); |
| |
| return file; |
| } |
| |
| api_notes::APINotesReader * |
| ClangImporter::Implementation::getAPINotesForModule( |
| const clang::Module *underlying) { |
| assert(underlying == underlying->getTopLevelModule() && |
| "Only allowed on a top-level module"); |
| // Check whether we already have an API notes reader. |
| auto known = APINotesReaders.find(underlying); |
| if (known != APINotesReaders.end()) |
| return known->second.get(); |
| |
| /// Determine the name of the API notes we're looking for. |
| llvm::SmallString<64> notesFilename(underlying->Name); |
| notesFilename += '.'; |
| notesFilename += api_notes::BINARY_APINOTES_EXTENSION; |
| |
| // Look for a compiled API notes file in at the given search path. Sets |
| // the reader and returns true if one was found. |
| llvm::SmallString<64> scratch; |
| std::unique_ptr<api_notes::APINotesReader> reader; |
| auto findAPINotes = [&](StringRef searchPath) -> bool { |
| // Compute the file name we're looking for. |
| scratch.clear(); |
| llvm::sys::path::append(scratch, searchPath, notesFilename.str()); |
| |
| // Try to open the file. |
| llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> bufferOrErr |
| = llvm::MemoryBuffer::getFile(scratch.str()); |
| if (!bufferOrErr) |
| return false; |
| |
| // We found the API notes file; try to load it. |
| reader = api_notes::APINotesReader::get(std::move(bufferOrErr.get())); |
| return true; |
| }; |
| |
| // Look for a ModuleName.apinotes file in the import search paths. |
| // FIXME: Good thing we have no notion of layering for these paths. |
| bool foundAny = false; |
| for (const auto& searchPath : SwiftContext.SearchPathOpts.ImportSearchPaths) { |
| if (findAPINotes(searchPath)) { |
| foundAny = true; |
| break; |
| } |
| } |
| |
| // If we didn't find anything in the user-provided import search paths, look |
| // in the runtime library import path. |
| if (!foundAny) |
| findAPINotes(SwiftContext.SearchPathOpts.RuntimeLibraryImportPath); |
| |
| // Add the reader we formed (if any) to the table. |
| return APINotesReaders.insert({underlying, std::move(reader)}) |
| .first->second.get(); |
| } |
| |
| Optional<const clang::Decl *> |
| ClangImporter::Implementation::getDefinitionForClangTypeDecl( |
| const clang::Decl *D) { |
| if (auto OID = dyn_cast<clang::ObjCInterfaceDecl>(D)) |
| return OID->getDefinition(); |
| |
| if (auto TD = dyn_cast<clang::TagDecl>(D)) |
| return TD->getDefinition(); |
| |
| if (auto OPD = dyn_cast<clang::ObjCProtocolDecl>(D)) |
| return OPD->getDefinition(); |
| |
| return None; |
| } |
| |
| Optional<clang::Module *> |
| ClangImporter::Implementation::getClangSubmoduleForDecl( |
| const clang::Decl *D, |
| bool allowForwardDeclaration) { |
| const clang::Decl *actual = nullptr; |
| |
| // Put an Objective-C class into the module that contains the @interface |
| // definition, not just some @class forward declaration. |
| if (auto maybeDefinition = getDefinitionForClangTypeDecl(D)) { |
| actual = maybeDefinition.getValue(); |
| if (!actual && !allowForwardDeclaration) |
| return None; |
| } |
| |
| if (!actual) |
| actual = D->getCanonicalDecl(); |
| |
| return actual->getImportedOwningModule(); |
| } |
| |
| 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(); |
| |
| auto &importer = |
| static_cast<ClangImporter &>(*SwiftContext.getClangModuleLoader()); |
| return getWrapperForModule(importer, 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 |
| |
| /// \brief Determine whether the given name is reserved for Swift. |
| bool ClangImporter::Implementation::isSwiftReservedName(StringRef name) { |
| tok kind = Lexer::kindOfIdentifier(name, /*InSILMode=*/false); |
| return (kind != tok::identifier); |
| } |
| |
| 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; |
| } |
| |
| /// Parse a stringified Swift declaration name, e.g. "init(frame:)". |
| static StringRef parseDeclName(StringRef Name, |
| SmallVectorImpl<StringRef> &ArgNames, |
| bool &IsFunctionName) { |
| if (Name.back() != ')') { |
| IsFunctionName = false; |
| if (Lexer::isIdentifier(Name) && Name != "_") |
| return Name; |
| |
| return ""; |
| } |
| |
| IsFunctionName = true; |
| |
| StringRef BaseName, Parameters; |
| std::tie(BaseName, Parameters) = Name.split('('); |
| if (!Lexer::isIdentifier(BaseName) || BaseName == "_") |
| return ""; |
| |
| if (Parameters.empty()) |
| return ""; |
| Parameters = Parameters.drop_back(); // ')' |
| |
| if (Parameters.empty()) |
| return BaseName; |
| |
| if (Parameters.back() != ':') |
| return ""; |
| |
| do { |
| StringRef NextParam; |
| std::tie(NextParam, Parameters) = Parameters.split(':'); |
| |
| if (!Lexer::isIdentifier(NextParam)) |
| return ""; |
| Identifier NextParamID; |
| if (NextParam == "_") |
| ArgNames.push_back(""); |
| else |
| ArgNames.push_back(NextParam); |
| } while (!Parameters.empty()); |
| |
| return BaseName; |
| } |
| |
| /// \brief Returns the common prefix of two strings at camel-case word |
| /// granularity. |
| /// |
| /// For example, given "NSFooBar" and "NSFooBas", returns "NSFoo" |
| /// (not "NSFooBa"). The returned StringRef is a slice of the "a" argument. |
| /// |
| /// If either string has a non-identifier character immediately after the |
| /// prefix, \p followedByNonIdentifier will be set to \c true. If both strings |
| /// have identifier characters after the prefix, \p followedByNonIdentifier will |
| /// be set to \c false. Otherwise, \p followedByNonIdentifier will not be |
| /// changed from its initial value. |
| /// |
| /// This is used to derive the common prefix of enum constants so we can elide |
| /// it from the Swift interface. |
| static StringRef getCommonWordPrefix(StringRef a, StringRef b, |
| bool &followedByNonIdentifier) { |
| auto aWords = camel_case::getWords(a), bWords = camel_case::getWords(b); |
| auto aI = aWords.begin(), aE = aWords.end(), |
| bI = bWords.begin(), bE = bWords.end(); |
| |
| unsigned prevLength = 0; |
| unsigned prefixLength = 0; |
| for ( ; aI != aE && bI != bE; ++aI, ++bI) { |
| if (*aI != *bI) { |
| followedByNonIdentifier = false; |
| break; |
| } |
| |
| prevLength = prefixLength; |
| prefixLength = aI.getPosition() + aI->size(); |
| } |
| |
| // Avoid creating a prefix where the rest of the string starts with a number. |
| if ((aI != aE && !Lexer::isIdentifier(*aI)) || |
| (bI != bE && !Lexer::isIdentifier(*bI))) { |
| followedByNonIdentifier = true; |
| prefixLength = prevLength; |
| } |
| |
| return a.slice(0, prefixLength); |
| } |
| |
| /// Returns the common word-prefix of two strings, allowing the second string |
| /// to be a common English plural form of the first. |
| /// |
| /// For example, given "NSProperty" and "NSProperties", the full "NSProperty" |
| /// is returned. Given "NSMagicArmor" and "NSMagicArmory", only |
| /// "NSMagic" is returned. |
| /// |
| /// The "-s", "-es", and "-ies" patterns cover every plural NS_OPTIONS name |
| /// in Cocoa and Cocoa Touch. |
| /// |
| /// \see getCommonWordPrefix |
| static StringRef getCommonPluralPrefix(StringRef singular, StringRef plural) { |
| assert(!plural.empty()); |
| |
| if (singular.empty()) |
| return singular; |
| |
| bool ignored; |
| StringRef commonPrefix = getCommonWordPrefix(singular, plural, ignored); |
| if (commonPrefix.size() == singular.size() || plural.back() != 's') |
| return commonPrefix; |
| |
| StringRef leftover = singular.substr(commonPrefix.size()); |
| StringRef firstLeftoverWord = camel_case::getFirstWord(leftover); |
| StringRef commonPrefixPlusWord = |
| singular.substr(0, commonPrefix.size() + firstLeftoverWord.size()); |
| |
| // Is the plural string just "[singular]s"? |
| plural = plural.drop_back(); |
| if (plural.endswith(firstLeftoverWord)) |
| return commonPrefixPlusWord; |
| |
| if (plural.empty() || plural.back() != 'e') |
| return commonPrefix; |
| |
| // Is the plural string "[singular]es"? |
| plural = plural.drop_back(); |
| if (plural.endswith(firstLeftoverWord)) |
| return commonPrefixPlusWord; |
| |
| if (plural.empty() || !(plural.back() == 'i' && singular.back() == 'y')) |
| return commonPrefix; |
| |
| // Is the plural string "[prefix]ies" and the singular "[prefix]y"? |
| plural = plural.drop_back(); |
| firstLeftoverWord = firstLeftoverWord.drop_back(); |
| if (plural.endswith(firstLeftoverWord)) |
| return commonPrefixPlusWord; |
| |
| return commonPrefix; |
| } |
| |
| StringRef ClangImporter::Implementation::getEnumConstantNamePrefix( |
| clang::Sema &sema, |
| const clang::EnumDecl *decl) { |
| switch (classifyEnum(sema.getPreprocessor(), decl)) { |
| case EnumKind::Enum: |
| case EnumKind::Options: |
| // Enums are mapped to Swift enums, Options to Swift option sets, both |
| // of which attempt prefix-stripping. |
| break; |
| |
| case EnumKind::Constants: |
| case EnumKind::Unknown: |
| // Nothing to do. |
| return StringRef(); |
| } |
| |
| // If there are no enumers, there is no prefix to compute. |
| auto ec = decl->enumerator_begin(), ecEnd = decl->enumerator_end(); |
| if (ec == ecEnd) |
| return StringRef(); |
| |
| // Determine whether we can cache the result. |
| // FIXME: Pass in a cache? |
| bool useCache = &sema == &getClangSema(); |
| |
| // If we've already computed the prefix, return it. |
| auto known = useCache ? EnumConstantNamePrefixes.find(decl) |
| : EnumConstantNamePrefixes.end(); |
| if (known != EnumConstantNamePrefixes.end()) |
| return known->second; |
| |
| // Determine whether the given enumerator is non-deprecated and has no |
| // specifically-provided name. |
| auto isNonDeprecatedWithoutCustomName = |
| [](const clang::EnumConstantDecl *elem) -> bool { |
| if (elem->hasAttr<clang::SwiftNameAttr>()) |
| return false; |
| |
| clang::VersionTuple maxVersion{~0U, ~0U, ~0U}; |
| switch (elem->getAvailability(nullptr, maxVersion)) { |
| case clang::AR_Available: |
| case clang::AR_NotYetIntroduced: |
| for (auto attr : elem->attrs()) { |
| if (auto annotate = dyn_cast<clang::AnnotateAttr>(attr)) { |
| if (annotate->getAnnotation() == "swift1_unavailable") |
| return false; |
| } |
| if (auto avail = dyn_cast<clang::AvailabilityAttr>(attr)) { |
| if (avail->getPlatform()->getName() == "swift") |
| return false; |
| } |
| } |
| return true; |
| |
| case clang::AR_Deprecated: |
| case clang::AR_Unavailable: |
| return false; |
| } |
| }; |
| |
| // Move to the first non-deprecated enumerator, or non-swift_name'd |
| // enumerator, if present. |
| auto firstNonDeprecated = std::find_if(ec, ecEnd, |
| isNonDeprecatedWithoutCustomName); |
| bool hasNonDeprecated = (firstNonDeprecated != ecEnd); |
| if (hasNonDeprecated) { |
| ec = firstNonDeprecated; |
| } else { |
| // Advance to the first case without a custom name, deprecated or not. |
| while (ec != ecEnd && (*ec)->hasAttr<clang::SwiftNameAttr>()) |
| ++ec; |
| if (ec == ecEnd) { |
| if (useCache) |
| EnumConstantNamePrefixes.insert({decl, StringRef()}); |
| return StringRef(); |
| } |
| } |
| |
| // Compute the common prefix. |
| StringRef commonPrefix = (*ec)->getName(); |
| bool followedByNonIdentifier = false; |
| for (++ec; ec != ecEnd; ++ec) { |
| // Skip deprecated or swift_name'd enumerators. |
| const clang::EnumConstantDecl *elem = *ec; |
| if (hasNonDeprecated) { |
| if (!isNonDeprecatedWithoutCustomName(elem)) |
| continue; |
| } else { |
| if (elem->hasAttr<clang::SwiftNameAttr>()) |
| continue; |
| } |
| |
| commonPrefix = getCommonWordPrefix(commonPrefix, elem->getName(), |
| followedByNonIdentifier); |
| if (commonPrefix.empty()) |
| break; |
| } |
| |
| if (!commonPrefix.empty()) { |
| StringRef checkPrefix = commonPrefix; |
| |
| // Account for the 'kConstant' naming convention on enumerators. |
| if (checkPrefix[0] == 'k') { |
| bool canDropK; |
| if (checkPrefix.size() >= 2) |
| canDropK = clang::isUppercase(checkPrefix[1]); |
| else |
| canDropK = !followedByNonIdentifier; |
| |
| if (canDropK) |
| checkPrefix = checkPrefix.drop_front(); |
| } |
| |
| // Don't use importFullName() here, we want to ignore the swift_name |
| // and swift_private attributes. |
| StringRef enumNameStr = decl->getName(); |
| StringRef commonWithEnum = getCommonPluralPrefix(checkPrefix, |
| enumNameStr); |
| size_t delta = commonPrefix.size() - checkPrefix.size(); |
| |
| // Account for the 'EnumName_Constant' convention on enumerators. |
| if (commonWithEnum.size() < checkPrefix.size() && |
| checkPrefix[commonWithEnum.size()] == '_' && |
| !followedByNonIdentifier) { |
| delta += 1; |
| } |
| |
| commonPrefix = commonPrefix.slice(0, commonWithEnum.size() + delta); |
| } |
| |
| if (useCache) |
| EnumConstantNamePrefixes.insert({decl, commonPrefix}); |
| return commonPrefix; |
| } |
| |
| /// Determine whether the given Clang selector matches the given |
| /// selector pieces. |
| static bool isNonNullarySelector(clang::Selector selector, |
| ArrayRef<StringRef> pieces) { |
| unsigned n = selector.getNumArgs(); |
| if (n == 0) return false; |
| if (n != pieces.size()) return false; |
| |
| for (unsigned i = 0; i != n; ++i) { |
| if (selector.getNameForSlot(i) != pieces[i]) return false; |
| } |
| |
| return true; |
| } |
| |
| /// Whether we should make a variadic method with the given selector |
| /// non-variadic. |
| static bool shouldMakeSelectorNonVariadic(clang::Selector selector) { |
| // This is UIActionSheet's designated initializer. |
| if (isNonNullarySelector(selector, |
| { "initWithTitle", |
| "delegate", |
| "cancelButtonTitle", |
| "destructiveButtonTitle", |
| "otherButtonTitles" })) |
| return true; |
| |
| // This is UIAlertView's designated initializer. |
| if (isNonNullarySelector(selector, |
| { "initWithTitle", |
| "message", |
| "delegate", |
| "cancelButtonTitle", |
| "otherButtonTitles" })) |
| return true; |
| |
| // Nothing else for now. |
| return false; |
| } |
| |
| static bool isBlockParameter(const clang::ParmVarDecl *param) { |
| return param->getType()->isBlockPointerType(); |
| } |
| |
| static bool isErrorOutParameter(const clang::ParmVarDecl *param, |
| ForeignErrorConvention::IsOwned_t &isErrorOwned) { |
| clang::QualType type = param->getType(); |
| |
| // Must be a pointer. |
| auto ptrType = type->getAs<clang::PointerType>(); |
| if (!ptrType) return false; |
| type = ptrType->getPointeeType(); |
| |
| // For NSError**, take ownership from the qualifier. |
| if (auto objcPtrType = type->getAs<clang::ObjCObjectPointerType>()) { |
| auto iface = objcPtrType->getInterfaceDecl(); |
| if (iface && iface->getName() == "NSError") { |
| switch (type.getObjCLifetime()) { |
| case clang::Qualifiers::OCL_None: |
| llvm_unreachable("not in ARC?"); |
| |
| case clang::Qualifiers::OCL_ExplicitNone: |
| case clang::Qualifiers::OCL_Autoreleasing: |
| isErrorOwned = ForeignErrorConvention::IsNotOwned; |
| return true; |
| |
| case clang::Qualifiers::OCL_Weak: |
| // We just don't know how to handle this. |
| return false; |
| |
| case clang::Qualifiers::OCL_Strong: |
| isErrorOwned = ForeignErrorConvention::IsOwned; |
| return false; |
| } |
| llvm_unreachable("bad error ownership"); |
| } |
| } |
| return false; |
| } |
| |
| static bool isBoolType(clang::ASTContext &ctx, clang::QualType type) { |
| do { |
| // Check whether we have a typedef for "BOOL" or "Boolean". |
| if (auto typedefType = dyn_cast<clang::TypedefType>(type.getTypePtr())) { |
| auto typedefDecl = typedefType->getDecl(); |
| if (typedefDecl->getName() == "BOOL" || |
| typedefDecl->getName() == "Boolean") |
| return true; |
| |
| type = typedefDecl->getUnderlyingType(); |
| continue; |
| } |
| |
| // Try to desugar one level... |
| clang::QualType desugared = type.getSingleStepDesugaredType(ctx); |
| if (desugared.getTypePtr() == type.getTypePtr()) |
| break; |
| |
| type = desugared; |
| } while (!type.isNull()); |
| |
| return false; |
| } |
| |
| static bool isIntegerType(clang::QualType clangType) { |
| if (auto builtinTy = clangType->getAs<clang::BuiltinType>()) { |
| return (builtinTy->getKind() >= clang::BuiltinType::Bool && |
| builtinTy->getKind() <= clang::BuiltinType::UInt128) || |
| (builtinTy->getKind() >= clang::BuiltinType::SChar && |
| builtinTy->getKind() <= clang::BuiltinType::Int128); |
| } |
| |
| return false; |
| } |
| |
| /// Whether the given Objective-C type can be imported as an optional type. |
| static bool canImportAsOptional(clang::ASTContext &ctx, clang::QualType type) { |
| // Note: this mimics ImportHint::canImportAsOptional. |
| |
| // Objective-C object pointers. |
| if (type->getAs<clang::ObjCObjectPointerType>()) return true; |
| |
| // Block and function pointers. |
| if (type->isBlockPointerType() || type->isFunctionPointerType()) return true; |
| |
| // CF types. |
| do { |
| // Check whether we have a typedef that refers to a CoreFoundation type. |
| if (auto typedefType = dyn_cast<clang::TypedefType>(type.getTypePtr())) { |
| if (ClangImporter::Implementation::isCFTypeDecl(typedefType->getDecl())) |
| return true; |
| |
| type = typedefType->getDecl()->getUnderlyingType(); |
| continue; |
| } |
| |
| // Try to desugar one level... |
| clang::QualType desugared = type.getSingleStepDesugaredType(ctx); |
| if (desugared.getTypePtr() == type.getTypePtr()) |
| break; |
| |
| type = desugared; |
| } while (!type.isNull()); |
| |
| return false; |
| } |
| |
| static Optional<ForeignErrorConvention::Kind> |
| classifyMethodErrorHandling(const clang::ObjCMethodDecl *clangDecl, |
| OptionalTypeKind resultOptionality) { |
| // TODO: opt out any non-standard methods here? |
| clang::ASTContext &clangCtx = clangDecl->getASTContext(); |
| |
| // Check for an explicit attribute. |
| if (auto attr = clangDecl->getAttr<clang::SwiftErrorAttr>()) { |
| switch (attr->getConvention()) { |
| case clang::SwiftErrorAttr::None: |
| return None; |
| |
| case clang::SwiftErrorAttr::NonNullError: |
| return ForeignErrorConvention::NonNilError; |
| |
| // Only honor null_result if we actually imported as a |
| // non-optional type. |
| case clang::SwiftErrorAttr::NullResult: |
| if (resultOptionality != OTK_None && |
| canImportAsOptional(clangCtx, clangDecl->getReturnType())) |
| return ForeignErrorConvention::NilResult; |
| return None; |
| |
| // Preserve the original result type on a zero_result unless we |
| // imported it as Bool. |
| case clang::SwiftErrorAttr::ZeroResult: |
| if (isBoolType(clangCtx, clangDecl->getReturnType())) { |
| return ForeignErrorConvention::ZeroResult; |
| } else if (isIntegerType(clangDecl->getReturnType())) { |
| return ForeignErrorConvention::ZeroPreservedResult; |
| } |
| return None; |
| |
| // There's no reason to do the same for nonzero_result because the |
| // only meaningful value remaining would be zero. |
| case clang::SwiftErrorAttr::NonZeroResult: |
| if (isIntegerType(clangDecl->getReturnType())) |
| return ForeignErrorConvention::NonZeroResult; |
| return None; |
| } |
| llvm_unreachable("bad swift_error kind"); |
| } |
| |
| // Otherwise, apply the default rules. |
| |
| // For bool results, a zero value is an error. |
| if (isBoolType(clangCtx, clangDecl->getReturnType())) { |
| return ForeignErrorConvention::ZeroResult; |
| } |
| |
| // For optional reference results, a nil value is normally an error. |
| if (resultOptionality != OTK_None && |
| canImportAsOptional(clangCtx, clangDecl->getReturnType())) { |
| return ForeignErrorConvention::NilResult; |
| } |
| |
| return None; |
| } |
| |
| static const char ErrorSuffix[] = "AndReturnError"; |
| static const char AltErrorSuffix[] = "WithError"; |
| |
| /// Look for a method that will import to have the same name as the |
| /// given method after importing the Nth parameter as an elided error |
| /// parameter. |
| static bool hasErrorMethodNameCollision(ClangImporter::Implementation &importer, |
| const clang::ObjCMethodDecl *method, |
| unsigned paramIndex, |
| StringRef suffixToStrip) { |
| // Copy the existing selector pieces into an array. |
| auto selector = method->getSelector(); |
| unsigned numArgs = selector.getNumArgs(); |
| assert(numArgs > 0); |
| |
| SmallVector<clang::IdentifierInfo *, 4> chunks; |
| for (unsigned i = 0, e = selector.getNumArgs(); i != e; ++i) { |
| chunks.push_back(selector.getIdentifierInfoForSlot(i)); |
| } |
| |
| auto &ctx = method->getASTContext(); |
| if (paramIndex == 0 && !suffixToStrip.empty()) { |
| StringRef name = chunks[0]->getName(); |
| assert(name.endswith(suffixToStrip)); |
| name = name.drop_back(suffixToStrip.size()); |
| chunks[0] = &ctx.Idents.get(name); |
| } else if (paramIndex != 0) { |
| chunks.erase(chunks.begin() + paramIndex); |
| } |
| |
| auto newSelector = ctx.Selectors.getSelector(numArgs - 1, chunks.data()); |
| const clang::ObjCMethodDecl *conflict; |
| if (auto iface = method->getClassInterface()) { |
| conflict = iface->lookupMethod(newSelector, method->isInstanceMethod()); |
| } else { |
| auto protocol = cast<clang::ObjCProtocolDecl>(method->getDeclContext()); |
| conflict = protocol->getMethod(newSelector, method->isInstanceMethod()); |
| } |
| |
| if (conflict == nullptr) |
| return false; |
| |
| // Look to see if the conflicting decl is unavailable, either because it's |
| // been marked NS_SWIFT_UNAVAILABLE, because it's actually marked unavailable, |
| // or because it was deprecated before our API sunset. We can handle |
| // "conflicts" where one form is unavailable. |
| // FIXME: Somewhat duplicated from Implementation::importAttributes. |
| clang::AvailabilityResult availability = conflict->getAvailability(); |
| if (availability != clang::AR_Unavailable && |
| importer.DeprecatedAsUnavailableFilter) { |
| for (auto *attr : conflict->specific_attrs<clang::AvailabilityAttr>()) { |
| if (attr->getPlatform()->getName() == "swift") { |
| availability = clang::AR_Unavailable; |
| break; |
| } |
| if (importer.PlatformAvailabilityFilter && |
| !importer.PlatformAvailabilityFilter(attr->getPlatform()->getName())){ |
| continue; |
| } |
| clang::VersionTuple version = attr->getDeprecated(); |
| if (version.empty()) |
| continue; |
| if (importer.DeprecatedAsUnavailableFilter(version.getMajor(), |
| version.getMinor())) { |
| availability = clang::AR_Unavailable; |
| break; |
| } |
| } |
| } |
| return availability != clang::AR_Unavailable; |
| } |
| |
| /// Determine the optionality of the given Objective-C method. |
| /// |
| /// \param method The Clang method. |
| /// |
| /// \param knownNullability When API notes describe the nullability of this |
| /// parameter, that nullability. |
| static OptionalTypeKind getResultOptionality( |
| const clang::ObjCMethodDecl *method, |
| Optional<clang::NullabilityKind> knownNullability) { |
| auto &clangCtx = method->getASTContext(); |
| |
| // If nullability is available on the type, use it. |
| if (auto nullability = method->getReturnType()->getNullability(clangCtx)) { |
| return ClangImporter::Implementation::translateNullability(*nullability); |
| } |
| |
| // If there is a returns_nonnull attribute, non-null. |
| if (method->hasAttr<clang::ReturnsNonNullAttr>()) |
| return OTK_None; |
| |
| // If API notes gives us nullability, use that. |
| if (knownNullability) { |
| return ClangImporter::Implementation::translateNullability( |
| *knownNullability); |
| } |
| |
| // Default to implicitly unwrapped optionals. |
| return OTK_ImplicitlyUnwrappedOptional; |
| } |
| |
| static Optional<ClangImporter::Implementation::ImportedErrorInfo> |
| considerErrorImport(ClangImporter::Implementation &importer, |
| const clang::ObjCMethodDecl *clangDecl, |
| StringRef &baseName, |
| SmallVectorImpl<StringRef> ¶mNames, |
| ArrayRef<const clang::ParmVarDecl *> params, |
| bool isInitializer, |
| bool hasCustomName) { |
| // If the declaration name isn't parallel to the actual parameter |
| // list (e.g. if the method has C-style parameter declarations), |
| // don't try to apply error conventions. |
| bool expectsToRemoveError = |
| hasCustomName && paramNames.size() + 1 == params.size(); |
| if (!expectsToRemoveError && paramNames.size() != params.size()) |
| return None; |
| |
| for (unsigned index = params.size(); index-- != 0; ) { |
| // Allow an arbitrary number of trailing blocks. |
| if (isBlockParameter(params[index])) |
| continue; |
| |
| // Otherwise, require the last parameter to be an out-parameter. |
| auto isErrorOwned = ForeignErrorConvention::IsNotOwned; |
| if (!isErrorOutParameter(params[index], isErrorOwned)) |
| break; |
| |
| // Determine the nullability of the result. |
| Optional<clang::NullabilityKind> knownResultNullability; |
| if (auto knownMethod = importer.getKnownObjCMethod(clangDecl)) { |
| if (knownMethod->NullabilityAudited) |
| knownResultNullability = knownMethod->getReturnTypeInfo(); |
| } |
| |
| auto errorKind = |
| classifyMethodErrorHandling(clangDecl, |
| getResultOptionality(clangDecl, |
| knownResultNullability)); |
| if (!errorKind) return None; |
| |
| // Consider adjusting the imported declaration name to remove the |
| // parameter. |
| bool adjustName = !hasCustomName; |
| |
| // Never do this if it's the first parameter of a constructor. |
| if (isInitializer && index == 0) { |
| adjustName = false; |
| } |
| |
| // If the error parameter is the first parameter, try removing the |
| // standard error suffix from the base name. |
| StringRef suffixToStrip; |
| StringRef origBaseName = baseName; |
| if (adjustName && index == 0 && paramNames[0].empty()) { |
| if (baseName.endswith(ErrorSuffix)) |
| suffixToStrip = ErrorSuffix; |
| else if (baseName.endswith(AltErrorSuffix)) |
| suffixToStrip = AltErrorSuffix; |
| |
| if (!suffixToStrip.empty()) { |
| StringRef newBaseName = baseName.drop_back(suffixToStrip.size()); |
| if (newBaseName.empty() || importer.isSwiftReservedName(newBaseName)) { |
| adjustName = false; |
| suffixToStrip = {}; |
| } else { |
| baseName = newBaseName; |
| } |
| } |
| } |
| |
| // Also suppress name changes if there's a collision. |
| // TODO: this logic doesn't really work with init methods |
| // TODO: this privileges the old API over the new one |
| if (adjustName && |
| hasErrorMethodNameCollision(importer, clangDecl, index, |
| suffixToStrip)) { |
| // If there was a conflict on the first argument, and this was |
| // the first argument and we're not stripping error suffixes, just |
| // give up completely on error import. |
| if (index == 0 && suffixToStrip.empty()) { |
| return None; |
| |
| // If there was a conflict stripping an error suffix, adjust the |
| // name but don't change the base name. This avoids creating a |
| // spurious _: () argument. |
| } else if (index == 0 && !suffixToStrip.empty()) { |
| suffixToStrip = {}; |
| baseName = origBaseName; |
| |
| // Otherwise, give up on adjusting the name. |
| } else { |
| adjustName = false; |
| baseName = origBaseName; |
| } |
| } |
| |
| // If we're adjusting the name, erase the error parameter. |
| if (adjustName) { |
| paramNames.erase(paramNames.begin() + index); |
| } |
| |
| bool replaceParamWithVoid = !adjustName && !expectsToRemoveError; |
| ClangImporter::Implementation::ImportedErrorInfo errorInfo { |
| *errorKind, isErrorOwned, index, replaceParamWithVoid |
| }; |
| return errorInfo; |
| } |
| |
| // Didn't find an error parameter. |
| return None; |
| } |
| |
| auto ClangImporter::Implementation::importFullName( |
| const clang::NamedDecl *D, |
| ImportNameOptions options, |
| clang::DeclContext **effectiveContext, |
| clang::Sema *clangSemaOverride) -> ImportedName { |
| clang::Sema &clangSema = clangSemaOverride ? *clangSemaOverride |
| : getClangSema(); |
| ImportedName result; |
| |
| // Objective-C categories and extensions don't have names, despite |
| // being "named" declarations. |
| if (isa<clang::ObjCCategoryDecl>(D)) |
| return result; |
| |
| // Compute the effective context, if requested. |
| if (effectiveContext) { |
| auto dc = const_cast<clang::DeclContext *>(D->getDeclContext()); |
| |
| // Enumerators can end up within their enclosing enum or in the global |
| // scope, depending how their enclosing enumeration is imported. |
| if (isa<clang::EnumConstantDecl>(D)) { |
| auto enumDecl = cast<clang::EnumDecl>(dc); |
| switch (classifyEnum(clangSema.getPreprocessor(), enumDecl)) { |
| case EnumKind::Enum: |
| case EnumKind::Options: |
| // Enums are mapped to Swift enums, Options to Swift option sets. |
| *effectiveContext = enumDecl; |
| break; |
| |
| case EnumKind::Constants: |
| case EnumKind::Unknown: |
| // The enum constant goes into the redeclaration context of the |
| // enum. |
| *effectiveContext = enumDecl->getRedeclContext(); |
| break; |
| } |
| } else { |
| // Everything else goes into its redeclaration context. |
| *effectiveContext = dc->getRedeclContext(); |
| } |
| |
| // Anything in an Objective-C category or extension is adjusted to the |
| // class context. |
| if (auto category = dyn_cast<clang::ObjCCategoryDecl>(*effectiveContext)) { |
| *effectiveContext = category->getClassInterface(); |
| } |
| } |
| |
| // Local function that forms a DeclName from the given strings. |
| auto formDeclName = [&](StringRef baseName, |
| ArrayRef<StringRef> argumentNames, |
| bool isFunction) -> DeclName { |
| // We cannot import when the base name is not an identifier. |
| if (!Lexer::isIdentifier(baseName)) |
| return DeclName(); |
| |
| // Get the identifier for the base name. |
| Identifier baseNameId = SwiftContext.getIdentifier(baseName); |
| |
| // For non-functions, just use the base name. |
| if (!isFunction) return baseNameId; |
| |
| // For functions, we need to form a complete name. |
| |
| // Convert the argument names. |
| SmallVector<Identifier, 4> argumentNameIds; |
| for (auto argName : argumentNames) { |
| if (argumentNames.empty() || !Lexer::isIdentifier(argName)) { |
| argumentNameIds.push_back(Identifier()); |
| continue; |
| } |
| |
| argumentNameIds.push_back(SwiftContext.getIdentifier(argName)); |
| } |
| |
| // Build the result. |
| return DeclName(SwiftContext, baseNameId, argumentNameIds); |
| }; |
| |
| // If we have a swift_name attribute, use that. |
| if (auto *nameAttr = D->getAttr<clang::SwiftNameAttr>()) { |
| bool skipCustomName = false; |
| |
| // If we have an Objective-C method that is being mapped to an |
| // initializer (e.g., a factory method whose name doesn't fit the |
| // convention for factory methods), make sure that it can be |
| // imported as an initializer. |
| bool isInitializer = false; |
| auto method = dyn_cast<clang::ObjCMethodDecl>(D); |
| if (method) { |
| unsigned initPrefixLength; |
| if (nameAttr->getName().startswith("init(")) { |
| if (!shouldImportAsInitializer(method, initPrefixLength, |
| result.InitKind)) { |
| // We cannot import this as an initializer anyway. |
| return { }; |
| } |
| |
| // If this swift_name attribute maps a factory method to an |
| // initializer and we were asked not to do so, ignore the |
| // custom name. |
| if (options.contains(ImportNameFlags::SuppressFactoryMethodAsInit) && |
| (result.InitKind == CtorInitializerKind::Factory || |
| result.InitKind == CtorInitializerKind::ConvenienceFactory)) { |
| skipCustomName = true; |
| } else { |
| // Note that this is an initializer. |
| isInitializer = true; |
| } |
| } |
| } |
| |
| if (!skipCustomName) { |
| SmallVector<StringRef, 4> argumentNames; |
| bool isFunctionName; |
| StringRef baseName = parseDeclName(nameAttr->getName(), argumentNames, |
| isFunctionName); |
| if (baseName.empty()) return result; |
| |
| result.HasCustomName = true; |
| result.Imported = formDeclName(baseName, argumentNames, isFunctionName); |
| |
| if (method) { |
| // Get the parameters. |
| ArrayRef<const clang::ParmVarDecl *> params{ |
| method->param_begin(), |
| method->param_end() |
| }; |
| |
| result.ErrorInfo = considerErrorImport(*this, method, baseName, |
| argumentNames, params, |
| isInitializer, |
| /*hasCustomName=*/true); |
| } |
| |
| |
| return result; |
| } |
| } |
| |
| // For empty names, there is nothing to do. |
| if (D->getDeclName().isEmpty()) return result; |
| |
| /// Whether the result is a function name. |
| bool isFunction = false; |
| bool isInitializer = false; |
| unsigned initializerPrefixLen; |
| StringRef baseName; |
| SmallVector<StringRef, 4> argumentNames; |
| SmallString<16> selectorSplitScratch; |
| StringScratchSpace stringScratch; |
| ArrayRef<const clang::ParmVarDecl *> params; |
| switch (D->getDeclName().getNameKind()) { |
| case clang::DeclarationName::CXXConstructorName: |
| case clang::DeclarationName::CXXConversionFunctionName: |
| case clang::DeclarationName::CXXDestructorName: |
| case clang::DeclarationName::CXXLiteralOperatorName: |
| case clang::DeclarationName::CXXOperatorName: |
| case clang::DeclarationName::CXXUsingDirective: |
| // Handling these is part of C++ interoperability. |
| return result; |
| |
| case clang::DeclarationName::Identifier: |
| // Map the identifier. |
| baseName = D->getDeclName().getAsIdentifierInfo()->getName(); |
| break; |
| |
| case clang::DeclarationName::ObjCMultiArgSelector: |
| case clang::DeclarationName::ObjCOneArgSelector: |
| case clang::DeclarationName::ObjCZeroArgSelector: { |
| auto objcMethod = cast<clang::ObjCMethodDecl>(D); |
| isInitializer = shouldImportAsInitializer(objcMethod, initializerPrefixLen, |
| result.InitKind); |
| |
| // If we would import a factory method as an initializer but were |
| // asked not to, don't consider this as an initializer. |
| if (isInitializer && |
| options.contains(ImportNameFlags::SuppressFactoryMethodAsInit) && |
| (result.InitKind == CtorInitializerKind::Factory || |
| result.InitKind == CtorInitializerKind::ConvenienceFactory)) { |
| isInitializer = false; |
| } |
| |
| // Map the Objective-C selector directly. |
| auto selector = D->getDeclName().getObjCSelector(); |
| if (isInitializer) |
| baseName = "init"; |
| else |
| baseName = selector.getNameForSlot(0); |
| |
| // Get the parameters. |
| params = { objcMethod->param_begin(), objcMethod->param_end() }; |
| |
| // If we have a variadic method for which we need to drop the last |
| // selector piece, do so now. |
| unsigned numArgs = selector.getNumArgs(); |
| if (objcMethod->isVariadic() && shouldMakeSelectorNonVariadic(selector)) { |
| --numArgs; |
| result.DroppedVariadic = true; |
| params = params.drop_back(1); |
| } |
| |
| for (unsigned index = 0; index != numArgs; ++index) { |
| if (index == 0) { |
| argumentNames.push_back(StringRef()); |
| } else { |
| StringRef argName = selector.getNameForSlot(index); |
| |
| // Swift 2 lowercased all subsequent argument names. |
| // Swift 3 may handle this as part of omitting needless words, below, |
| // but don't preempt that here. |
| if (!OmitNeedlessWords) |
| argName = camel_case::toLowercaseWord(argName, stringScratch); |
| |
| argumentNames.push_back(argName); |
| } |
| } |
| |
| // For initializers, compute the first argument name. |
| if (isInitializer) { |
| // Skip over the prefix. |
| auto argName = selector.getNameForSlot(0).substr(initializerPrefixLen); |
| |
| // Drop "With" if present after the "init". |
| bool droppedWith = false; |
| if (argName.startswith("With")) { |
| argName = argName.substr(4); |
| droppedWith = true; |
| } |
| |
| // Lowercase the remaining argument name. |
| argName = camel_case::toLowercaseWord(argName, selectorSplitScratch); |
| |
| // If we dropped "with" and ended up with a reserved name, |
| // put "with" back. |
| if (droppedWith && isSwiftReservedName(argName)) { |
| selectorSplitScratch = "with"; |
| selectorSplitScratch += selector.getNameForSlot(0).substr( |
| initializerPrefixLen + 4); |
| argName = selectorSplitScratch; |
| } |
| |
| // Set the first argument name to be the name we computed. If |
| // there is no first argument, create one for this purpose. |
| if (argumentNames.empty()) { |
| if (!argName.empty()) { |
| // FIXME: Record what happened here for the caller? |
| argumentNames.push_back(argName); |
| } |
| } else { |
| argumentNames[0] = argName; |
| } |
| } |
| |
| result.ErrorInfo = considerErrorImport(*this, objcMethod, baseName, |
| argumentNames, params, isInitializer, |
| /*hasCustomName=*/false); |
| |
| isFunction = true; |
| |
| // Is this one of the accessors for subscripts? |
| if (objcMethod->getMethodFamily() == clang::OMF_None && |
| objcMethod->isInstanceMethod() && |
| (isNonNullarySelector(objcMethod->getSelector(), |
| { "objectAtIndexedSubscript" }) || |
| isNonNullarySelector(objcMethod->getSelector(), |
| { "setObject", "atIndexedSubscript" }) || |
| isNonNullarySelector(objcMethod->getSelector(), |
| { "objectForKeyedSubscript" }) || |
| isNonNullarySelector(objcMethod->getSelector(), |
| { "setObject", "forKeyedSubscript" }))) |
| result.IsSubscriptAccessor = true; |
| |
| break; |
| } |
| } |
| |
| // Perform automatic name transformations. |
| |
| // Enumeration constants may have common prefixes stripped. |
| if (isa<clang::EnumConstantDecl>(D)) { |
| auto enumDecl = cast<clang::EnumDecl>(D->getDeclContext()); |
| StringRef removePrefix = getEnumConstantNamePrefix(clangSema, enumDecl); |
| if (baseName.startswith(removePrefix)) |
| baseName = baseName.substr(removePrefix.size()); |
| } |
| |
| // Objective-C protocols may have the suffix "Protocol" appended if |
| // the non-suffixed name would conflict with another entity in the |
| // same top-level module. |
| SmallString<16> baseNameWithProtocolSuffix; |
| if (auto objcProto = dyn_cast<clang::ObjCProtocolDecl>(D)) { |
| if (objcProto->hasDefinition()) { |
| // Test to see if there is a value with the same name as the protocol |
| // in the same module. |
| // FIXME: This will miss macros. |
| auto clangModule = getClangSubmoduleForDecl(objcProto); |
| if (clangModule.hasValue() && clangModule.getValue()) |
| clangModule = clangModule.getValue()->getTopLevelModule(); |
| |
| auto isInSameModule = [&](const clang::Decl *D) -> bool { |
| auto declModule = getClangSubmoduleForDecl(D); |
| if (!declModule.hasValue()) |
| return false; |
| // Handle the bridging header case. This is pretty nasty since things |
| // can get added to it *later*, but there's not much we can do. |
| if (!declModule.getValue()) |
| return *clangModule == nullptr; |
| return *clangModule == declModule.getValue()->getTopLevelModule(); |
| }; |
| |
| // Allow this lookup to find hidden names. We don't want the |
| // decision about whether to rename the protocol to depend on |
| // what exactly the user has imported. Indeed, if we're being |
| // asked to resolve a serialization cross-reference, the user |
| // may not have imported this module at all, which means a |
| // normal lookup wouldn't even find the protocol! |
| // |
| // Meanwhile, we don't need to worry about finding unwanted |
| // hidden declarations from different modules because we do a |
| // module check before deciding that there's a conflict. |
| bool hasConflict = false; |
| clang::LookupResult lookupResult(clangSema, D->getDeclName(), |
| clang::SourceLocation(), |
| clang::Sema::LookupOrdinaryName); |
| lookupResult.setAllowHidden(true); |
| lookupResult.suppressDiagnostics(); |
| |
| if (clangSema.LookupName(lookupResult, /*scope=*/nullptr)) { |
| hasConflict = std::any_of(lookupResult.begin(), lookupResult.end(), |
| isInSameModule); |
| } |
| if (!hasConflict) { |
| lookupResult.clear(clang::Sema::LookupTagName); |
| if (clangSema.LookupName(lookupResult, /*scope=*/nullptr)) { |
| hasConflict = std::any_of(lookupResult.begin(), lookupResult.end(), |
| isInSameModule); |
| } |
| } |
| |
| if (hasConflict) { |
| baseNameWithProtocolSuffix = baseName; |
| baseNameWithProtocolSuffix += SWIFT_PROTOCOL_SUFFIX; |
| baseName = baseNameWithProtocolSuffix; |
| } |
| } |
| } |
| |
| // Typedef declarations might be CF types that will drop the "Ref" |
| // suffix. |
| bool aliasIsFunction = false; |
| bool aliasIsInitializer = false; |
| StringRef aliasBaseName; |
| SmallVector<StringRef, 4> aliasArgumentNames; |
| if (auto typedefNameDecl = dyn_cast<clang::TypedefNameDecl>(D)) { |
| auto swiftName = getCFTypeName(typedefNameDecl, &aliasBaseName); |
| if (!swiftName.empty()) { |
| baseName = swiftName; |
| } |
| } |
| |
| // Local function to determine whether the given declaration is subject to |
| // a swift_private attribute. |
| auto clangSemaPtr = &clangSema; |
| auto hasSwiftPrivate = [clangSemaPtr](const clang::NamedDecl *D) { |
| if (D->hasAttr<clang::SwiftPrivateAttr>()) |
| return true; |
| |
| // Enum constants that are not imported as members should be considered |
| // private if the parent enum is marked private. |
| if (auto *ECD = dyn_cast<clang::EnumConstantDecl>(D)) { |
| auto *ED = cast<clang::EnumDecl>(ECD->getDeclContext()); |
| switch (classifyEnum(clangSemaPtr->getPreprocessor(), ED)) { |
| case EnumKind::Constants: |
| case EnumKind::Unknown: |
| if (ED->hasAttr<clang::SwiftPrivateAttr>()) |
| return true; |
| if (auto *enumTypedef = ED->getTypedefNameForAnonDecl()) |
| if (enumTypedef->hasAttr<clang::SwiftPrivateAttr>()) |
| return true; |
| break; |
| |
| case EnumKind::Enum: |
| case EnumKind::Options: |
| break; |
| } |
| } |
| |
| return false; |
| }; |
| |
| // Omit needless words. |
| clang::ASTContext &clangCtx = clangSema.Context; |
| StringScratchSpace omitNeedlessWordsScratch; |
| if (OmitNeedlessWords) { |
| // Objective-C properties. |
| if (auto objcProperty = dyn_cast<clang::ObjCPropertyDecl>(D)) { |
| auto contextType = getClangDeclContextType(D->getDeclContext()); |
| if (!contextType.isNull()) { |
| auto contextTypeName = getClangTypeNameForOmission(clangCtx, |
| contextType); |
| auto propertyTypeName = getClangTypeNameForOmission( |
| clangCtx, objcProperty->getType()); |
| // Find the property names. |
| const InheritedNameSet *allPropertyNames = nullptr; |
| if (!contextType.isNull()) { |
| if (auto objcPtrType = contextType->getAsObjCInterfacePointerType()) |
| if (auto objcClassDecl = objcPtrType->getInterfaceDecl()) |
| allPropertyNames = SwiftContext.getAllPropertyNames( |
| objcClassDecl, |
| /*forInstance=*/true); |
| } |
| |
| (void)omitNeedlessWords(baseName, { }, "", propertyTypeName, |
| contextTypeName, { }, /*returnsSelf=*/false, |
| /*isProperty=*/true, allPropertyNames, |
| omitNeedlessWordsScratch); |
| } |
| } |
| |
| // Objective-C methods. |
| if (auto method = dyn_cast<clang::ObjCMethodDecl>(D)) { |
| (void)omitNeedlessWordsInFunctionName( |
| clangSema, |
| baseName, |
| argumentNames, |
| params, |
| method->getReturnType(), |
| method->getDeclContext(), |
| getNonNullArgs(method, params), |
| getKnownObjCMethod(method), |
| result.ErrorInfo ? Optional<unsigned>(result.ErrorInfo->ParamIndex) |
| : None, |
| method->hasRelatedResultType(), |
| method->isInstanceMethod(), |
| omitNeedlessWordsScratch); |
| } |
| } |
| |
| // If this declaration has the swift_private attribute, prepend "__" to the |
| // appropriate place. |
| SmallString<16> swiftPrivateScratch; |
| SmallString<16> swiftPrivateAliasScratch; |
| if (hasSwiftPrivate(D)) { |
| // Make the given name private. |
| // |
| // Returns true if this is not possible. |
| auto makeNamePrivate = [](bool isInitializer, |
| StringRef &baseName, |
| SmallVectorImpl<StringRef> &argumentNames, |
| CtorInitializerKind initKind, |
| SmallString<16> &scratch) -> bool { |
| scratch = "__"; |
| |
| if (isInitializer) { |
| // For initializers, prepend "__" to the first argument name. |
| if (argumentNames.empty()) { |
| // FIXME: ... unless it was from a factory method, for historical |
| // reasons. |
| if (initKind == CtorInitializerKind::Factory || |
| initKind == CtorInitializerKind::ConvenienceFactory) |
| return true; |
| |
| // FIXME: Record that we did this. |
| argumentNames.push_back("__"); |
| } else { |
| scratch += argumentNames[0]; |
| argumentNames[0] = scratch; |
| } |
| } else { |
| // For all other entities, prepend "__" to the base name. |
| scratch += baseName; |
| baseName = scratch; |
| } |
| |
| return false; |
| }; |
| |
| // Make the name private. |
| if (makeNamePrivate(isInitializer, baseName, argumentNames, |
| result.InitKind, swiftPrivateScratch)) |
| return result; |
| |
| // If we have an alias name, make it private as well. |
| if (!aliasBaseName.empty()) { |
| (void)makeNamePrivate(aliasIsInitializer, aliasBaseName, |
| aliasArgumentNames, CtorInitializerKind::Designated, |
| swiftPrivateAliasScratch); |
| } |
| } |
| |
| result.Imported = formDeclName(baseName, argumentNames, isFunction); |
| result.Alias = formDeclName(aliasBaseName, aliasArgumentNames, |
| aliasIsFunction); |
| return result; |
| } |
| |
| 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.getBaseName()).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()); |
| } |
| |
| /// Translate the "nullability" notion from API notes into an optional type |
| /// kind. |
| OptionalTypeKind ClangImporter::Implementation::translateNullability( |
| clang::NullabilityKind kind) { |
| switch (kind) { |
| case clang::NullabilityKind::NonNull: |
| return OptionalTypeKind::OTK_None; |
| |
| case clang::NullabilityKind::Nullable: |
| return OptionalTypeKind::OTK_Optional; |
| |
| case clang::NullabilityKind::Unspecified: |
| return OptionalTypeKind::OTK_ImplicitlyUnwrappedOptional; |
| } |
| } |
| |
| api_notes::APINotesReader* |
| ClangImporter::Implementation::getAPINotesForDecl(const clang::Decl *decl) { |
| if (auto module = getClangSubmoduleForDecl(decl)) |
| if (*module) |
| return getAPINotesForModule((*module)->getTopLevelModule()); |
| return nullptr; |
| } |
| |
| std::tuple<StringRef, api_notes::APINotesReader *, api_notes::APINotesReader *> |
| ClangImporter::Implementation::getAPINotesForContext( |
| const clang::ObjCContainerDecl *container) { |
| // Find the primary set of API notes, in the module where this container |
| // was declared. |
| api_notes::APINotesReader *primary = getAPINotesForDecl(container); |
| |
| StringRef name; |
| |
| // Find the secondary set of API notes, in the module where the original |
| // class was declared. |
| api_notes::APINotesReader *secondary = nullptr; |
| if (auto category = dyn_cast<clang::ObjCCategoryDecl>(container)) { |
| auto *objcClass = category->getClassInterface(); |
| secondary = getAPINotesForDecl(objcClass); |
| |
| // For categories, use the name of the class itself. |
| name = objcClass->getName(); |
| } else { |
| // For protocols and classes, we just need the class name. |
| name = container->getName(); |
| } |
| |
| // If the primary and secondary are the same. |
| if (primary == secondary) { |
| // Drop the secondary; we only care about the primary. |
| secondary = nullptr; |
| } |
| |
| return std::make_tuple(name, primary, secondary); |
| } |
| |
| void ClangImporter::Implementation::mergePropInfoIntoAccessor( |
| const clang::ObjCMethodDecl *method, api_notes::ObjCMethodInfo &methodInfo){ |
| |
| if (!method->isPropertyAccessor()) |
| return; |
| |
| const clang::ObjCPropertyDecl *pDecl = method->findPropertyDecl(); |
| if (!pDecl) |
| return; |
| |
| if (auto pInfo = getKnownObjCProperty(pDecl)) { |
| if (method->param_size() == 0) { |
| methodInfo.mergePropInfoIntoGetter(*pInfo); |
| } else { |
| assert(method->param_size() == 1); |
| methodInfo.mergePropInfoIntoSetter(*pInfo); |
| } |
| } |
| } |
| |
| static Optional<std::pair<api_notes::ContextID, api_notes::ObjCContextInfo>> lookupObjCContext(api_notes::APINotesReader *reader, |
| StringRef contextName, |
| const clang::ObjCContainerDecl *contextDecl) { |
| Optional<std::pair<api_notes::ContextID, api_notes::ObjCContextInfo>> |
| contextInfo; |
| |
| // Look for context information in the primary source. |
| if (isa<clang::ObjCProtocolDecl>(contextDecl)) |
| contextInfo = reader->lookupObjCProtocol(contextName); |
| else |
| contextInfo = reader->lookupObjCClass(contextName); |
| |
| return contextInfo; |
| } |
| |
| |
| Optional<api_notes::ObjCMethodInfo> |
| ClangImporter::Implementation::getKnownObjCMethod( |
| const clang::ObjCMethodDecl *method, |
| const clang::ObjCContainerDecl *container) { |
| if (!container) |
| container = cast<clang::ObjCContainerDecl>(method->getDeclContext()); |
| |
| // Figure out where to look for context information. |
| StringRef contextName; |
| api_notes::APINotesReader *primary; |
| api_notes::APINotesReader *secondary; |
| std::tie(contextName, primary, secondary) = getAPINotesForContext(container); |
| |
| // Map the selector. |
| SmallVector<StringRef, 2> selectorPieces; |
| api_notes::ObjCSelectorRef selectorRef; |
| selectorRef.NumPieces = method->getSelector().getNumArgs(); |
| for (unsigned i = 0, n = std::max(1u, selectorRef.NumPieces); i != n; ++i) { |
| selectorPieces.push_back(method->getSelector().getNameForSlot(i)); |
| } |
| selectorRef.Identifiers = selectorPieces; |
| |
| // Look for method and context information in the primary source. |
| Optional<api_notes::ObjCMethodInfo> methodInfo; |
| Optional<std::pair<api_notes::ContextID, api_notes::ObjCContextInfo>> |
| contextInfo; |
| |
| if (primary) { |
| // Look for context information in the primary source. |
| contextInfo = lookupObjCContext(primary, contextName, container); |
| |
| // Look for method information in the primary source. |
| if (contextInfo) { |
| methodInfo = primary->lookupObjCMethod(contextInfo->first, selectorRef, |
| method->isInstanceMethod()); |
| } |
| } |
| |
| // If we found method or class information in the primary source, return what |
| // we found. |
| if (methodInfo || contextInfo) { |
| if (!methodInfo) |
| methodInfo = api_notes::ObjCMethodInfo(); |
| |
| // If accessor, merge the property info in. |
| mergePropInfoIntoAccessor(method, *methodInfo); |
| |
| // Merge class information into the method. |
| *methodInfo |= contextInfo->second; |
| |
| return methodInfo; |
| } |
| |
| // Look for method information in the secondary source. |
| if (secondary) { |
| // Look for the context information in the secondary source. |
| contextInfo = lookupObjCContext(secondary, contextName, container); |
| |
| // Look for the method in the secondary source. We don't merge context |
| // information from the secondary source. |
| if (contextInfo) { |
| methodInfo = secondary->lookupObjCMethod(contextInfo->first, selectorRef, |
| method->isInstanceMethod()); |
| |
| if (!methodInfo) |
| methodInfo = api_notes::ObjCMethodInfo(); |
| |
| // If accessor, merge the property info in. |
| mergePropInfoIntoAccessor(method, *methodInfo); |
| |
| return methodInfo; |
| } |
| } |
| |
| return None; |
| } |
| |
| Optional<api_notes::ObjCContextInfo> |
| ClangImporter::Implementation::getKnownObjCContext( |
| const clang::ObjCContainerDecl *container) { |
| // Figure out where to look for context information. |
| StringRef name; |
| api_notes::APINotesReader *primary; |
| api_notes::APINotesReader *secondary; |
| std::tie(name, primary, secondary) = getAPINotesForContext(container); |
| |
| Optional<std::pair<api_notes::ContextID, api_notes::ObjCContextInfo>> |
| primaryInfo; |
| if (primary) |
| primaryInfo = lookupObjCContext(primary, name, container); |
| |
| Optional<std::pair<api_notes::ContextID, api_notes::ObjCContextInfo>> |
| secondaryInfo; |
| if (secondary) |
| secondaryInfo = lookupObjCContext(secondary, name, container); |
| |
| // If neither place had information about this class, we're done. |
| if (!primaryInfo && !secondaryInfo) |
| return None; |
| |
| api_notes::ObjCContextInfo info; |
| |
| // Merge in primary information, if available. |
| if (primaryInfo) { |
| info |= primaryInfo->second; |
| } |
| |
| // Merge in secondary information after stripping out anything that is not |
| // propagated from the context's defining module. |
| if (secondaryInfo) { |
| secondaryInfo->second.stripModuleLocalInfo(); |
| info |= secondaryInfo->second; |
| } |
| |
| return info; |
| } |
| |
| Optional<api_notes::ObjCPropertyInfo> |
| ClangImporter::Implementation::getKnownObjCProperty( |
| const clang::ObjCPropertyDecl *property) { |
| auto *container = cast<clang::ObjCContainerDecl>(property->getDeclContext()); |
| |
| // Figure out where to look for context information. |
| StringRef contextName; |
| api_notes::APINotesReader *primary; |
| api_notes::APINotesReader *secondary; |
| std::tie(contextName, primary, secondary) = getAPINotesForContext(container); |
| |
| // Look for property and context information in the primary source. |
| Optional<api_notes::ObjCPropertyInfo> propertyInfo; |
| Optional<std::pair<api_notes::ContextID, api_notes::ObjCContextInfo>> |
| contextInfo; |
| |
| if (primary) { |
| // Look for context information in the primary source. |
| contextInfo = lookupObjCContext(primary, contextName, container); |
| |
| // Look for property information in the primary source. |
| if (contextInfo) { |
| propertyInfo = primary->lookupObjCProperty(contextInfo->first, |
| property->getName()); |
| } |
| } |
| |
| // If we found property or class information in the primary source, return what |
| // we found. |
| if (propertyInfo || contextInfo) { |
| if (!propertyInfo) |
| propertyInfo = api_notes::ObjCPropertyInfo(); |
| |
| // Merge class information into the property. |
| *propertyInfo |= contextInfo->second; |
| |
| return propertyInfo; |
| } |
| |
| // Look for property information in the secondary source. |
| if (secondary) { |
| // Look for the context information in the secondary source. |
| contextInfo = lookupObjCContext(secondary, contextName, container); |
| |
| // Look for the property in the secondary source. We don't merge context |
| // information from the secondary source. |
| if (contextInfo) { |
| return secondary->lookupObjCProperty(contextInfo->first, |
| property->getName()); |
| } |
| } |
| |
| return None; |
| } |
| |
| Optional<api_notes::GlobalVariableInfo> |
| ClangImporter::Implementation::getKnownGlobalVariable( |
| const clang::VarDecl *global) { |
| if (auto notesReader = getAPINotesForDecl(global)) |
| return notesReader->lookupGlobalVariable(global->getName()); |
| |
| return None; |
| } |
| |
| Optional<api_notes::GlobalFunctionInfo> |
| ClangImporter::Implementation::getKnownGlobalFunction( |
| const clang::FunctionDecl *fn) { |
| if (auto notesReader = getAPINotesForDecl(fn)) |
| return notesReader->lookupGlobalFunction(fn->getName()); |
| |
| return None; |
| } |
| |
| bool ClangImporter::Implementation::hasDesignatedInitializers( |
| const clang::ObjCInterfaceDecl *classDecl) { |
| if (classDecl->hasDesignatedInitializers()) |
| return true; |
| |
| if (auto info = getKnownObjCContext(classDecl)) |
| return info->hasDesignatedInits(); |
| |
| return false; |
| } |
| |
| bool ClangImporter::Implementation::isDesignatedInitializer( |
| const clang::ObjCInterfaceDecl *classDecl, |
| const clang::ObjCMethodDecl *method) { |
| // If the information is on the AST, use it. |
| if (classDecl->hasDesignatedInitializers()) { |
| auto *methodParent = method->getClassInterface(); |
| if (!methodParent || |
| methodParent->getCanonicalDecl() == classDecl->getCanonicalDecl()) { |
| return method->hasAttr<clang::ObjCDesignatedInitializerAttr>(); |
| } |
| } |
| |
| if (auto info = getKnownObjCMethod(method, classDecl)) |
| return info->DesignatedInit; |
| |
| return false; |
| } |
| |
| bool ClangImporter::Implementation::isRequiredInitializer( |
| const clang::ObjCMethodDecl *method) { |
| // FIXME: No way to express this in Objective-C. |
| |
| if (auto info = getKnownObjCMethod(method)) |
| return info->Required; |
| |
| return false; |
| } |
| |
| FactoryAsInitKind ClangImporter::Implementation::getFactoryAsInit( |
| const clang::ObjCInterfaceDecl *classDecl, |
| const clang::ObjCMethodDecl *method) { |
| if (auto info = getKnownObjCMethod(method, classDecl)) |
| return info->getFactoryAsInitKind(); |
| |
| if (auto *customNameAttr = method->getAttr<clang::SwiftNameAttr>()) { |
| if (customNameAttr->getName().startswith("init(")) |
| return FactoryAsInitKind::AsInitializer; |
| else |
| return FactoryAsInitKind::AsClassMethod; |
| } |
| |
| return FactoryAsInitKind::Infer; |
| } |
| |
| /// Check if this method is declared in the context that conforms to |
| /// NSAccessibility. |
| static bool |
| isAccessibilityConformingContext(const clang::DeclContext *ctx) { |
| const clang::ObjCProtocolList *protocols = nullptr; |
| |
| if (auto protocol = dyn_cast<clang::ObjCProtocolDecl>(ctx)) { |
| if (protocol->getName() == "NSAccessibility") |
| return true; |
| return false; |
| } else if (auto interface = dyn_cast<clang::ObjCInterfaceDecl>(ctx)) |
| protocols = &interface->getReferencedProtocols(); |
| else if (auto category = dyn_cast<clang::ObjCCategoryDecl>(ctx)) |
| protocols = &category->getReferencedProtocols(); |
| else |
| return false; |
| |
| for (auto pi : *protocols) { |
| if (pi->getName() == "NSAccessibility") |
| return true; |
| } |
| return false; |
| |
| } |
| |
| /// 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 ClangImporter::Implementation::shouldSuppressDeclImport( |
| const clang::Decl *decl) { |
| if (auto objcMethod = dyn_cast<clang::ObjCMethodDecl>(decl)) { |
| // 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>(decl->getDeclContext())) |
| return isPotentiallyConflictingSetter(proto, objcMethod); |
| |
| return false; |
| } |
| |
| if (auto objcProperty = dyn_cast<clang::ObjCPropertyDecl>(decl)) { |
| // Suppress certain accessibility properties; they're imported as |
| // getter/setter pairs instead. |
| if (isAccessibilityDecl(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 dc = objcProperty->getDeclContext(); |
| auto objcClass = dyn_cast<clang::ObjCInterfaceDecl>(dc); |
| if (!objcClass) { |
| if (auto objcCategory = dyn_cast<clang::ObjCCategoryDecl>(dc)) |
| objcClass = objcCategory->getClassInterface(); |
| } |
| |
| if (objcClass) { |
| if (auto objcSuperclass = objcClass->getSuperClass()) { |
| if (auto getterMethod |
| = objcSuperclass->lookupInstanceMethod( |
| objcProperty->getGetterName())) { |
| if (!shouldSuppressDeclImport(getterMethod)) |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| return false; |
| } |
| |
| bool |
| ClangImporter::Implementation::isAccessibilityDecl(const clang::Decl *decl) { |
| |
| if (auto objCMethod = dyn_cast<clang::ObjCMethodDecl>(decl)) { |
| StringRef name = objCMethod->getSelector().getNameForSlot(0); |
| if (!(objCMethod->getSelector().getNumArgs() <= 1 && |
| (name.startswith("accessibility") || |
| name.startswith("setAccessibility") || |
| name.startswith("isAccessibility")))) { |
| return false; |
| } |
| |
| } else if (auto objCProperty = dyn_cast<clang::ObjCPropertyDecl>(decl)) { |
| if (!objCProperty->getName().startswith("accessibility")) |
| return false; |
| |
| } else { |
| llvm_unreachable("The declaration is not an ObjC property or method."); |
| } |
| |
| if (isAccessibilityConformingContext(decl->getDeclContext())) |
| return true; |
| |
| return false; |
| } |
| |
| bool ClangImporter::Implementation::isInitMethod( |
| const clang::ObjCMethodDecl *method) { |
| // init methods are always instance methods. |
| if (!method->isInstanceMethod()) return false; |
| |
| // init methods must be classified as such by Clang. |
| if (method->getMethodFamily() != clang::OMF_init) return false; |
| |
| // Swift restriction: init methods must start with the word "init". |
| auto selector = method->getSelector(); |
| return camel_case::getFirstWord(selector.getNameForSlot(0)) == "init"; |
| } |
| |
| bool ClangImporter::Implementation::shouldImportAsInitializer( |
| const clang::ObjCMethodDecl *method, |
| unsigned &prefixLength, |
| CtorInitializerKind &kind) { |
| /// Is this an initializer? |
| if (isInitMethod(method)) { |
| prefixLength = 4; |
| kind = CtorInitializerKind::Designated; |
| return true; |
| } |
| |
| // It must be a class method. |
| if (!method->isClassMethod()) return false; |
| |
| // Said class methods must be in an actual class. |
| auto objcClass = method->getClassInterface(); |
| if (!objcClass) return false; |
| |
| // Check whether we should try to import this factory method as an |
| // initializer. |
| switch (getFactoryAsInit(objcClass, method)) { |
| case FactoryAsInitKind::AsInitializer: |
| // Okay; check for the correct result type below. |
| prefixLength = 0; |
| break; |
| |
| case FactoryAsInitKind::Infer: { |
| // See if we can match the class name to the beginning of the first |
| // selector piece. |
| auto firstPiece = method->getSelector().getNameForSlot(0); |
| StringRef firstArgLabel = matchLeadingTypeName(firstPiece, |
| objcClass->getName()); |
| if (firstArgLabel.size() == firstPiece.size()) |
| return false; |
| |
| // FIXME: Factory methods cannot have dummy parameters added for |
| // historical reasons. |
| if (!firstArgLabel.empty() && method->getSelector().getNumArgs() == 0) |
| return false; |
| |
| // Store the prefix length. |
| prefixLength = firstPiece.size() - firstArgLabel.size(); |
| |
| // Continue checking the result type, below. |
| break; |
| } |
| |
| case FactoryAsInitKind::AsClassMethod: |
| return false; |
| } |
| |
| // Determine whether we have a suitable return type. |
| if (method->hasRelatedResultType()) { |
| // When the factory method has an "instancetype" result type, we |
| // can import it as a convenience factory method. |
| kind = CtorInitializerKind::ConvenienceFactory; |
| } else if (auto objcPtr = method->getReturnType() |
| ->getAs<clang::ObjCObjectPointerType>()) { |
| if (objcPtr->getInterfaceDecl() != objcClass) { |
| // FIXME: Could allow a subclass here, but the rest of the compiler |
| // isn't prepared for that yet. |
| return false; |
| } |
| |
| // Factory initializer. |
| kind = CtorInitializerKind::Factory; |
| } else { |
| // Not imported as an initializer. |
| return false; |
| } |
| |
| return true; |
| } |
| |
| #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) { |
| auto ExtSource = ClangCtx.getExternalSource(); |
| assert(ExtSource); |
| if (const clang::Decl *D = Node.getAsDecl()) |
| return ExtSource->getModule(D->getOwningModuleID()); |
| if (const clang::MacroInfo *MI = Node.getAsMacro()) |
| return ExtSource->getModule(MI->getOwningModuleID()); |
| |
| return nullptr; |
| } |
| |
| static bool isVisibleFromModule(const ClangModuleUnit *ModuleFilter, |
| const ValueDecl *VD) { |
| // Include a value from module X if: |
| // * no particular module was requested, or |
| // * module X was specifically requested. |
| if (!ModuleFilter) |
| return true; |
| |
| auto ContainingUnit = VD->getDeclContext()->getModuleScopeContext(); |
| if (ModuleFilter == ContainingUnit) |
| return true; |
| |
| auto Wrapper = dyn_cast<ClangModuleUnit>(ContainingUnit); |
| if (!Wrapper) |
| return false; |
| |
| auto ClangNode = VD->getClangNode(); |
| assert(ClangNode); |
| |
| auto &ClangASTContext = ModuleFilter->getClangASTContext(); |
| auto OwningClangModule = getClangOwningModule(ClangNode, ClangASTContext); |
| |
| // We don't handle Clang submodules; pop everything up to the top-level |
| // module. |
| if (OwningClangModule) |
| OwningClangModule = OwningClangModule->getTopLevelModule(); |
| |
| if (OwningClangModule == ModuleFilter->getClangModule()) |
| return true; |
| |
| if (auto D = ClangNode.getAsDecl()) { |
| // Handle redeclared decls. |
| if (isa<clang::FunctionDecl>(D) || isa<clang::VarDecl>(D) || |
| isa<clang::TypedefNameDecl>(D)) { |
| for (auto Redeclaration : D->redecls()) { |
| if (Redeclaration == D) |
| continue; |
| auto OwningClangModule = getClangOwningModule(Redeclaration, |
| ClangASTContext); |
| if (OwningClangModule) |
| OwningClangModule = OwningClangModule->getTopLevelModule(); |
| |
| if (OwningClangModule == ModuleFilter->getClangModule()) |
| return true; |
| } |
| } else if (isa<clang::TagDecl>(D)) { |
| for (auto Redeclaration : D->redecls()) { |
| if (Redeclaration == D) |
| continue; |
| if (!cast<clang::TagDecl>(Redeclaration)->isCompleteDefinition()) |
| continue; |
| auto OwningClangModule = getClangOwningModule(Redeclaration, |
| ClangASTContext); |
| if (OwningClangModule) |
| OwningClangModule = OwningClangModule->getTopLevelModule(); |
| |
| 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 = nullptr; |
| |
| public: |
| FilteringVisibleDeclConsumer(swift::VisibleDeclConsumer &consumer, |
| const ClangModuleUnit *CMU) |
| : NextConsumer(consumer), ModuleFilter(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 = nullptr; |
| |
| public: |
| FilteringDeclaredDeclConsumer(swift::VisibleDeclConsumer &consumer, |
| const ClangModuleUnit *CMU) |
| : NextConsumer(consumer), |
| ModuleFilter(CMU) {} |
| |
| void foundDecl(ValueDecl *VD, DeclVisibilityKind Reason) override { |
| if (isDeclaredInModule(ModuleFilter, VD)) |
| NextConsumer.foundDecl(VD, Reason); |
| } |
| }; |
| |
| /// A hack to blacklist particular types in the Darwin module on |
| /// Apple platforms. |
| class DarwinBlacklistDeclConsumer : public swift::VisibleDeclConsumer { |
| swift::VisibleDeclConsumer &NextConsumer; |
| clang::ASTContext &ClangASTContext; |
| |
| bool isBlacklisted(ValueDecl *VD) { |
| if (!VD->hasClangNode()) |
| return false; |
| |
| const clang::Module *clangModule = getClangOwningModule(VD->getClangNode(), |
| ClangASTContext); |
| if (!clangModule) |
| return false; |
| |
| if (clangModule->Name == "MacTypes") { |
| return llvm::StringSwitch<bool>(VD->getNameStr()) |
| .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 blacklist rather than a whitelist. |
| // 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: |
| DarwinBlacklistDeclConsumer(swift::VisibleDeclConsumer &consumer, |
| clang::ASTContext &clangASTContext) |
| : NextConsumer(consumer), ClangASTContext(clangASTContext) {} |
| |
| static bool needsBlacklist(const clang::Module *topLevelModule) { |
| if (!topLevelModule) |
| return false; |
| if (topLevelModule->Name == "Darwin") |
| return true; |
| if (topLevelModule->Name == "CoreServices") |
| return true; |
| return false; |
| } |
| |
| void foundDecl(ValueDecl *VD, DeclVisibilityKind Reason) override { |
| if (!isBlacklisted(VD)) |
| NextConsumer.foundDecl(VD, Reason); |
| } |
| }; |
| |
| } // unnamed namespace |
| |
| 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)) |
| receiver(imported); |
| } |
| } |
| } |
| ASTContext &Ctx = Impl.SwiftContext; |
| auto &ClangPP = Impl.getClangPreprocessor(); |
| for (clang::IdentifierInfo *II : Impl.BridgeHeaderMacros) { |
| if (auto *MI = ClangPP.getMacroInfo(II)) { |
| if (filter(MI)) { |
| Identifier Name = Ctx.getIdentifier(II->getName()); |
| if (Decl *imported = Impl.importMacro(Name, MI)) |
| 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; |
| |
| ASTContext &Ctx = Impl.SwiftContext; |
| 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)) |
| 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 *MD = dyn_cast<clang::MacroDefinitionRecord>(PPE)) { |
| auto *II = const_cast<clang::IdentifierInfo*>(MD->getName()); |
| if (auto *MI = ClangPP.getMacroInfo(II)) { |
| if (filter(MI)) { |
| Identifier Name = Ctx.getIdentifier(II->getName()); |
| if (Decl *imported = Impl.importMacro(Name, MI)) |
| 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){ |
| // Look for values in the bridging header's lookup table. |
| Impl.lookupValue(Impl.BridgingHeaderLookupTable, name, consumer); |
| |
| // Collect and sort the set of module names. |
| SmallVector<StringRef, 4> moduleNames; |
| for (const auto &entry : Impl.LookupTables) { |
| moduleNames.push_back(entry.first()); |
| } |
| llvm::array_pod_sort(moduleNames.begin(), moduleNames.end()); |
| |
| // Look for values in the module lookup tables. |
| for (auto moduleName : moduleNames) { |
| Impl.lookupValue(*Impl.LookupTables[moduleName].get(), name, consumer); |
| } |
| } |
| |
| void ClangModuleUnit::lookupVisibleDecls(Module::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); |
| |
| DarwinBlacklistDeclConsumer darwinBlacklistConsumer(filterConsumer, |
| getClangASTContext()); |
| |
| swift::VisibleDeclConsumer *actualConsumer = &filterConsumer; |
| if (lookupKind == NLKind::UnqualifiedLookup && |
| DarwinBlacklistDeclConsumer::needsBlacklist(clangModule)) { |
| actualConsumer = &darwinBlacklistConsumer; |
| } |
| |
| // Find the corresponding lookup table. |
| if (auto lookupTable = owner.Impl.findLookupTable(clangModule)) { |
| // Search it. |
| owner.Impl.lookupVisibleDecls(*lookupTable, *actualConsumer); |
| } |
| } |
| |
| namespace { |
| class VectorDeclPtrConsumer : public swift::VisibleDeclConsumer { |
| public: |
| SmallVectorImpl<Decl *> &Results; |
| explicit VectorDeclPtrConsumer(SmallVectorImpl<Decl *> &Decls) |
| : Results(Decls) {} |
| |
| virtual 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); |
| DarwinBlacklistDeclConsumer blacklistConsumer(filterConsumer, |
| getClangASTContext()); |
| |
| const clang::Module *topLevelModule = clangModule->getTopLevelModule(); |
| |
| swift::VisibleDeclConsumer *actualConsumer = &filterConsumer; |
| if (DarwinBlacklistDeclConsumer::needsBlacklist(topLevelModule)) |
| actualConsumer = &blacklistConsumer; |
| |
| // Find the corresponding lookup table. |
| if (auto lookupTable = owner.Impl.findLookupTable(topLevelModule)) { |
| // Search it. |
| owner.Impl.lookupVisibleDecls(*lookupTable, *actualConsumer); |
| |
| // Add the extensions produced by importing categories. |
| for (auto category : lookupTable->categories()) { |
| if (auto extension = cast_or_null<ExtensionDecl>( |
| owner.Impl.importDecl(category))) |
| results.push_back(extension); |
| } |
| } |
| } |
| |
| 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(Module::AccessPathTy accessPath, |
| DeclName name, NLKind lookupKind, |
| SmallVectorImpl<ValueDecl*> &results) const { |
| if (!Module::matchesAccessPath(accessPath, name)) |
| return; |
| |
| // FIXME: Ignore submodules, which are empty for now. |
| if (clangModule && clangModule->isSubModule()) |
| return; |
| |
| VectorDeclConsumer vectorWriter(results); |
| FilteringVisibleDeclConsumer filteringConsumer(vectorWriter, this); |
| |
| DarwinBlacklistDeclConsumer darwinBlacklistConsumer(filteringConsumer, |
| getClangASTContext()); |
| |
| swift::VisibleDeclConsumer *consumer = &filteringConsumer; |
| if (lookupKind == NLKind::UnqualifiedLookup && |
| DarwinBlacklistDeclConsumer::needsBlacklist(clangModule)) { |
| consumer = &darwinBlacklistConsumer; |
| } |
| |
| // Find the corresponding lookup table. |
| if (auto lookupTable = owner.Impl.findLookupTable(clangModule)) { |
| // Search it. |
| owner.Impl.lookupValue(*lookupTable, name, *consumer); |
| } |
| } |
| |
| void ClangImporter::loadExtensions(NominalTypeDecl *nominal, |
| unsigned previousGeneration) { |
| auto objcClass = dyn_cast_or_null<clang::ObjCInterfaceDecl>( |
| nominal->getClangDecl()); |
| if (!objcClass) { |
| if (auto typeResolver = Impl.getTypeResolver()) |
| typeResolver->resolveDeclSignature(nominal); |
| if (nominal->isObjC()) { |
| // Map the name. If we can't represent the Swift name in Clang, bail out |
| // now. |
| auto clangName = Impl.exportName(nominal->getName()); |
| if (!clangName) |
| return; |
| |
| auto &sema = Impl.Instance->getSema(); |
| |
| // Perform name lookup into the global scope. |
| // FIXME: Map source locations over. |
| clang::LookupResult lookupResult(sema, clangName, |
| clang::SourceLocation(), |
| clang::Sema::LookupOrdinaryName); |
| if (sema.LookupName(lookupResult, /*Scope=*/0)) { |
| // FIXME: Filter based on access path? C++ access control? |
| for (auto clangDecl : lookupResult) { |
| objcClass = dyn_cast<clang::ObjCInterfaceDecl>(clangDecl); |
| if (objcClass) |
| break; |
| } |
| } |
| } |
| } |
| |
| if (!objcClass) |
| return; |
| |
| // Import all of the visible categories. Simply loading them 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); |
| } |
| } |
| |
| 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); |
| |
| // 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 (auto method = dyn_cast_or_null<AbstractFunctionDecl>( |
| Impl.importDecl(objcMethod))) { |
| 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(Module::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.Impl.findLookupTable(clangModule)) { |
| // Search it. |
| owner.Impl.lookupObjCMembers(*lookupTable, name, consumer); |
| } |
| } |
| |
| void ClangModuleUnit::lookupClassMembers(Module::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.Impl.findLookupTable(clangModule)) { |
| // Search it. |
| owner.Impl.lookupAllObjCMembers(*lookupTable, consumer); |
| } |
| } |
| |
| void ClangModuleUnit::collectLinkLibraries( |
| Module::LinkLibraryCallback callback) const { |
| if (!clangModule) |
| 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) { |
| return "<imports>"; |
| } |
| return clangModule->getASTFile() |
| ? clangModule->getASTFile()->getName() : 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::Module *ClangImporter::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(); |
| } |
| |
| Decl *ClangImporter::importDeclCached(const clang::NamedDecl *ClangDecl) { |
| return Impl.importDeclCached(ClangDecl); |
| } |
| |
| bool ClangImporter::shouldIgnoreMacro(StringRef Name, |
| const clang::MacroInfo *Macro) { |
| return Impl.shouldIgnoreMacro(Name, Macro); |
| } |
| |
| void ClangImporter::printStatistics() const { |
| Impl.Instance->getModuleManager()->PrintStats(); |
| } |
| |
| void ClangImporter::verifyAllModules() { |
| #ifndef NDEBUG |
| if (Impl.ImportCounter == Impl.VerifiedImportCounter) |
| 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. |
| SmallVector<Decl *, 8> Decls; |
| for (auto &I : Impl.ImportedDecls) |
| if (Decl *D = I.second) |
| Decls.push_back(D); |
| |
| for (auto D : Decls) |
| verify(D); |
| |
| Impl.VerifiedImportCounter = Impl.ImportCounter; |
| #endif |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // ClangModule Implementation |
| //===----------------------------------------------------------------------===// |
| |
| ClangModuleUnit::ClangModuleUnit(Module &M, ClangImporter &owner, |
| const clang::Module *clangModule) |
| : LoadedFile(FileUnitKind::ClangModule, M), owner(owner), |
| clangModule(clangModule) { |
| } |
| |
| bool ClangModuleUnit::hasClangModule(Module *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(); |
| } |
| |
| Module *ClangModuleUnit::getAdapterModule() const { |
| if (!clangModule) |
| return nullptr; |
| |
| if (!isTopLevel()) { |
| // FIXME: Is this correct for submodules? |
| auto topLevel = clangModule->getTopLevelModule(); |
| auto wrapper = owner.Impl.getWrapperForModule(owner, topLevel); |
| return wrapper->getAdapterModule(); |
| } |
| |
| if (!adapterModule.getInt()) { |
| // FIXME: Include proper source location. |
| Module *M = getParentModule(); |
| ASTContext &Ctx = M->getASTContext(); |
| auto adapter = Ctx.getModule(Module::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<Module::ImportedModule> &imports, |
| Module::ImportFilter filter) const { |
| if (filter != Module::ImportFilter::Public) |
| imports.push_back({Module::AccessPathTy(), |
| getASTContext().getStdlibModule()}); |
| |
| if (!clangModule) { |
| // This is the special "imported headers" module. |
| if (filter != Module::ImportFilter::Private) { |
| imports.append(owner.Impl.ImportedHeaderExports.begin(), |
| owner.Impl.ImportedHeaderExports.end()); |
| } |
| return; |
| } |
| |
| auto topLevelAdapter = getAdapterModule(); |
| |
| SmallVector<clang::Module *, 8> imported; |
| clangModule->getExportedModules(imported); |
| if (filter != Module::ImportFilter::Public) { |
| if (filter == Module::ImportFilter::All) { |
| llvm::SmallPtrSet<clang::Module *, 8> knownModules; |
| imported.append(clangModule->Imports.begin(), clangModule->Imports.end()); |
| imported.erase(std::remove_if(imported.begin(), imported.end(), |
| [&](clang::Module *mod) -> bool { |
| return !knownModules.insert(mod).second; |
| }), |
| imported.end()); |
| } else { |
| llvm::SmallPtrSet<clang::Module *, 8> knownModules(imported.begin(), |
| imported.end()); |
| SmallVector<clang::Module *, 8> privateImports; |
| std::copy_if(clangModule->Imports.begin(), clangModule->Imports.end(), |
| std::back_inserter(privateImports), [&](clang::Module *mod) { |
| return knownModules.count(mod) == 0; |
| }); |
| imported.swap(privateImports); |
| } |
| |
| // 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); |
| } |
| |
| for (auto importMod : imported) { |
| auto wrapper = owner.Impl.getWrapperForModule(owner, 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 && |
| importTopLevel != clangModule->getTopLevelModule()) { |
| auto topLevelWrapper = owner.Impl.getWrapperForModule(owner, |
| importTopLevel); |
| imports.push_back({ Module::AccessPathTy(), |
| topLevelWrapper->getParentModule() }); |
| } |
| actualMod = wrapper->getParentModule(); |
| } else if (actualMod == topLevelAdapter) { |
| actualMod = wrapper->getParentModule(); |
| } |
| |
| assert(actualMod && "Missing imported adapter module"); |
| imports.push_back({Module::AccessPathTy(), actualMod}); |
| } |
| } |
| |
| void ClangModuleUnit::getImportedModulesForLookup( |
| SmallVectorImpl<Module::ImportedModule> &imports) const { |
| |
| if (!clangModule) { |
| // This is the special "imported headers" module. |
| imports.append(owner.Impl.ImportedHeaderExports.begin(), |
| owner.Impl.ImportedHeaderExports.end()); |
| return; |
| } |
| |
| // 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(); |
| auto topLevel = clangModule->getTopLevelModule(); |
| auto topLevelAdapter = getAdapterModule(); |
| |
| SmallVector<clang::Module *, 8> imported; |
| clangModule->getExportedModules(imported); |
| 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.Impl.getWrapperForModule(owner, 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.Impl.getWrapperForModule(owner, importMod); |
| |
| auto actualMod = wrapper->getAdapterModule(); |
| if (!actualMod || actualMod == topLevelAdapter) |
| actualMod = wrapper->getParentModule(); |
| |
| assert(actualMod && "Missing imported adapter module"); |
| imports.push_back({Module::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 |
| // --------------------------------------------------------------------------- |
| |
| clang::ModuleFileExtensionMetadata |
| ClangImporter::Implementation::getExtensionMetadata() const { |
| clang::ModuleFileExtensionMetadata metadata; |
| metadata.BlockName = "swift.lookup"; |
| metadata.MajorVersion = SWIFT_LOOKUP_TABLE_VERSION_MAJOR; |
| metadata.MinorVersion = SWIFT_LOOKUP_TABLE_VERSION_MINOR; |
| metadata.UserInfo = version::getSwiftFullVersion(); |
| return metadata; |
| } |
| |
| llvm::hash_code ClangImporter::Implementation::hashExtension( |
| llvm::hash_code code) const { |
| return llvm::hash_combine(code, StringRef("swift.lookup"), |
| SWIFT_LOOKUP_TABLE_VERSION_MAJOR, |
| SWIFT_LOOKUP_TABLE_VERSION_MINOR, |
| OmitNeedlessWords, |
| InferDefaultArguments); |
| } |
| |
| std::unique_ptr<clang::ModuleFileExtensionWriter> |
| ClangImporter::Implementation::createExtensionWriter(clang::ASTWriter &writer) { |
| // Local function to populate the lookup table. |
| auto populateTable = [this](clang::Sema &sema, SwiftLookupTable &table) { |
| for (auto decl |
| : sema.Context.getTranslationUnitDecl()->noload_decls()) { |
| // Skip anything from an AST file. |
| if (decl->isFromASTFile()) continue; |
| |
| // Skip non-named declarations. |
| auto named = dyn_cast<clang::NamedDecl>(decl); |
| if (!named) continue; |
| |
| // Add this entry to the lookup table. |
| addEntryToLookupTable(sema, table, named); |
| } |
| |
| // Add macros to the lookup table. |
| addMacrosToLookupTable(sema.Context, sema.getPreprocessor(), table); |
| }; |
| |
| return std::unique_ptr<clang::ModuleFileExtensionWriter>( |
| new SwiftLookupTableWriter(this, populateTable, writer)); |
| } |
| |
| std::unique_ptr<clang::ModuleFileExtensionReader> |
| ClangImporter::Implementation::createExtensionReader( |
| const clang::ModuleFileExtensionMetadata &metadata, |
| clang::ASTReader &reader, |
| clang::serialization::ModuleFile &mod, |
| const llvm::BitstreamCursor &stream) |
| { |
| // Make sure we have a compatible block. Since these values are part |
| // of the hash, it should never be wrong. |
| assert(metadata.BlockName == "swift.lookup"); |
| assert(metadata.MajorVersion == SWIFT_LOOKUP_TABLE_VERSION_MAJOR); |
| assert(metadata.MinorVersion == SWIFT_LOOKUP_TABLE_VERSION_MINOR); |
| |
| // Check whether we already have an entry in the set of lookup tables. |
| auto &entry = LookupTables[mod.ModuleName]; |
| if (entry) return nullptr; |
| |
| // Local function used to remove this entry when the reader goes away. |
| std::string moduleName = mod.ModuleName; |
| auto onRemove = [this, moduleName]() { |
| LookupTables.erase(moduleName); |
| }; |
| |
| // Create the reader. |
| auto tableReader = SwiftLookupTableReader::create(this, reader, mod, onRemove, |
| stream); |
| if (!tableReader) return nullptr; |
| |
| // Create the lookup table. |
| entry.reset(new SwiftLookupTable(tableReader.get())); |
| |
| // Return the new reader. |
| return std::move(tableReader); |
| } |
| |
| SwiftLookupTable *ClangImporter::Implementation::findLookupTable( |
| const clang::Module *clangModule) { |
| // If the Clang module is null, use the bridging header lookup table. |
| if (!clangModule) |
| return &BridgingHeaderLookupTable; |
| |
| // 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(); |
| } |
| |
| /// 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::Preprocessor &pp, |
| StringRef name, |
| 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; |
| } |
| |
| // Check whether the macro is defined. |
| // FIXME: We could get the wrong macro definition here. |
| return pp.isMacroDefined(name); |
| } |
| |
| void ClangImporter::Implementation::lookupValue( |
| SwiftLookupTable &table, DeclName name, |
| VisibleDeclConsumer &consumer) { |
| auto clangTU = getClangASTContext().getTranslationUnitDecl(); |
| auto &clangPP = getClangPreprocessor(); |
| auto baseName = name.getBaseName().str(); |
| |
| for (auto entry : table.lookup(name.getBaseName().str(), clangTU)) { |
| // If the entry is not visible, skip it. |
| if (!isVisibleClangEntry(clangPP, baseName, 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())); |
| if (!decl) continue; |
| } else { |
| // Try to import a macro. |
| auto clangMacro = entry.get<clang::MacroInfo *>(); |
| decl = importMacro(name.getBaseName(), clangMacro); |
| if (!decl) 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. |
| if (decl->getFullName().matchesRef(name)) |
| consumer.foundDecl(decl, DeclVisibilityKind::VisibleAtTopLevel); |
| |
| // If there is an alternate declaration and the name matches, |
| // report this result. |
| if (auto alternate = getAlternateDecl(decl)) { |
| if (alternate->getFullName().matchesRef(name)) |
| consumer.foundDecl(alternate, DeclVisibilityKind::VisibleAtTopLevel); |
| } |
| } |
| } |
| |
| 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, SwiftContext.getIdentifier(baseName), consumer); |
| } |
| } |
| |
| void ClangImporter::Implementation::lookupObjCMembers( |
| SwiftLookupTable &table, |
| DeclName name, |
| VisibleDeclConsumer &consumer) { |
| auto &clangPP = getClangPreprocessor(); |
| auto baseName = name.getBaseName().str(); |
| |
| for (auto clangDecl : table.lookupObjCMembers(baseName)) { |
| // If the entry is not visible, skip it. |
| if (!isVisibleClangEntry(clangPP, baseName, clangDecl)) continue; |
| |
| // Import the declaration. |
| auto decl = cast_or_null<ValueDecl>(importDeclReal(clangDecl)); |
| if (!decl) |
| continue; |
| |
| // If the name we found matches, report the declaration. |
| if (decl->getFullName().matchesRef(name)) |
| consumer.foundDecl(decl, DeclVisibilityKind::DynamicLookup); |
| |
| // Check for an alternate declaration; if it's name matches, |
| // report it. |
| if (auto alternate = getAlternateDecl(decl)) { |
| if (alternate->getFullName().matchesRef(name)) |
| consumer.foundDecl(alternate, DeclVisibilityKind::DynamicLookup); |
| } |
| } |
| } |
| |
| 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, SwiftContext.getIdentifier(baseName), consumer); |
| } |
| } |
| |
| 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(); |
| } |