Files
swift-mirror/lib/ClangImporter/ClangImporter.cpp
Dmitri Hrybenko ab408d4dc3 Update the compiler and SDK overlay for nullability and generics in Foundation
We have an SPI between the Swift compiler and Foundation based on the
SWIFT_SDK_OVERLAY_FOUNDATION_EPOCH preprocessor macro that allows us to
request the new API.  rdar://20270080 tracks removing it.

Swift SVN r26475
2015-03-24 02:18:06 +00:00

2860 lines
99 KiB
C++

//===--- ClangImporter.cpp - Import Clang Modules -------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 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/ClangImporter/ClangImporterOptions.h"
#include "swift/Parse/Lexer.h"
#include "clang/AST/ASTContext.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/LLVMModuleProvider.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Frontend/Utils.h"
#include "clang/Serialization/ASTReader.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/Statistic.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/CrashRecoveryContext.h"
#include "llvm/Support/Path.h"
#include <algorithm>
#include <memory>
using namespace swift;
//===--------------------------------------------------------------------===//
// Importer statistics
//===--------------------------------------------------------------------===//
#define DEBUG_TYPE "Clang module importer"
STATISTIC(NumNullaryMethodNames,
"nullary selectors imported");
STATISTIC(NumUnaryMethodNames,
"unary selectors imported");
STATISTIC(NumNullaryInitMethodsMadeUnary,
"nullary Objective-C init methods turned into unary initializers");
STATISTIC(NumMultiMethodNames,
"multi-part selector method names imported");
STATISTIC(NumPrepositionSplitMethodNames,
"selectors where the first selector piece was split on a "
"preposition");
STATISTIC(NumLinkingVerbNonSplits,
"selectors where splitting was prevented by a linking verb ");
STATISTIC(NumLinkingVerbNonSplitsPreposition,
"selectors where preposition splitting was prevented by a linking "
"verb ");
STATISTIC(NumPrepositionTrailingFirstPiece,
"selectors where the first piece ends in a preposition");
STATISTIC(NumSelectorsNotSplit,
"selectors that were not split (for any reason)");
STATISTIC(NumMethodsMissingFirstArgName,
"selectors where the first argument name is missing");
STATISTIC(NumFactoryMethodsNullary,
"# of factory methods not mapped due to nullary with long name");
STATISTIC(NumInitsDroppedWith,
"# of initializer selectors from which \"with\" was dropped");
STATISTIC(NumInitsPrepositionSplit,
"# of initializer selectors where the split was on a preposition");
STATISTIC(NumInitsNonPrepositionSplit,
"# of initializer selectors where the split wasn't on a preposition");
// 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) 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;
}
};
}
ClangImporter::ClangImporter(ASTContext &ctx,
const ClangImporterOptions &clangImporterOpts,
DependencyTracker *tracker)
: ClangModuleLoader(tracker),
Impl(*new Implementation(ctx, clangImporterOpts))
{
}
ClangImporter::~ClangImporter() {
delete &Impl;
}
void ClangImporter::setTypeResolver(LazyResolver &resolver) {
Impl.setTypeResolver(&resolver);
}
void ClangImporter::clearTypeResolver() {
Impl.setTypeResolver(nullptr);
}
#pragma mark Module loading
#ifdef NDEBUG
#define SHIMS_INCLUDE_FLAG "-isystem"
#else
#define SHIMS_INCLUDE_FLAG "-I"
#endif
std::unique_ptr<ClangImporter>
ClangImporter::create(ASTContext &ctx,
const ClangImporterOptions &importerOpts,
DependencyTracker *tracker) {
const llvm::Triple &triple = ctx.LangOpts.Target;
std::unique_ptr<ClangImporter> importer{
new ClangImporter(ctx, importerOpts, tracker)
};
SearchPathOptions &searchPathOpts = ctx.SearchPathOpts;
// Construct the invocation arguments for the current target.
// Add target-independent options first.
std::vector<std::string> invocationArgStrs = {
// Enable modules.
"-fmodules",
// Don't emit LLVM IR.
"-fsyntax-only",
"-femit-all-decls",
"-target", triple.str(),
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()) {
std::vector<std::string> extraArgs = {
// 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=2",
};
invocationArgStrs.insert(invocationArgStrs.end(), extraArgs.begin(),
extraArgs.end());
} else {
std::vector<std::string> extraArgs = {
// 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",
};
invocationArgStrs.insert(invocationArgStrs.end(), extraArgs.begin(),
extraArgs.end());
}
invocationArgStrs.push_back(Implementation::moduleImportBufferName);
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 (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");
}
}
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);
}
for (auto path : searchPathOpts.ImportSearchPaths) {
invocationArgStrs.push_back("-I");
invocationArgStrs.push_back(path);
}
for (auto path : searchPathOpts.FrameworkSearchPaths) {
invocationArgStrs.push_back("-F");
invocationArgStrs.push_back(path);
}
const std::string &moduleCachePath = importerOpts.ModuleCachePath;
// Set the module cache path.
if (!moduleCachePath.empty()) {
invocationArgStrs.push_back("-fmodules-cache-path=");
invocationArgStrs.back().append(moduleCachePath);
}
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", CLANG_VERSION_STRING);
// 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);
}
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());
// Create a compiler instance.
importer->Impl.Instance.reset(new CompilerInstance(
clang::SharedModuleProvider::Create<clang::LLVMModuleProvider>()));
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());
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();
auto *CB = new HeaderImportCallbacks(*importer, importer->Impl);
clangPP.addPPCallbacks(std::unique_ptr<clang::PPCallbacks>(CB));
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();
clang::Parser::DeclGroupPtrTy parsed;
while (!importer->Impl.Parser->ParseTopLevelDecl(parsed)) {}
// 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::importHeader(
Module *adapter, StringRef headerName, SourceLoc diagLoc,
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;
assert(adapter);
ImportedHeaderOwners.push_back(adapter);
bool hadError = clangDiags.hasErrorOccurred();
clang::Preprocessor &pp = getClangPreprocessor();
clang::SourceManager &sourceMgr = getClangASTContext().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)) {}
pp.EndSourceFile();
if (!hadError && clangDiags.hasErrorOccurred()) {
SwiftContext.Diags.diagnose(diagLoc, diag::bridging_header_error,
headerName);
}
bumpGeneration();
}
void 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)
};
Impl.importHeader(adapter, header, diagLoc, std::move(sourceBuffer));
}
void ClangImporter::importBridgingHeader(StringRef header, Module *adapter,
SourceLoc diagLoc) {
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;
}
llvm::SmallString<128> importLine{"#import \""};
importLine += header;
importLine += "\"\n";
std::unique_ptr<llvm::MemoryBuffer> sourceBuffer{
llvm::MemoryBuffer::getMemBufferCopy(
importLine, Implementation::bridgingHeaderBufferName)
};
Impl.importHeader(adapter, header, diagLoc, 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->getSharedModuleProvider());
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;
}
Module *ClangImporter::loadModule(
SourceLoc importLoc,
ArrayRef<std::pair<Identifier, SourceLoc>> path) {
// Don't even try to load the module if the Clang context is in a bad way.
// It could cause a crash.
auto &clangContext = Impl.getClangASTContext();
auto &clangDiags = clangContext.getDiagnostics();
if (clangDiags.hasFatalErrorOccurred())
return nullptr;
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.importSourceLoc(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++);
return Impl.Instance->loadModule(clangImportLoc, path, visibility,
/*IsInclusionDirective=*/false);
};
// 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);
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->Name];
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),
SplitPrepositions(ctx.LangOpts.SplitPrepositions),
InferImplicitProperties(opts.InferImplicitProperties),
ImportForwardDeclarations(opts.ImportForwardDeclarations)
{
// 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()) {
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.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);
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->getOwningModule();
}
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::importSourceLoc(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.
static bool isSwiftReservedName(StringRef name) {
tok kind = Lexer::kindOfIdentifier(name, /*InSILMode=*/false);
return (kind != tok::identifier);
}
clang::DeclarationName
ClangImporter::Implementation::importName(Identifier name) {
// FIXME: When we start dealing with C++, we can map over some operator
// names.
if (name.empty() || name.isOperator())
return clang::DeclarationName();
// Map the identifier. If it's some kind of keyword, it can't be mapped.
auto ident = &Instance->getASTContext().Idents.get(name.str());
if (ident->getTokenID() != clang::tok::identifier)
return clang::DeclarationName();
return ident;
}
Identifier
ClangImporter::Implementation::importName(clang::DeclarationName name,
StringRef suffix,
StringRef removePrefix) {
// FIXME: At some point, we'll be able to import operators as well.
if (!name || name.getNameKind() != clang::DeclarationName::Identifier)
return Identifier();
StringRef nameStr = name.getAsIdentifierInfo()->getName();
// Remove the prefix, if any.
if (!removePrefix.empty()) {
if (nameStr.startswith(removePrefix)) {
nameStr = nameStr.slice(removePrefix.size(), nameStr.size());
}
}
// Get the Swift identifier.
if (suffix.empty()) {
return SwiftContext.getIdentifier(nameStr);
}
// Append the suffix, and try again.
llvm::SmallString<64> nameBuf;
nameBuf += nameStr;
nameBuf += suffix;
return SwiftContext.getIdentifier(nameBuf);
}
/// Split the given selector piece at the given index, updating
/// capitalization as required.
///
/// \param selector The selector piece.
/// \param index The index at which to split.
/// \param buffer Buffer used for scratch space.
///
/// \returns a pair of strings \c (before,after), where \c after has
/// has its capitalization adjusted if necessary.
static std::pair<StringRef, StringRef>
splitSelectorPieceAt(StringRef selector, unsigned index,
SmallVectorImpl<char> &buffer) {
// If the split point is at the end of the selector, the solution is
// trivial.
if (selector.str().size() == index) {
return { selector, "" };
}
// Split at the index and lowercase the parameter name.
return { selector.substr(0, index),
camel_case::toLowercaseWord(selector.substr(index), buffer) };
}
/// Split the first selector piece into a function name and first
/// parameter name, if possible.
///
/// \param selector The selector piece to split.
/// \param scratch Scratch space to use when forming strings.
///
/// \returns a (function name, first parameter name) pair describing
/// the split. If no split is possible, the function name will be \c
/// selector and the parameter name will be empty.
static std::pair<StringRef, StringRef>
splitFirstSelectorPiece(StringRef selector,
SmallVectorImpl<char> &scratch) {
// Get the camelCase words in this selector.
auto words = camel_case::getWords(selector);
if (words.empty())
return { selector, "" };
// If "init" is the first word, we have an initializer.
if (*words.begin() == "init") {
return splitSelectorPieceAt(selector, 4, scratch);
}
// Scan words from the back of the selector looking for a
// preposition.
auto lastPrep = words.rbegin();
auto end = words.rend();
for (; lastPrep != end; ++lastPrep) {
// If we found a linking verb, don't split.
if (isLinkingVerb(*lastPrep)) {
++NumLinkingVerbNonSplits;
return { selector, "" };
}
if (getPrepositionKind(*lastPrep) != PK_None) {
// Found a preposition.
break;
}
}
// If we found a preposition, split here.
if (lastPrep != end) {
// .. unless there is a linking verb.
for (auto i = std::next(lastPrep); i != end; ++i) {
// If we found a linking verb, don't split.
if (isLinkingVerb(*i)) {
++NumLinkingVerbNonSplitsPreposition;
return { selector, "" };
}
}
++NumPrepositionSplitMethodNames;
if (lastPrep == words.rbegin())
++NumPrepositionTrailingFirstPiece;
return splitSelectorPieceAt(selector,
std::prev(lastPrep.base()).getPosition(),
scratch);
}
// We did not find a preposition. None to split.
++NumSelectorsNotSplit;
return { selector, "" };
}
/// Import an argument name.
static Identifier importArgName(ASTContext &ctx, StringRef name, bool dropWith){
// Simple case: empty name.
if (name.empty())
return Identifier();
llvm::SmallString<16> scratch;
auto words = camel_case::getWords(name);
auto firstWord = *words.begin();
// If we're dropping "with", handle that now.
if (dropWith) {
// If the first word is "with"...
StringRef argName;
if (name.size() > 4 &&
camel_case::sameWordIgnoreFirstCase(firstWord, "with")) {
// Drop it.
++NumInitsDroppedWith;
auto iter = words.begin();
++iter;
argName = name.substr(iter.getPosition());
// Don't drop "with" if the resulting arg is a reserved name.
if (isSwiftReservedName(camel_case::toLowercaseWord(argName, scratch))) {
argName = name;
}
} else {
// If we're tracking statistics, check whether the name starts with
// a preposition.
if (llvm::AreStatisticsEnabled()) {
if (getPrepositionKind(firstWord))
++NumInitsPrepositionSplit;
else
++NumInitsNonPrepositionSplit;
}
argName = name;
}
return ctx.getIdentifier(camel_case::toLowercaseWord(argName, scratch));
}
// If the first word isn't a non-directional preposition, lowercase
// it to form the argument name.
if (!ctx.LangOpts.SplitPrepositions ||
getPrepositionKind(firstWord) != PK_Nondirectional)
return ctx.getIdentifier(camel_case::toLowercaseWord(name, scratch));
// The first word is a non-directional preposition.
// If there's only one word, we're left with no argument name.
if (firstWord.size() == name.size())
return Identifier();
return ctx.getIdentifier(
camel_case::toLowercaseWord(name.substr(firstWord.size()), scratch));
}
/// Map an Objective-C selector name to a Swift method name.
static DeclName mapSelectorName(ASTContext &ctx,
ObjCSelector selector,
bool isInitializer) {
// Zero-argument selectors.
if (selector.getNumArgs() == 0) {
++NumNullaryMethodNames;
auto name = selector.getSelectorPieces()[0];
StringRef nameText = name.empty()? "" : name.str();
// Simple case.
if (!isInitializer || nameText.size() == 4)
return DeclName(ctx, name, { });
// This is an initializer with no parameters but a name that
// contains more than 'init', so synthesize an argument to capture
// what follows 'init'.
++NumNullaryInitMethodsMadeUnary;
assert(camel_case::getFirstWord(nameText).equals("init"));
auto baseName = ctx.Id_init;
auto argName = importArgName(ctx, nameText.substr(4), /*dropWith=*/true);
return DeclName(ctx, baseName, argName);
}
// Determine the base name and first argument name.
Identifier baseName;
SmallVector<Identifier, 2> argumentNames;
Identifier firstPiece = selector.getSelectorPieces()[0];
StringRef firstPieceText = firstPiece.empty()? "" : firstPiece.str();
if (isInitializer) {
assert(camel_case::getFirstWord(firstPieceText).equals("init"));
baseName = ctx.Id_init;
argumentNames.push_back(
importArgName(ctx, firstPieceText.substr(4), /*dropWith=*/true));
} else if (ctx.LangOpts.SplitPrepositions) {
llvm::SmallString<16> scratch;
StringRef funcName, firstArgName;
std::tie(funcName, firstArgName) = splitFirstSelectorPiece(firstPieceText,
scratch);
baseName = ctx.getIdentifier(funcName);
argumentNames.push_back(importArgName(ctx, firstArgName,
/*dropWith=*/false));
} else {
baseName = firstPiece;
argumentNames.push_back(Identifier());
}
if (argumentNames[0].empty())
++NumMethodsMissingFirstArgName;
// Determine the remaining argument names.
unsigned n = selector.getNumArgs();
if (n == 1)
++NumUnaryMethodNames;
else
++NumMultiMethodNames;
for (auto piece : selector.getSelectorPieces().slice(1)) {
if (piece.empty())
argumentNames.push_back(piece);
else
argumentNames.push_back(importArgName(ctx, piece.str(),
/*dropWith=*/false));
}
return DeclName(ctx, baseName, argumentNames);
}
namespace {
/// Function object used to create Clang selectors from strings.
class CreateSelector {
ASTContext &Ctx;
public:
CreateSelector(ASTContext &ctx) : Ctx(ctx){ }
template<typename ...Strings>
ObjCSelector operator()(unsigned numParams, Strings ...strings) const {
Identifier pieces[sizeof...(Strings)] = {
(strings[0]? Ctx.getIdentifier(strings) : Identifier())...
};
assert((numParams == 0 && sizeof...(Strings) == 1) ||
(numParams > 0 && sizeof...(Strings) == numParams));
return ObjCSelector(Ctx, numParams, pieces);
}
};
/// Function object used to create Swift method names from strings.
class CreateMethodName {
ASTContext &Ctx;
Identifier BaseName;
public:
CreateMethodName(ASTContext &ctx, StringRef baseName)
: Ctx(ctx)
{
BaseName = Ctx.getIdentifier(baseName);
}
template<typename ...Strings>
DeclName operator()(Strings ...strings) const {
Identifier pieces[sizeof...(Strings)] = {
(strings[0]? Ctx.getIdentifier(strings) : Identifier())...
};
return DeclName(Ctx, BaseName, pieces);
}
};
}
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::importSelector(DeclName name,
bool allowSimpleName) {
if (!allowSimpleName && name.isSimpleName())
return {};
clang::ASTContext &ctx = getClangASTContext();
SmallVector<clang::IdentifierInfo *, 8> pieces;
pieces.push_back(importName(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(importName(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(importName(piece).getAsIdentifierInfo());
return getClangASTContext().Selectors.getSelector(selector.getNumArgs(),
pieces.data());
}
DeclName ClangImporter::Implementation::mapSelectorToDeclName(
ObjCSelector selector,
bool isInitializer)
{
// If we haven't computed any selector mappings before, load the
// default mappings.
if (SelectorMappings.empty() && SplitPrepositions) {
// Function object that creates a selector.
CreateSelector createSelector(SwiftContext);
#define MAP_SELECTOR(Selector, ForMethod, ForInitializer, BaseName, ArgNames) \
{ \
auto selector = createSelector Selector; \
CreateMethodName createMethodName(SwiftContext, BaseName); \
auto methodName = createMethodName ArgNames; \
assert((ForMethod || ForInitializer) && \
"Must be for a method or initializer"); \
assert(selector.getNumArgs() == methodName.getArgumentNames().size()); \
if (ForMethod) \
SelectorMappings[{selector, false}] = methodName; \
if (ForInitializer) { \
assert(methodName.getBaseName() == SwiftContext.Id_init); \
SelectorMappings[{selector, true}] = methodName; \
} \
}
#include "MappedNames.def"
}
// Check whether we've already mapped this selector.
auto known = SelectorMappings.find({selector, isInitializer});
if (known != SelectorMappings.end())
return known->second;
// Map the selector.
auto result = mapSelectorName(SwiftContext, selector, isInitializer);
// Cache the result and return.
SelectorMappings[{selector, isInitializer}] = result;
return result;
}
DeclName ClangImporter::Implementation::mapFactorySelectorToInitializerName(
ObjCSelector selector,
StringRef className) {
auto firstPiece = selector.getSelectorPieces()[0];
if (firstPiece.empty())
return DeclName();
// If the first selector piece starts with an acronym, and that acronym
// matches the end of the first word of the class name, pretend the class
// name starts at the beginning of that acronym. This effectively
// adjusts the class name "NSURL" to "URL" when mapping a selector
// starting with "URL".
auto firstPieceStr = firstPiece.str();
if (firstPieceStr.size() > 1 &&
clang::isUppercase(firstPieceStr[0]) &&
clang::isUppercase(firstPieceStr[1])) {
auto selectorAcronym = camel_case::getFirstWord(firstPieceStr);
auto classNameStart = camel_case::getFirstWord(className);
if (classNameStart.endswith(selectorAcronym)) {
className = className.substr(
classNameStart.size() - selectorAcronym.size());
}
}
// Match the camelCase beginning of the first selector piece to the
// ending of the class name.
auto methodWords = camel_case::getWords(firstPieceStr);
auto classWords = camel_case::getWords(className);
auto methodWordIter = methodWords.begin(),
methodWordIterEnd = methodWords.end();
auto classWordRevIter = classWords.rbegin(),
classWordRevIterEnd = classWords.rend();
// Find the last instance of the first word in the method's name within
// the words in the class name.
while (classWordRevIter != classWordRevIterEnd &&
!camel_case::sameWordIgnoreFirstCase(*methodWordIter,
*classWordRevIter)) {
++classWordRevIter;
}
// If we didn't find the first word in the method's name at all, we're
// done.
if (classWordRevIter == classWordRevIterEnd)
return DeclName();
// Now, match from the first word up until the end of the class.
auto classWordIter = classWordRevIter.base(),
classWordIterEnd = classWords.end();
++methodWordIter;
while (classWordIter != classWordIterEnd &&
methodWordIter != methodWordIterEnd &&
camel_case::sameWordIgnoreFirstCase(*classWordIter,
*methodWordIter)) {
++classWordIter;
++methodWordIter;
}
// If we didn't reach the end of the class name, don't match.
if (classWordIter != classWordIterEnd)
return DeclName();
// We found the chopping point. Form the first argument name.
llvm::SmallString<32> scratch;
SmallVector<Identifier, 4> argumentNames;
argumentNames.push_back(
importArgName(SwiftContext,
splitSelectorPieceAt(firstPieceStr,
methodWordIter.getPosition(),
scratch).second,
/*dropWith=*/true));
// Handle nullary factory methods.
if (selector.getNumArgs() == 0) {
if (argumentNames[0].empty())
return DeclName(SwiftContext, SwiftContext.Id_init, { });
// We don't have a convenience place to put the remaining argument name,
// so leave it as a factory method.
++NumFactoryMethodsNullary;
return DeclName();
}
// Map the remaining selector pieces.
for (auto piece : selector.getSelectorPieces().slice(1)) {
if (piece.empty())
argumentNames.push_back(piece);
else
argumentNames.push_back(importArgName(SwiftContext, piece.str(),
/*dropWith=*/false));
}
return DeclName(SwiftContext, SwiftContext.Id_init, argumentNames);
}
/// 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();
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;
}
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;
}
#pragma mark Name lookup
void ClangImporter::lookupValue(Identifier name, VisibleDeclConsumer &consumer){
auto &pp = Impl.Instance->getPreprocessor();
auto &sema = Impl.Instance->getSema();
// Map the name. If we can't represent the Swift name in Clang, bail out now.
auto clangName = Impl.importName(name);
if (!clangName)
return;
// See if there's a preprocessor macro we can import by this name.
clang::IdentifierInfo *clangID = clangName.getAsIdentifierInfo();
if (clangID && clangID->hasMacroDefinition()) {
if (auto clangMacro = pp.getMacroInfo(clangID)) {
if (auto valueDecl = Impl.importMacro(name, clangMacro)) {
consumer.foundDecl(valueDecl, DeclVisibilityKind::VisibleAtTopLevel);
}
}
}
bool FoundType = false;
bool FoundAny = false;
auto processResults = [&](clang::LookupResult &result) {
// FIXME: Filter based on access path? C++ access control?
for (auto decl : result) {
if (auto swiftDecl = Impl.importDeclReal(decl->getUnderlyingDecl())) {
if (auto valueDecl = dyn_cast<ValueDecl>(swiftDecl)) {
// If the importer gave us a declaration from the stdlib, make sure
// it does not show up in the lookup results for the imported module.
if (valueDecl->getDeclContext()->isModuleScopeContext() &&
valueDecl->getModuleContext() == Impl.getStdlibModule())
continue;
// Check that we didn't pick up something with a remapped name.
if (valueDecl->getName() != name)
continue;
consumer.foundDecl(valueDecl, DeclVisibilityKind::VisibleAtTopLevel);
FoundType = FoundType || isa<TypeDecl>(valueDecl);
FoundAny = true;
}
}
}
};
// 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=*/nullptr))
processResults(lookupResult);
if (!FoundType) {
// Look up a tag name if we did not find a type with this name already.
// We don't want to introduce multiple types with same name.
lookupResult.clear(clang::Sema::LookupTagName);
if (sema.LookupName(lookupResult, /*Scope=*/nullptr))
processResults(lookupResult);
}
// Look up protocol names as well.
lookupResult.clear(clang::Sema::LookupObjCProtocolName);
if (sema.LookupName(lookupResult, /*Scope=*/nullptr)) {
processResults(lookupResult);
} else if (!FoundAny && name.str().endswith(SWIFT_PROTOCOL_SUFFIX)) {
auto noProtoNameStr = name.str().drop_back(strlen(SWIFT_PROTOCOL_SUFFIX));
auto protoIdent = &Impl.getClangASTContext().Idents.get(noProtoNameStr);
lookupResult.clear(clang::Sema::LookupObjCProtocolName);
lookupResult.setLookupName(protoIdent);
if (sema.LookupName(lookupResult, /*Scope=*/nullptr))
processResults(lookupResult);
lookupResult.setLookupName(clangName);
}
// If we *still* haven't found anything, try looking for '<name>Ref'.
// Eventually, this should be optimized by recognizing this case when
// generating the clang module.
if (!FoundAny && clangID) {
lookupResult.clear(clang::Sema::LookupOrdinaryName);
llvm::SmallString<128> buffer;
buffer += clangID->getName();
buffer += SWIFT_CFTYPE_SUFFIX;
auto refIdent = &Impl.Instance->getASTContext().Idents.get(buffer.str());
lookupResult.setLookupName(refIdent);
if (sema.LookupName(lookupResult, /*Scope=*/0)) {
// FIXME: Filter based on access path? C++ access control?
for (auto decl : lookupResult) {
if (auto swiftDecl = Impl.importDeclReal(decl->getUnderlyingDecl())) {
auto alias = dyn_cast<TypeAliasDecl>(swiftDecl);
if (!alias) continue;
Type underlyingTy = alias->getUnderlyingType();
TypeDecl *underlying = nullptr;
if (auto anotherAlias =
dyn_cast<NameAliasType>(underlyingTy.getPointer())) {
underlying = anotherAlias->getDecl();
} else if (auto aliasedClass = underlyingTy->getAs<ClassType>()) {
underlying = aliasedClass->getDecl();
}
if (!underlying)
continue;
if (underlying->getName() == name) {
consumer.foundDecl(underlying,
DeclVisibilityKind::VisibleAtTopLevel);
}
}
}
}
}
}
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 == 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;
SmallVectorImpl<ExtensionDecl *> &ExtensionResults;
const ClangModuleUnit *ModuleFilter = nullptr;
public:
FilteringDeclaredDeclConsumer(swift::VisibleDeclConsumer &consumer,
SmallVectorImpl<ExtensionDecl *> &ExtensionResults,
const ClangModuleUnit *CMU)
: NextConsumer(consumer),
ExtensionResults(ExtensionResults),
ModuleFilter(CMU) {}
void foundDecl(ValueDecl *VD, DeclVisibilityKind Reason) override {
if (isDeclaredInModule(ModuleFilter, VD))
NextConsumer.foundDecl(VD, Reason);
// Also report the extensions declared in this module (whether the extended
// type is from this module or not).
if (auto NTD = dyn_cast<NominalTypeDecl>(VD)) {
for (auto Ext : NTD->getExtensions()) {
if (isDeclaredInModule(ModuleFilter, Ext))
ExtensionResults.push_back(Ext);
}
}
}
};
/// 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)
.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::lookupVisibleDecls(VisibleDeclConsumer &Consumer) const {
if (Impl.CurrentCacheState != Implementation::CacheState::Valid) {
do {
Impl.CurrentCacheState = Implementation::CacheState::InProgress;
Impl.CachedVisibleDecls.clear();
ClangVectorDeclConsumer clangConsumer;
auto &sema = Impl.getClangSema();
sema.LookupVisibleDecls(sema.getASTContext().getTranslationUnitDecl(),
clang::Sema::LookupNameKind::LookupAnyName,
clangConsumer);
// Sort all the Clang decls we find, so that we process them
// deterministically. This *shouldn't* be necessary, but the importer
// definitely still has ordering dependencies.
auto results = clangConsumer.getResults();
llvm::array_pod_sort(results.begin(), results.end(),
[](clang::NamedDecl * const *lhs,
clang::NamedDecl * const *rhs) -> int {
return clang::DeclarationName::compare((*lhs)->getDeclName(),
(*rhs)->getDeclName());
});
for (const clang::NamedDecl *clangDecl : results) {
if (Impl.CurrentCacheState != Implementation::CacheState::InProgress)
break;
if (Decl *imported = Impl.importDeclReal(clangDecl))
Impl.CachedVisibleDecls.push_back(cast<ValueDecl>(imported));
}
// If we changed things /while/ we were caching, we need to start over
// and try again. Fortunately we record a map of decls we've already
// imported, so most of the work is just the lookup and then going
// through the list.
} while (Impl.CurrentCacheState != Implementation::CacheState::InProgress);
auto &ClangPP = Impl.getClangPreprocessor();
for (auto I = ClangPP.macro_begin(), E = ClangPP.macro_end(); I != E; ++I) {
if (!I->first->hasMacroDefinition())
continue;
auto Name = Impl.importName(I->first);
if (Name.empty())
continue;
if (auto *Imported =
Impl.importMacro(Name, I->second->getMacroInfo())) {
Impl.CachedVisibleDecls.push_back(Imported);
}
}
Impl.CurrentCacheState = Implementation::CacheState::Valid;
}
for (auto VD : Impl.CachedVisibleDecls)
Consumer.foundDecl(VD, DeclVisibilityKind::VisibleAtTopLevel);
}
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;
}
owner.lookupVisibleDecls(*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);
SmallVector<ExtensionDecl *, 16> extensions;
FilteringDeclaredDeclConsumer filterConsumer(consumer, extensions, this);
DarwinBlacklistDeclConsumer blacklistConsumer(filterConsumer,
getClangASTContext());
const clang::Module *topLevelModule = clangModule->getTopLevelModule();
if (DarwinBlacklistDeclConsumer::needsBlacklist(topLevelModule)) {
owner.lookupVisibleDecls(blacklistConsumer);
} else {
owner.lookupVisibleDecls(filterConsumer);
}
results.append(extensions.begin(), extensions.end());
}
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) {
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, ClangUnit, SourceLoc(),
ImportKind::Module, SourceLoc(), AccessPath,
ImportedMod);
if (IsExported)
ID->getAttrs().add(new (Ctx) ExportedAttr(/*IsImplicit=*/false));
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;
// There should be no multi-part top-level decls in a Clang module.
if (!name.isSimpleName())
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;
}
owner.lookupValue(name.getBaseName(), *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.importName(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;
// 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.
auto owningDC = Impl.importDeclContextOf(objcMethod);
if (!owningDC)
continue;
if (owningDC->isClassOrClassExtensionContext() != classDecl)
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);
}
}
// FIXME: This should just be the implementation of
// llvm::array_pod_sort_comparator. The only difference is that it uses
// std::less instead of operator<.
// FIXME: Copied from IRGenModule.cpp.
template <typename T>
static int pointerPODSortComparator(T * const *lhs, T * const *rhs) {
std::less<T *> lt;
if (lt(*lhs, *rhs))
return -1;
if (lt(*rhs, *lhs))
return -1;
return 0;
}
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});
}
}
static void lookupClassMembersImpl(ClangImporter::Implementation &Impl,
VisibleDeclConsumer &consumer,
DeclName name) {
// When looking for a subscript, we actually look for the getters
// and setters.
bool isSubscript = name.isSimpleName(Impl.SwiftContext.Id_subscript);
// FIXME: Does not include methods from protocols.
auto importMethodsImpl = [&](const clang::ObjCMethodList &start) {
for (auto *list = &start; list != nullptr; list = list->getNext()) {
if (list->getMethod()->isUnavailable())
continue;
// If the method is a property accessor, we want the property.
const clang::NamedDecl *searchForDecl = list->getMethod();
if (list->getMethod()->isPropertyAccessor() &&
!Impl.isAccessibilityDecl(list->getMethod())) {
if (auto property = list->getMethod()->findPropertyDecl()) {
// ... unless we are enumerating all decls. In this case, if we see
// a getter, return a property. If we see a setter, we know that
// there is a getter, and we will visit it and return a property at
// that time.
if (!name && list->getMethod()->param_size() != 0)
continue;
searchForDecl = property;
}
}
auto VD = cast_or_null<ValueDecl>(Impl.importDeclReal(searchForDecl));
if (!VD)
continue;
if (auto func = dyn_cast<FuncDecl>(VD)) {
if (auto storage = func->getAccessorStorageDecl()) {
consumer.foundDecl(storage, DeclVisibilityKind::DynamicLookup);
continue;
} else if (isSubscript || !name) {
auto known = Impl.Subscripts.find({func, nullptr});
if (known != Impl.Subscripts.end()) {
consumer.foundDecl(known->second,
DeclVisibilityKind::DynamicLookup);
}
// If we were looking only for subscripts, don't report the getter.
if (isSubscript)
continue;
}
}
consumer.foundDecl(VD, DeclVisibilityKind::DynamicLookup);
}
};
auto importMethods = [=](const clang::Sema::GlobalMethods &methodListPair) {
if (methodListPair.first.getMethod())
importMethodsImpl(methodListPair.first);
if (methodListPair.second.getMethod())
importMethodsImpl(methodListPair.second);
};
clang::Sema &S = Impl.getClangSema();
if (isSubscript) {
clang::Selector sels[] = {
Impl.objectAtIndexedSubscript,
Impl.setObjectAtIndexedSubscript,
Impl.objectForKeyedSubscript,
Impl.setObjectForKeyedSubscript
};
for (auto sel : sels) {
S.ReadMethodPool(sel);
importMethods(S.MethodPool[sel]);
}
} else if (name) {
auto sel = Impl.importSelector(name);
if (!sel.isNull()) {
S.ReadMethodPool(sel);
importMethods(S.MethodPool[sel]);
// If this is a simple name, we only checked nullary selectors. Check
// unary ones as well.
// Note: If this is ever used to look up init methods, we'd need to do
// the reverse as well.
if (name.isSimpleName()) {
auto *II = Impl.importName(name.getBaseName()).getAsIdentifierInfo();
sel = Impl.getClangASTContext().Selectors.getUnarySelector(II);
assert(!sel.isNull());
S.ReadMethodPool(sel);
importMethods(S.MethodPool[sel]);
}
}
} else {
// Force load all external methods.
// FIXME: Copied from Clang's SemaCodeComplete.
clang::ExternalASTSource *source = S.getExternalSource();
for (uint32_t i = 0, n = source->GetNumExternalSelectors(); i != n; ++i) {
clang::Selector sel = source->GetExternalSelector(i);
if (sel.isNull() || S.MethodPool.count(sel))
continue;
S.ReadMethodPool(sel);
}
for (auto entry : S.MethodPool)
importMethods(entry.second);
}
}
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;
// FIXME: Not limited by module.
VectorDeclConsumer consumer(results);
lookupClassMembersImpl(owner.Impl, consumer, name);
}
void ClangModuleUnit::lookupClassMembers(Module::AccessPathTy accessPath,
VisibleDeclConsumer &consumer) const {
// FIXME: Ignore submodules, which are empty for now.
if (clangModule && clangModule->isSubModule())
return;
// FIXME: Not limited by module.
lookupClassMembersImpl(owner.Impl, 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->Ctx;
auto adapter = Ctx.getModule(Module::AccessPathTy({M->Name, SourceLoc()}));
if (adapter == M) {
adapter = nullptr;
} else {
auto &sharedModuleRef = Ctx.LoadedModules[M->Name];
assert(!sharedModuleRef || sharedModuleRef == adapter ||
sharedModuleRef == M);
sharedModuleRef = adapter;
}
auto mutableThis = const_cast<ClangModuleUnit *>(this);
mutableThis->adapterModule.setPointerAndInt(adapter, true);
}
return adapterModule.getPointer();
}