blob: ce7faf352cb64e4c0c350cb8071a1f2f526e59c9 [file] [log] [blame]
//===--- ImportName.cpp - Imported Swift names for Clang decls ------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file provides class definitions for naming-related concerns in the
// ClangImporter.
//
//===----------------------------------------------------------------------===//
#include "CFTypeInfo.h"
#include "IAMInference.h"
#include "ImporterImpl.h"
#include "ClangDiagnosticConsumer.h"
#include "swift/Subsystems.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/DiagnosticEngine.h"
#include "swift/AST/DiagnosticsClangImporter.h"
#include "swift/AST/Module.h"
#include "swift/AST/NameLookup.h"
#include "swift/AST/Types.h"
#include "swift/AST/TypeRepr.h"
#include "swift/Basic/StringExtras.h"
#include "swift/ClangImporter/ClangImporterOptions.h"
#include "swift/Parse/Parser.h"
#include "clang/AST/ASTContext.h"
#include "clang/Basic/IdentifierTable.h"
#include "clang/Basic/Module.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Parse/Parser.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Sema.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/ErrorHandling.h"
#include <algorithm>
#include <memory>
#include "llvm/ADT/Statistic.h"
#define DEBUG_TYPE "Import Name"
STATISTIC(ImportNameNumCacheHits, "# of times the import name cache was hit");
STATISTIC(ImportNameNumCacheMisses, "# of times the import name cache was missed");
using namespace swift;
using namespace importer;
// Commonly-used Clang classes.
using clang::CompilerInstance;
using clang::CompilerInvocation;
/// 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 C pointers, including CF types.
if (type->isBlockPointerType() || type->isPointerType()) return true;
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";
/// Determine the optionality of the given Objective-C method.
///
/// \param method The Clang method.
static OptionalTypeKind getResultOptionality(
const clang::ObjCMethodDecl *method) {
auto &clangCtx = method->getASTContext();
// If nullability is available on the type, use it.
if (auto nullability = method->getReturnType()->getNullability(clangCtx)) {
return translateNullability(*nullability);
}
// If there is a returns_nonnull attribute, non-null.
if (method->hasAttr<clang::ReturnsNonNullAttr>())
return OTK_None;
// Default to implicitly unwrapped optionals.
return OTK_ImplicitlyUnwrappedOptional;
}
/// 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);
}
/// Determine whether we should lowercase the first word of the given value
/// name.
static bool shouldLowercaseValueName(StringRef name) {
// If we see any lowercase characters, we can lowercase.
for (auto c : name) {
if (clang::isLowercase(c)) return true;
}
// Otherwise, lowercasing will either be a no-op or we have ALL_CAPS.
return false;
}
/// Will recursively print out the fully qualified context for the given name.
/// Ends with a trailing "."
static void printFullContextPrefix(ImportedName name, ImportNameVersion version,
llvm::raw_ostream &os,
ClangImporter::Implementation &Impl) {
const clang::NamedDecl *newDeclContextNamed = nullptr;
switch (name.getEffectiveContext().getKind()) {
case EffectiveClangContext::UnresolvedContext:
os << name.getEffectiveContext().getUnresolvedName() << ".";
// And we're done!
return;
case EffectiveClangContext::DeclContext: {
auto namedDecl = dyn_cast<clang::NamedDecl>(
name.getEffectiveContext().getAsDeclContext());
if (!namedDecl) {
// We're done
return;
}
newDeclContextNamed = cast<clang::NamedDecl>(namedDecl);
break;
}
case EffectiveClangContext::TypedefContext:
newDeclContextNamed = name.getEffectiveContext().getTypedefName();
break;
}
// Now, let's print out the parent
assert(newDeclContextNamed && "should of been set");
auto parentName = Impl.importFullName(newDeclContextNamed, version);
printFullContextPrefix(parentName, version, os, Impl);
os << parentName.getDeclName() << ".";
}
void ClangImporter::Implementation::printSwiftName(ImportedName name,
ImportNameVersion version,
bool fullyQualified,
llvm::raw_ostream &os) {
// Property accessors.
bool isGetter = false;
bool isSetter = false;
switch (name.getAccessorKind()) {
case ImportedAccessorKind::None:
break;
case ImportedAccessorKind::PropertyGetter:
case ImportedAccessorKind::SubscriptGetter:
os << "getter:";
isGetter = true;
break;
case ImportedAccessorKind::PropertySetter:
case ImportedAccessorKind::SubscriptSetter:
os << "setter:";
isSetter = true;
break;
}
if (fullyQualified)
printFullContextPrefix(name, version, os, *this);
// Base name.
os << name.getDeclName().getBaseName();
// Determine the number of argument labels we'll be producing.
auto argumentNames = name.getDeclName().getArgumentNames();
unsigned numArguments = argumentNames.size();
if (name.getSelfIndex()) ++numArguments;
if (isSetter) ++numArguments;
// If the result is a simple name that is not a getter, we're done.
if (numArguments == 0 && name.getDeclName().isSimpleName() && !isGetter)
return;
// We need to produce a function name.
os << "(";
unsigned currentArgName = 0;
for (unsigned i = 0; i != numArguments; ++i) {
// The "self" parameter.
if (name.getSelfIndex() && *name.getSelfIndex() == i) {
os << "self:";
continue;
}
if (currentArgName < argumentNames.size()) {
if (argumentNames[currentArgName].empty())
os << "_";
else
os << argumentNames[currentArgName].str();
os << ":";
++currentArgName;
continue;
}
// We don't have a name for this argument.
os << "_:";
}
os << ")";
}
/// Retrieve the name of the given Clang declaration context for
/// printing.
static StringRef getClangDeclContextName(const clang::DeclContext *dc) {
auto type = getClangDeclContextType(dc);
if (type.isNull()) return StringRef();
return getClangTypeNameForOmission(dc->getParentASTContext(), type).Name;
}
namespace {
/// Merge the a set of imported names produced for the overridden
/// declarations of a given method or property.
template<typename DeclType>
void mergeOverriddenNames(ASTContext &ctx,
const DeclType *decl,
SmallVectorImpl<std::pair<const DeclType *,
ImportedName>>
&overriddenNames) {
typedef std::pair<const DeclType *, ImportedName> OverriddenName;
llvm::SmallPtrSet<DeclName, 4> known;
(void)known.insert(DeclName());
overriddenNames.erase(
std::remove_if(overriddenNames.begin(), overriddenNames.end(),
[&](OverriddenName overridden) {
return !known.insert(overridden.second.getDeclName())
.second;
}),
overriddenNames.end());
if (overriddenNames.size() < 2)
return;
// Complain about inconsistencies.
std::string nameStr;
auto method = dyn_cast<clang::ObjCMethodDecl>(decl);
if (method)
nameStr = method->getSelector().getAsString();
else
nameStr = cast<clang::ObjCPropertyDecl>(decl)->getName().str();
for (unsigned i = 1, n = overriddenNames.size(); i != n; ++i) {
ctx.Diags.diagnose(SourceLoc(), diag::inconsistent_swift_name,
method == nullptr,
nameStr,
getClangDeclContextName(decl->getDeclContext()),
overriddenNames[0].second,
getClangDeclContextName(
overriddenNames[0].first->getDeclContext()),
overriddenNames[i].second,
getClangDeclContextName(
overriddenNames[i].first->getDeclContext()));
}
}
} // end anonymous namespace
/// Skip a leading 'k' in a 'kConstant' pattern
static StringRef stripLeadingK(StringRef name) {
if (name.size() >= 2 && name[0] == 'k' &&
clang::isUppercase(name[1]))
return name.drop_front(1);
return name;
}
/// Strips a trailing "Notification", if present. Returns {} if name doesn't end
/// in "Notification", or it there would be nothing left.
StringRef importer::stripNotification(StringRef name) {
name = stripLeadingK(name);
StringRef notification = "Notification";
if (name.size() <= notification.size() || !name.endswith(notification))
return {};
return name.drop_back(notification.size());
}
/// Whether the decl is from a module who requested import-as-member inference
static bool moduleIsInferImportAsMember(const clang::NamedDecl *decl,
clang::Sema &clangSema) {
clang::Module *submodule;
if (auto m = decl->getImportedOwningModule()) {
submodule = m;
} else if (auto m = decl->getLocalOwningModule()) {
submodule = m;
} else if (auto m = clangSema.getPreprocessor().getCurrentModule()) {
submodule = m;
} else if (auto m = clangSema.getPreprocessor().getCurrentLexerSubmodule()) {
submodule = m;
} else {
return false;
}
while (submodule) {
if (submodule->IsSwiftInferImportAsMember) {
// HACK HACK HACK: This is a workaround for some module invalidation issue
// and inconsistency. This will go away soon.
return submodule->Name == "CoreGraphics";
}
submodule = submodule->Parent;
}
return false;
}
/// Match the name of the given Objective-C method to its enclosing class name
/// to determine the name prefix that would be stripped if the class method
/// were treated as an initializer.
static Optional<unsigned>
matchFactoryAsInitName(const clang::ObjCMethodDecl *method) {
// Only class methods can be mapped to initializers in this way.
if (!method->isClassMethod()) return None;
// Said class methods must be in an actual class.
auto objcClass = method->getClassInterface();
if (!objcClass) return None;
// See if we can match the class name to the beginning of the first
// selector piece.
auto firstPiece = method->getSelector().getNameForSlot(0);
if (firstPiece.empty())
return None;
StringRef firstArgLabel = matchLeadingTypeName(firstPiece,
objcClass->getName());
if (firstArgLabel.size() == firstPiece.size())
return None;
// FIXME: Factory methods cannot have dummy parameters added for
// historical reasons.
if (!firstArgLabel.empty() && method->getSelector().getNumArgs() == 0)
return None;
// Return the prefix length.
return firstPiece.size() - firstArgLabel.size();
}
/// Determine the kind of initializer the given factory method could be mapped
/// to, or produce \c None.
static Optional<CtorInitializerKind>
determineCtorInitializerKind(const clang::ObjCMethodDecl *method) {
// 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.
return CtorInitializerKind::ConvenienceFactory;
}
if (auto objcPtr = method->getReturnType()
->getAs<clang::ObjCObjectPointerType>()) {
auto objcClass = method->getClassInterface();
if (!objcClass) return None;
if (objcPtr->getInterfaceDecl() != objcClass) {
// FIXME: Could allow a subclass here, but the rest of the compiler
// isn't prepared for that yet.
return None;
}
// Factory initializer.
return CtorInitializerKind::Factory;
}
// Not imported as an initializer.
return None;
}
namespace {
/// Aggregate struct for the common members of clang::SwiftVersionedAttr and
/// clang::SwiftVersionedRemovalAttr.
///
/// For a SwiftVersionedRemovalAttr, the Attr member will be null.
struct VersionedSwiftNameInfo {
const clang::SwiftNameAttr *Attr;
llvm::VersionTuple Version;
bool IsReplacedByActive;
};
/// The action to take upon seeing a particular versioned swift_name annotation.
enum class VersionedSwiftNameAction {
/// This annotation is not interesting.
Ignore,
/// This annotation is better than whatever we have so far.
Use,
/// This annotation is better than nothing, but that's all; don't bother
/// recording its version.
UseAsFallback,
/// This annotation itself isn't interesting, but its version shows that the
/// correct answer is whatever's currently active.
ResetToActive
};
} // end anonymous namespace
static VersionedSwiftNameAction
checkVersionedSwiftName(VersionedSwiftNameInfo info,
llvm::VersionTuple bestSoFar,
ImportNameVersion requestedVersion) {
if (!bestSoFar.empty() && bestSoFar <= info.Version)
return VersionedSwiftNameAction::Ignore;
auto requestedClangVersion = requestedVersion.asClangVersionTuple();
if (info.IsReplacedByActive) {
// We know that there are no versioned names between the active version and
// a replacement version, because otherwise /that/ name would be active.
// So if replacement < requested, we want to use the old value that was
// replaced (but with very low priority), and otherwise we want to use the
// new value that is now active. (Special case: replacement = 0 means that
// a header annotation was replaced by an unversioned API notes annotation.)
if (info.Version.empty() ||
info.Version >= requestedClangVersion) {
return VersionedSwiftNameAction::ResetToActive;
}
if (bestSoFar.empty())
return VersionedSwiftNameAction::UseAsFallback;
return VersionedSwiftNameAction::Ignore;
}
if (info.Version < requestedClangVersion)
return VersionedSwiftNameAction::Ignore;
return VersionedSwiftNameAction::Use;
}
static const clang::SwiftNameAttr *
findSwiftNameAttr(const clang::Decl *decl, ImportNameVersion version) {
#ifndef NDEBUG
if (Optional<const clang::Decl *> def = getDefinitionForClangTypeDecl(decl)) {
assert((*def == nullptr || *def == decl) &&
"swift_name should only appear on the definition");
}
#endif
if (version == ImportNameVersion::raw())
return nullptr;
// Handle versioned API notes for Swift 3 and later. This is the common case.
if (version > ImportNameVersion::swift2()) {
// FIXME: Until Apple gets a chance to update UIKit's API notes, always use
// the new name for certain properties.
if (auto *namedDecl = dyn_cast<clang::NamedDecl>(decl))
if (importer::isSpecialUIKitStructZeroProperty(namedDecl))
version = ImportNameVersion::swift4_2();
const auto *activeAttr = decl->getAttr<clang::SwiftNameAttr>();
const clang::SwiftNameAttr *result = activeAttr;
llvm::VersionTuple bestSoFar;
for (auto *attr : decl->attrs()) {
VersionedSwiftNameInfo info;
if (auto *versionedAttr = dyn_cast<clang::SwiftVersionedAttr>(attr)) {
auto *added =
dyn_cast<clang::SwiftNameAttr>(versionedAttr->getAttrToAdd());
if (!added)
continue;
info = {added, versionedAttr->getVersion(),
versionedAttr->getIsReplacedByActive()};
} else if (auto *removeAttr =
dyn_cast<clang::SwiftVersionedRemovalAttr>(attr)) {
if (removeAttr->getAttrKindToRemove() != clang::attr::SwiftName)
continue;
info = {nullptr, removeAttr->getVersion(),
removeAttr->getIsReplacedByActive()};
} else {
continue;
}
switch (checkVersionedSwiftName(info, bestSoFar, version)) {
case VersionedSwiftNameAction::Ignore:
continue;
case VersionedSwiftNameAction::Use:
result = info.Attr;
bestSoFar = info.Version;
break;
case VersionedSwiftNameAction::UseAsFallback:
// HACK: If there's a swift_name attribute in the headers /and/ in the
// unversioned API notes /and/ in the active versioned API notes, there
// will be two "replacement" attributes, one for each of the first two
// cases. Prefer the first one we see, because that turns out to be the
// one from the API notes, which matches the semantics when there are no
// versioned API notes. (This isn't very principled but there's at least
// a test to tell us if it changes.)
if (result == activeAttr)
result = info.Attr;
assert(bestSoFar.empty());
break;
case VersionedSwiftNameAction::ResetToActive:
result = activeAttr;
bestSoFar = info.Version;
break;
}
}
return result;
}
// The remainder of this function emulates the limited form of swift_name
// supported in Swift 2.
auto attr = decl->getAttr<clang::SwiftNameAttr>();
if (!attr) return nullptr;
// API notes produce attributes with no source location; ignore them because
// they weren't used for naming in Swift 2.
if (attr->getLocation().isInvalid()) return nullptr;
// Hardcode certain kinds of explicitly-written Swift names that were
// permitted and used in Swift 2. All others are ignored, so that we are
// assuming a more direct translation from the Objective-C APIs into Swift.
if (auto enumerator = dyn_cast<clang::EnumConstantDecl>(decl)) {
// Foundation's NSXMLDTDKind had an explicit swift_name attribute in
// Swift 2. Honor it.
if (enumerator->getName() == "NSXMLDTDKind") return attr;
return nullptr;
}
if (auto method = dyn_cast<clang::ObjCMethodDecl>(decl)) {
// Special case: mapping to an initializer.
if (attr->getName().startswith("init(")) {
// If we have a class method, honor the annotation to turn a class
// method into an initializer.
if (method->isClassMethod()) return attr;
return nullptr;
}
// Special case: preventing a mapping to an initializer.
if (matchFactoryAsInitName(method) && determineCtorInitializerKind(method))
return attr;
return nullptr;
}
return nullptr;
}
/// Determine whether the given class method should be imported as
/// an initializer.
static FactoryAsInitKind
getFactoryAsInit(const clang::ObjCInterfaceDecl *classDecl,
const clang::ObjCMethodDecl *method,
ImportNameVersion version) {
if (auto *customNameAttr = findSwiftNameAttr(method, version)) {
if (customNameAttr->getName().startswith("init("))
return FactoryAsInitKind::AsInitializer;
else
return FactoryAsInitKind::AsClassMethod;
}
return FactoryAsInitKind::Infer;
}
/// Determine whether this Objective-C method should be imported as
/// an initializer.
///
/// \param prefixLength Will be set to the length of the prefix that
/// should be stripped from the first selector piece, e.g., "init"
/// or the restated name of the class in a factory method.
///
/// \param kind Will be set to the kind of initializer being
/// imported. Note that this does not distinguish designated
/// vs. convenience; both will be classified as "designated".
static bool shouldImportAsInitializer(const clang::ObjCMethodDecl *method,
ImportNameVersion version,
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, version)) {
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.
if (auto matchedLength = matchFactoryAsInitName(method)) {
prefixLength = *matchedLength;
break;
}
return false;
case FactoryAsInitKind::AsClassMethod:
return false;
}
// Determine what kind of initializer we're creating.
if (auto initKind = determineCtorInitializerKind(method)) {
kind = *initKind;
return true;
}
// Not imported as an initializer.
return false;
}
/// Attempt to omit needless words from the given function name.
static bool omitNeedlessWordsInFunctionName(
StringRef &baseName, SmallVectorImpl<StringRef> &argumentNames,
ArrayRef<const clang::ParmVarDecl *> params, clang::QualType resultType,
const clang::DeclContext *dc, const SmallBitVector &nonNullArgs,
Optional<unsigned> errorParamIndex, bool returnsSelf, bool isInstanceMethod,
NameImporter &nameImporter) {
clang::ASTContext &clangCtx = nameImporter.getClangContext();
const version::Version &swiftLanguageVersion =
nameImporter.getLangOpts().EffectiveLanguageVersion;
// Collect the parameter type names.
StringRef firstParamName;
SmallVector<OmissionTypeName, 4> paramTypes;
for (unsigned i = 0, n = params.size(); i != n; ++i) {
auto param = params[i];
// Capture the first parameter name.
if (i == 0)
firstParamName = param->getName();
// Determine the number of parameters.
unsigned numParams = params.size();
if (errorParamIndex) --numParams;
bool isLastParameter
= (i == params.size() - 1) ||
(i == params.size() - 2 &&
errorParamIndex && *errorParamIndex == params.size() - 1);
// Figure out whether there will be a default argument for this
// parameter.
StringRef argumentName;
if (i < argumentNames.size())
argumentName = argumentNames[i];
bool hasDefaultArg =
ClangImporter::Implementation::inferDefaultArgument(
param->getType(),
getParamOptionality(swiftLanguageVersion, param,
!nonNullArgs.empty() && nonNullArgs[i]),
nameImporter.getIdentifier(baseName), numParams, argumentName,
i == 0, isLastParameter, nameImporter) != DefaultArgumentKind::None;
paramTypes.push_back(getClangTypeNameForOmission(clangCtx,
param->getOriginalType())
.withDefaultArgument(hasDefaultArg));
}
// Find the property names.
const InheritedNameSet *allPropertyNames = nullptr;
auto contextType = getClangDeclContextType(dc);
if (!contextType.isNull()) {
if (auto objcPtrType = contextType->getAsObjCInterfacePointerType())
if (auto objcClassDecl = objcPtrType->getInterfaceDecl())
allPropertyNames = nameImporter.getAllPropertyNames(
objcClassDecl, isInstanceMethod);
}
// Omit needless words.
return omitNeedlessWords(baseName, argumentNames, firstParamName,
getClangTypeNameForOmission(clangCtx, resultType),
getClangTypeNameForOmission(clangCtx, contextType),
paramTypes, returnsSelf, /*isProperty=*/false,
allPropertyNames, nameImporter.getScratch());
}
/// Prepare global name for importing onto a swift_newtype.
static StringRef determineSwiftNewtypeBaseName(StringRef baseName,
StringRef newtypeName,
bool &strippedPrefix) {
StringRef newBaseName = stripLeadingK(baseName);
if (newBaseName != baseName) {
baseName = newBaseName;
strippedPrefix = true;
}
// Special case: Strip Notification for NSNotificationName
auto stripped = stripNotification(baseName);
if (!stripped.empty())
return stripped;
bool nonIdentifier = false;
auto pre = getCommonWordPrefix(newtypeName, baseName, nonIdentifier);
if (pre.size()) {
baseName = baseName.drop_front(pre.size());
strippedPrefix = true;
}
return baseName;
}
EffectiveClangContext
NameImporter::determineEffectiveContext(const clang::NamedDecl *decl,
const clang::DeclContext *dc,
ImportNameVersion version) {
EffectiveClangContext res;
// Enumerators can end up within their enclosing enum or in the global
// scope, depending how their enclosing enumeration is imported.
if (isa<clang::EnumConstantDecl>(decl)) {
auto enumDecl = cast<clang::EnumDecl>(dc);
switch (getEnumKind(enumDecl)) {
case EnumKind::NonFrozenEnum:
case EnumKind::FrozenEnum:
case EnumKind::Options:
// Enums are mapped to Swift enums, Options to Swift option sets.
if (version != ImportNameVersion::raw()) {
res = cast<clang::DeclContext>(enumDecl);
break;
}
LLVM_FALLTHROUGH;
case EnumKind::Constants:
case EnumKind::Unknown:
// The enum constant goes into the redeclaration context of the
// enum.
res = enumDecl->getRedeclContext();
break;
}
// Import onto a swift_newtype if present
} else if (auto newtypeDecl = findSwiftNewtype(decl, clangSema, version)) {
res = newtypeDecl;
// Everything else goes into its redeclaration context.
} else {
res = dc->getRedeclContext();
}
// Anything in an Objective-C category or extension is adjusted to the
// class context.
if (auto category =
dyn_cast_or_null<clang::ObjCCategoryDecl>(res.getAsDeclContext())) {
// If the enclosing category is invalid, we cannot import the declaration.
if (category->isInvalidDecl())
return {};
return category->getClassInterface();
}
return res;
}
bool NameImporter::hasNamingConflict(const clang::NamedDecl *decl,
const clang::IdentifierInfo *proposedName,
const clang::TypedefNameDecl *cfTypedef) {
// Test to see if there is a value with the same name as 'proposedName'
// in the same module as the decl
// FIXME: This will miss macros.
auto clangModule = getClangSubmoduleForDecl(decl);
if (clangModule.hasValue() && clangModule.getValue())
clangModule = clangModule.getValue()->getTopLevelModule();
auto conflicts = [&](const clang::Decl *OtherD) -> bool {
// If these are simply redeclarations, they do not conflict.
if (decl->getCanonicalDecl() == OtherD->getCanonicalDecl())
return false;
// If we have a CF typedef, check whether the "other"
// declaration we found is just the opaque type behind it. If
// so, it does not conflict.
if (cfTypedef) {
if (auto cfPointerTy =
cfTypedef->getUnderlyingType()->getAs<clang::PointerType>()) {
if (auto tagDecl = cfPointerTy->getPointeeType()->getAsTagDecl()) {
if (tagDecl->getCanonicalDecl() == OtherD)
return false;
}
}
}
auto declModule = getClangSubmoduleForDecl(OtherD);
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 decl 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 decl!
//
// 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.
clang::LookupResult lookupResult(clangSema, proposedName,
clang::SourceLocation(),
clang::Sema::LookupOrdinaryName);
lookupResult.setAllowHidden(true);
lookupResult.suppressDiagnostics();
if (clangSema.LookupName(lookupResult, /*scope=*/nullptr)) {
if (std::any_of(lookupResult.begin(), lookupResult.end(), conflicts))
return true;
}
lookupResult.clear(clang::Sema::LookupTagName);
if (clangSema.LookupName(lookupResult, /*scope=*/nullptr)) {
if (std::any_of(lookupResult.begin(), lookupResult.end(), conflicts))
return true;
}
return false;
}
static bool shouldBeSwiftPrivate(NameImporter &nameImporter,
const clang::NamedDecl *decl,
ImportNameVersion version) {
// Decl with the attribute are obviously private
if (decl->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>(decl)) {
auto *ED = cast<clang::EnumDecl>(ECD->getDeclContext());
switch (nameImporter.getEnumKind(ED)) {
case EnumKind::NonFrozenEnum:
case EnumKind::FrozenEnum:
case EnumKind::Options:
if (version != ImportNameVersion::raw())
break;
LLVM_FALLTHROUGH;
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;
}
}
return false;
}
Optional<ForeignErrorConvention::Info> NameImporter::considerErrorImport(
const clang::ObjCMethodDecl *clangDecl, StringRef &baseName,
SmallVectorImpl<StringRef> &paramNames,
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;
auto errorKind =
classifyMethodErrorHandling(clangDecl,
getResultOptionality(clangDecl));
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() || 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(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;
ForeignErrorConvention::Info errorInfo(
*errorKind, index, isErrorOwned,
(ForeignErrorConvention::IsReplaced_t)replaceParamWithVoid);
return errorInfo;
}
// Didn't find an error parameter.
return None;
}
bool NameImporter::hasErrorMethodNameCollision(
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.
return !isUnavailableInSwift(conflict, availability,
enableObjCInterop());
}
/// Whether we should suppress this factory method being imported as an
/// initializer. We want to do this when explicitly directed to, or when
/// importing a property accessor.
static bool suppressFactoryMethodAsInit(const clang::ObjCMethodDecl *method,
ImportNameVersion version,
CtorInitializerKind initKind) {
return (version == ImportNameVersion::raw() || method->isPropertyAccessor()) &&
(initKind == CtorInitializerKind::Factory ||
initKind == CtorInitializerKind::ConvenienceFactory);
}
ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
ImportNameVersion version,
clang::DeclarationName givenName) {
ImportedName result;
/// Whether we want a Swift 3 or later name
bool swift3OrLaterName = version > ImportNameVersion::swift2();
// Objective-C categories and extensions don't have names, despite
// being "named" declarations.
if (isa<clang::ObjCCategoryDecl>(D))
return ImportedName();
// Dig out the definition, if there is one.
if (auto def = getDefinitionForClangTypeDecl(D)) {
if (*def)
D = static_cast<const clang::NamedDecl *>(*def);
}
// Compute the effective context.
auto dc = const_cast<clang::DeclContext *>(D->getDeclContext());
auto effectiveCtx = determineEffectiveContext(D, dc, version);
if (!effectiveCtx)
return ImportedName();
result.effectiveContext = effectiveCtx;
// FIXME: ugly to check here, instead perform unified check up front in
// containing struct...
if (findSwiftNewtype(D, clangSema, version))
result.info.importAsMember = true;
// Find the original method/property declaration and retrieve the
// name from there.
if (auto method = dyn_cast<clang::ObjCMethodDecl>(D)) {
// Inherit the name from the "originating" declarations, if
// there are any.
SmallVector<std::pair<const clang::ObjCMethodDecl *, ImportedName>, 4>
overriddenNames;
SmallVector<const clang::ObjCMethodDecl *, 4> overriddenMethods;
method->getOverriddenMethods(overriddenMethods);
for (auto overridden : overriddenMethods) {
const auto overriddenName = importName(overridden, version, givenName);
if (overriddenName.getDeclName())
overriddenNames.push_back({overridden, overriddenName});
}
// If we found any names of overridden methods, return those names.
if (!overriddenNames.empty()) {
if (overriddenNames.size() > 1)
mergeOverriddenNames(swiftCtx, method, overriddenNames);
overriddenNames[0].second.effectiveContext = result.effectiveContext;
return overriddenNames[0].second;
}
} else if (auto property = dyn_cast<clang::ObjCPropertyDecl>(D)) {
// Inherit the name from the "originating" declarations, if
// there are any.
if (auto getter = property->getGetterMethodDecl()) {
SmallVector<std::pair<const clang::ObjCPropertyDecl *, ImportedName>, 4>
overriddenNames;
SmallVector<const clang::ObjCMethodDecl *, 4> overriddenMethods;
SmallPtrSet<const clang::ObjCPropertyDecl *, 4> knownProperties;
(void)knownProperties.insert(property);
getter->getOverriddenMethods(overriddenMethods);
for (auto overridden : overriddenMethods) {
if (!overridden->isPropertyAccessor())
continue;
auto overriddenProperty = overridden->findPropertyDecl(true);
if (!overriddenProperty)
continue;
if (!knownProperties.insert(overriddenProperty).second)
continue;
const auto overriddenName = importName(overriddenProperty, version,
givenName);
if (overriddenName.getDeclName())
overriddenNames.push_back({overriddenProperty, overriddenName});
}
// If we found any names of overridden methods, return those names.
if (!overriddenNames.empty()) {
if (overriddenNames.size() > 1)
mergeOverriddenNames(swiftCtx, property, overriddenNames);
overriddenNames[0].second.effectiveContext = result.effectiveContext;
return overriddenNames[0].second;
}
}
}
// If we have a swift_name attribute, use that.
if (auto *nameAttr = findSwiftNameAttr(D, version)) {
bool skipCustomName = false;
// Parse the name.
ParsedDeclName parsedName = parseDeclName(nameAttr->getName());
if (!parsedName || parsedName.isOperator())
return result;
// 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 (parsedName.BaseName == "init" && parsedName.IsFunctionName) {
if (!shouldImportAsInitializer(method, version, initPrefixLength,
result.info.initKind)) {
// We cannot import this as an initializer anyway.
return ImportedName();
}
// 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 (suppressFactoryMethodAsInit(method, version,
result.getInitKind())) {
skipCustomName = true;
} else {
// Note that this is an initializer.
isInitializer = true;
}
}
}
if (!skipCustomName) {
result.info.hasCustomName = true;
result.declName = parsedName.formDeclName(swiftCtx);
// Handle globals treated as members.
if (parsedName.isMember()) {
// FIXME: Make sure this thing is global.
result.effectiveContext = parsedName.ContextName;
if (parsedName.SelfIndex) {
result.info.hasSelfIndex = true;
result.info.selfIndex = *parsedName.SelfIndex;
}
result.info.importAsMember = true;
if (parsedName.BaseName == "init")
result.info.initKind = CtorInitializerKind::Factory;
}
// Map property getters/setters.
if (parsedName.IsGetter)
result.info.accessorKind = ImportedAccessorKind::PropertyGetter;
else if (parsedName.IsSetter)
result.info.accessorKind = ImportedAccessorKind::PropertySetter;
if (method && parsedName.IsFunctionName) {
// Get the parameters.
ArrayRef<const clang::ParmVarDecl *> params{method->param_begin(),
method->param_end()};
if (auto errorInfo = considerErrorImport(method, parsedName.BaseName,
parsedName.ArgumentLabels,
params, isInitializer,
/*hasCustomName=*/true)) {
result.info.hasErrorInfo = true;
result.info.errorInfo = *errorInfo;
}
}
return result;
}
} else if (swift3OrLaterName && (inferImportAsMember ||
moduleIsInferImportAsMember(D, clangSema)) &&
(isa<clang::VarDecl>(D) || isa<clang::FunctionDecl>(D)) &&
dc->isTranslationUnit()) {
auto inference = IAMResult::infer(swiftCtx, clangSema, D);
if (inference.isImportAsMember()) {
result.info.importAsMember = true;
result.declName = inference.name;
result.effectiveContext = inference.effectiveDC;
// Instance or static
if (inference.selfIndex) {
result.info.hasSelfIndex = true;
result.info.selfIndex = *inference.selfIndex;
}
// Property
if (inference.isGetter())
result.info.accessorKind = ImportedAccessorKind::PropertyGetter;
else if (inference.isSetter())
result.info.accessorKind = ImportedAccessorKind::PropertySetter;
// Inits are factory. These C functions are neither convenience nor
// designated, as they return a fully formed object of that type.
if (inference.isInit())
result.info.initKind = CtorInitializerKind::Factory;
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;
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:
case clang::DeclarationName::CXXDeductionGuideName:
// Handling these is part of C++ interoperability.
llvm_unreachable("unhandled C++ interoperability");
case clang::DeclarationName::Identifier:
// Map the identifier.
baseName = D->getDeclName().getAsIdentifierInfo()->getName();
if (givenName) {
if (!givenName.isIdentifier())
return ImportedName();
baseName = givenName.getAsIdentifierInfo()->getName();
}
// For Objective-C BOOL properties, use the name of the getter
// which, conventionally, has an "is" prefix.
if (swift3OrLaterName) {
if (auto property = dyn_cast<clang::ObjCPropertyDecl>(D)) {
if (isBoolType(clangSema.Context, property->getType()))
baseName = property->getGetterName().getNameForSlot(0);
}
}
// For C functions, create empty argument names.
if (auto function = dyn_cast<clang::FunctionDecl>(D)) {
isFunction = true;
params = {function->param_begin(), function->param_end()};
for (auto param : params) {
(void)param;
argumentNames.push_back(StringRef());
}
if (function->isVariadic())
argumentNames.push_back(StringRef());
}
break;
case clang::DeclarationName::ObjCMultiArgSelector:
case clang::DeclarationName::ObjCOneArgSelector:
case clang::DeclarationName::ObjCZeroArgSelector: {
auto objcMethod = cast<clang::ObjCMethodDecl>(D);
// Map the Objective-C selector directly.
auto selector = D->getDeclName().getObjCSelector();
// Respect the given name.
if (givenName) {
switch (givenName.getNameKind()) {
case clang::DeclarationName::ObjCOneArgSelector:
case clang::DeclarationName::ObjCMultiArgSelector:
case clang::DeclarationName::ObjCZeroArgSelector:
// Make sure the given name has the right count of arguments.
if (selector.getNumArgs() != givenName.getObjCSelector().getNumArgs())
return ImportedName();
selector = givenName.getObjCSelector();
break;
default:
return ImportedName();
}
}
baseName = selector.getNameForSlot(0);
// We don't support methods with empty first selector pieces.
if (baseName.empty())
return ImportedName();
isInitializer = shouldImportAsInitializer(objcMethod, version,
initializerPrefixLen,
result.info.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 && suppressFactoryMethodAsInit(objcMethod, version,
result.getInitKind())) {
isInitializer = false;
}
if (isInitializer)
baseName = "init";
// 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.info.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);
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;
}
}
if (auto errorInfo = considerErrorImport(
objcMethod, baseName, argumentNames, params, isInitializer,
/*hasCustomName=*/false)) {
result.info.hasErrorInfo = true;
result.info.errorInfo = *errorInfo;
}
isFunction = true;
// Is this one of the accessors for subscripts?
if (objcMethod->getMethodFamily() == clang::OMF_None &&
objcMethod->isInstanceMethod()) {
if (isNonNullarySelector(objcMethod->getSelector(),
{"objectAtIndexedSubscript"}) ||
isNonNullarySelector(objcMethod->getSelector(),
{"objectForKeyedSubscript"}))
result.info.accessorKind = ImportedAccessorKind::SubscriptGetter;
else if (isNonNullarySelector(objcMethod->getSelector(),
{"setObject", "atIndexedSubscript"}) ||
isNonNullarySelector(objcMethod->getSelector(),
{"setObject", "forKeyedSubscript"}))
result.info.accessorKind = ImportedAccessorKind::SubscriptSetter;
}
break;
}
}
// Perform automatic name transformations.
// Enumeration constants may have common prefixes stripped.
bool strippedPrefix = false;
if (version != ImportNameVersion::raw() && isa<clang::EnumConstantDecl>(D)) {
auto enumDecl = cast<clang::EnumDecl>(D->getDeclContext());
auto enumInfo = getEnumInfo(enumDecl);
StringRef removePrefix = enumInfo.getConstantNamePrefix();
if (!removePrefix.empty()) {
if (baseName.startswith(removePrefix)) {
baseName = baseName.substr(removePrefix.size());
strippedPrefix = true;
} else if (givenName) {
// Calculate the new prefix.
// What if the preferred name causes longer prefix?
StringRef subPrefix = [](StringRef LHS, StringRef RHS) {
if (LHS.size() > RHS.size())
std::swap(LHS, RHS) ;
return StringRef(LHS.data(), std::mismatch(LHS.begin(), LHS.end(),
RHS.begin()).first - LHS.begin());
}(removePrefix, baseName);
if (!subPrefix.empty()) {
baseName = baseName.substr(subPrefix.size());
strippedPrefix = true;
}
}
}
}
// If the error is an error enum, it will be mapped to the 'Code'
// enum nested within an NSError-containing struct. Strip the word
// "Code" off the end of the name, if it's there, because it's
// redundant.
if (auto enumDecl = dyn_cast<clang::EnumDecl>(D)) {
if (enumDecl->isThisDeclarationADefinition()) {
auto enumInfo = getEnumInfo(enumDecl);
if (enumInfo.isErrorEnum() && baseName.size() > 4 &&
camel_case::getLastWord(baseName) == "Code")
baseName = baseName.substr(0, baseName.size() - 4);
}
}
// 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()) {
if (hasNamingConflict(D, objcProto->getIdentifier(), nullptr)) {
baseNameWithProtocolSuffix = baseName;
baseNameWithProtocolSuffix += SWIFT_PROTOCOL_SUFFIX;
baseName = baseNameWithProtocolSuffix;
}
}
}
// Typedef declarations might be CF types that will drop the "Ref"
// suffix.
clang::ASTContext &clangCtx = clangSema.Context;
if (swift3OrLaterName) {
if (auto typedefNameDecl = dyn_cast<clang::TypedefNameDecl>(D)) {
auto swiftName = getCFTypeName(typedefNameDecl);
if (!swiftName.empty() &&
!hasNamingConflict(D, &clangCtx.Idents.get(swiftName),
typedefNameDecl)) {
// Adopt the requested name.
baseName = swiftName;
}
}
}
// swift_newtype-ed declarations may have common words with the type name
// stripped.
if (auto newtypeDecl = findSwiftNewtype(D, clangSema, version)) {
result.info.importAsMember = true;
baseName = determineSwiftNewtypeBaseName(baseName, newtypeDecl->getName(),
strippedPrefix);
}
if (!result.isSubscriptAccessor() && swift3OrLaterName) {
// 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 =
getAllPropertyNames(objcClassDecl, /*forInstance=*/true);
}
(void)omitNeedlessWords(baseName, {}, "", propertyTypeName,
contextTypeName, {}, /*returnsSelf=*/false,
/*isProperty=*/true, allPropertyNames,
scratch);
}
}
// Objective-C methods.
if (auto method = dyn_cast<clang::ObjCMethodDecl>(D)) {
(void)omitNeedlessWordsInFunctionName(
baseName, argumentNames, params, method->getReturnType(),
method->getDeclContext(), getNonNullArgs(method, params),
result.getErrorInfo()
? Optional<unsigned>(result.getErrorInfo()->ErrorParameterIndex)
: None,
method->hasRelatedResultType(), method->isInstanceMethod(), *this);
}
// If the result is a value, lowercase it.
if (strippedPrefix && isa<clang::ValueDecl>(D) &&
shouldLowercaseValueName(baseName)) {
baseName = camel_case::toLowercaseInitialisms(baseName, scratch);
}
}
// If this declaration has the swift_private attribute, prepend "__" to the
// appropriate place.
SmallString<16> swiftPrivateScratch;
if (shouldBeSwiftPrivate(*this, D, version)) {
// Special case: empty arg factory, "for historical reasons", is not private
if (isInitializer && argumentNames.empty() &&
(result.getInitKind() == CtorInitializerKind::Factory ||
result.getInitKind() == CtorInitializerKind::ConvenienceFactory))
return result;
// Make the given name private.
swiftPrivateScratch = "__";
if (isInitializer) {
// For initializers, prepend "__" to the first argument name.
if (argumentNames.empty()) {
// FIXME: Record that we did this.
argumentNames.push_back("__");
} else {
swiftPrivateScratch += argumentNames[0];
argumentNames[0] = swiftPrivateScratch;
}
} else {
// For all other entities, prepend "__" to the base name.
swiftPrivateScratch += baseName;
baseName = swiftPrivateScratch;
}
}
result.declName = formDeclName(swiftCtx, baseName, argumentNames, isFunction,
isInitializer);
return result;
}
/// Returns true if it is expected that the macro is ignored.
static bool shouldIgnoreMacro(StringRef name, const clang::MacroInfo *macro) {
// Ignore include guards.
if (macro->isUsedForHeaderGuard())
return true;
// If there are no tokens, there is nothing to convert.
if (macro->tokens_empty())
return true;
// Currently we only convert non-function-like macros.
if (macro->isFunctionLike())
return true;
// Consult the list of macros to suppress.
auto suppressMacro = llvm::StringSwitch<bool>(name)
#define SUPPRESS_MACRO(NAME) .Case(#NAME, true)
#include "MacroTable.def"
.Default(false);
if (suppressMacro)
return true;
return false;
}
bool ClangImporter::shouldIgnoreMacro(StringRef Name,
const clang::MacroInfo *Macro) {
return ::shouldIgnoreMacro(Name, Macro);
}
Identifier
NameImporter::importMacroName(const clang::IdentifierInfo *clangIdentifier,
const clang::MacroInfo *macro) {
// If we're supposed to ignore this macro, return an empty identifier.
if (::shouldIgnoreMacro(clangIdentifier->getName(), macro))
return Identifier();
// No transformation is applied to the name.
StringRef name = clangIdentifier->getName();
return swiftCtx.getIdentifier(name);
}
ImportedName NameImporter::importName(const clang::NamedDecl *decl,
ImportNameVersion version,
clang::DeclarationName givenName) {
CacheKeyType key(decl, version);
if (importNameCache.count(key) && !givenName) {
++ImportNameNumCacheHits;
return importNameCache[key];
}
++ImportNameNumCacheMisses;
auto res = importNameImpl(decl, version, givenName);
if (!givenName)
importNameCache[key] = res;
return res;
}
bool NameImporter::forEachDistinctImportName(
const clang::NamedDecl *decl, ImportNameVersion activeVersion,
llvm::function_ref<bool(ImportedName, ImportNameVersion)> action) {
using ImportNameKey = std::pair<DeclName, EffectiveClangContext>;
SmallVector<ImportNameKey, 8> seenNames;
ImportedName newName = importName(decl, activeVersion);
if (!newName)
return true;
ImportNameKey key(newName.getDeclName(), newName.getEffectiveContext());
if (action(newName, activeVersion))
seenNames.push_back(key);
activeVersion.forEachOtherImportNameVersion(
[&](ImportNameVersion nameVersion) {
// Check to see if the name is different.
ImportedName newName = importName(decl, nameVersion);
if (!newName)
return;
ImportNameKey key(newName.getDeclName(), newName.getEffectiveContext());
bool seen = llvm::any_of(
seenNames, [&key](const ImportNameKey &existing) -> bool {
return key.first == existing.first &&
key.second.equalsWithoutResolving(existing.second);
});
if (seen)
return;
if (action(newName, nameVersion))
seenNames.push_back(key);
});
return false;
}
const InheritedNameSet *NameImporter::getAllPropertyNames(
clang::ObjCInterfaceDecl *classDecl,
bool forInstance) {
classDecl = classDecl->getCanonicalDecl();
// If we already have this information, return it.
auto known = allProperties.find({classDecl, forInstance});
if (known != allProperties.end()) return known->second.get();
// Otherwise, get information from our superclass first.
const InheritedNameSet *parentSet = nullptr;
if (auto superclassDecl = classDecl->getSuperClass()) {
parentSet = getAllPropertyNames(superclassDecl, forInstance);
}
// Create the set of properties.
known = allProperties.insert(
{ std::pair<const clang::ObjCInterfaceDecl *, char>(classDecl,
forInstance),
llvm::make_unique<InheritedNameSet>(parentSet) }).first;
// Local function to add properties from the given set.
auto addProperties = [&](clang::DeclContext::decl_range members) {
for (auto member : members) {
// Add Objective-C property names.
if (auto property = dyn_cast<clang::ObjCPropertyDecl>(member)) {
if (forInstance)
known->second->add(property->getName());
continue;
}
// Add no-parameter, non-void method names.
if (auto method = dyn_cast<clang::ObjCMethodDecl>(member)) {
if (method->getSelector().isUnarySelector() &&
!method->getReturnType()->isVoidType() &&
!method->hasRelatedResultType() &&
method->isInstanceMethod() == forInstance) {
known->second->add(method->getSelector().getNameForSlot(0));
continue;
}
}
}
};
// Dig out the class definition.
auto classDef = classDecl->getDefinition();
if (!classDef) return known->second.get();
// Collect property names from the class definition.
addProperties(classDef->decls());
// Dig out the module that owns the class definition.
auto module = classDef->getImportedOwningModule();
if (module) module = module->getTopLevelModule();
// Collect property names from all categories and extensions in the same
// module as the class.
for (auto category : classDef->known_categories()) {
auto categoryModule = category->getImportedOwningModule();
if (categoryModule) categoryModule = categoryModule->getTopLevelModule();
if (module != categoryModule) continue;
addProperties(category->decls());
}
return known->second.get();
}