Skip to content

Commit ec91244

Browse files
committed
Clang importer: teach importFullName about factory methods as initializers.
1 parent eedf0fc commit ec91244

File tree

4 files changed

+111
-8
lines changed

4 files changed

+111
-8
lines changed

lib/ClangImporter/ClangImporter.cpp

Lines changed: 90 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1643,6 +1643,19 @@ DeclName ClangImporter::Implementation::importFullName(
16431643
if (auto *nameAttr = D->getAttr<clang::SwiftNameAttr>()) {
16441644
if (hasCustomName)
16451645
*hasCustomName = true;
1646+
1647+
// If we have an Objective-C method that is being mapped to an
1648+
// initializer (e.g., a factory method whose name doesn't fit the
1649+
// convention for factory methods), make sure that it can be
1650+
// imported as an initializer.
1651+
if (auto method = dyn_cast<clang::ObjCMethodDecl>(D)) {
1652+
unsigned initPrefixLength;
1653+
CtorInitializerKind kind;
1654+
if (nameAttr->getName().startswith("init(") &&
1655+
!shouldImportAsInitializer(method, initPrefixLength, kind))
1656+
return { };
1657+
}
1658+
16461659
return parseDeclName(SwiftContext, nameAttr->getName());
16471660
}
16481661

@@ -1656,6 +1669,8 @@ DeclName ClangImporter::Implementation::importFullName(
16561669
/// Whether the result is a function name.
16571670
bool isFunction = false;
16581671
bool isInitializer = false;
1672+
unsigned initializerPrefixLen;
1673+
CtorInitializerKind initKind;
16591674
StringRef baseName;
16601675
SmallVector<StringRef, 4> argumentNames;
16611676
SmallString<16> selectorSplitScratch;
@@ -1678,7 +1693,8 @@ DeclName ClangImporter::Implementation::importFullName(
16781693
case clang::DeclarationName::ObjCOneArgSelector:
16791694
case clang::DeclarationName::ObjCZeroArgSelector: {
16801695
auto objcMethod = cast<clang::ObjCMethodDecl>(D);
1681-
isInitializer = isInitMethod(objcMethod);
1696+
isInitializer = shouldImportAsInitializer(objcMethod, initializerPrefixLen,
1697+
initKind);
16821698

16831699
// Map the Objective-C selector directly.
16841700
auto selector = D->getDeclName().getObjCSelector();
@@ -1697,8 +1713,8 @@ DeclName ClangImporter::Implementation::importFullName(
16971713

16981714
// For initializers, compute the first argument name.
16991715
if (isInitializer) {
1700-
// Skip over the 'init'.
1701-
auto argName = selector.getNameForSlot(0).substr(4);
1716+
// Skip over the prefix.
1717+
auto argName = selector.getNameForSlot(0).substr(initializerPrefixLen);
17021718

17031719
// Drop "With" if present after the "init".
17041720
bool droppedWith = false;
@@ -1714,7 +1730,8 @@ DeclName ClangImporter::Implementation::importFullName(
17141730
// put "with" back.
17151731
if (droppedWith && isSwiftReservedName(argName)) {
17161732
selectorSplitScratch = "with";
1717-
selectorSplitScratch += selector.getNameForSlot(0).substr(8);
1733+
selectorSplitScratch += selector.getNameForSlot(0).substr(
1734+
initializerPrefixLen + 4);
17181735
argName = selectorSplitScratch;
17191736
}
17201737

@@ -2625,6 +2642,75 @@ bool ClangImporter::Implementation::isInitMethod(
26252642
return camel_case::getFirstWord(selector.getNameForSlot(0)) == "init";
26262643
}
26272644

2645+
bool ClangImporter::Implementation::shouldImportAsInitializer(
2646+
const clang::ObjCMethodDecl *method,
2647+
unsigned &prefixLength,
2648+
CtorInitializerKind &kind) {
2649+
/// Is this an initializer?
2650+
if (isInitMethod(method)) {
2651+
prefixLength = 4;
2652+
kind = CtorInitializerKind::Designated;
2653+
return true;
2654+
}
2655+
2656+
// It must be a class method.
2657+
if (!method->isClassMethod()) return false;
2658+
2659+
// Said class methods must be in an actual class.
2660+
auto objcClass = method->getClassInterface();
2661+
if (!objcClass) return false;
2662+
2663+
// Check whether we should try to import this factory method as an
2664+
// initializer.
2665+
switch (getFactoryAsInit(objcClass, method)) {
2666+
case FactoryAsInitKind::AsInitializer:
2667+
// Okay; check for the correct result type below.
2668+
prefixLength = 0;
2669+
break;
2670+
2671+
case FactoryAsInitKind::Infer: {
2672+
// See if we can match the class name to the beginning of the first
2673+
// selector piece.
2674+
auto firstPiece = method->getSelector().getNameForSlot(0);
2675+
StringRef firstArgLabel = matchLeadingTypeName(firstPiece,
2676+
objcClass->getName());
2677+
if (firstArgLabel.size() == firstPiece.size())
2678+
return false;
2679+
2680+
// Store the prefix length.
2681+
prefixLength = firstPiece.size() - firstArgLabel.size();
2682+
2683+
// Continue checking the result type, below.
2684+
break;
2685+
}
2686+
2687+
case FactoryAsInitKind::AsClassMethod:
2688+
return false;
2689+
}
2690+
2691+
// Determine whether we have a suitable return type.
2692+
if (method->hasRelatedResultType()) {
2693+
// When the factory method has an "instancetype" result type, we
2694+
// can import it as a convenience factory method.
2695+
kind = CtorInitializerKind::ConvenienceFactory;
2696+
} else if (auto objcPtr = method->getReturnType()
2697+
->getAs<clang::ObjCObjectPointerType>()) {
2698+
if (objcPtr->getInterfaceDecl() != objcClass) {
2699+
// FIXME: Could allow a subclass here, but the rest of the compiler
2700+
// isn't prepared for that yet.
2701+
return false;
2702+
}
2703+
2704+
// Factory initializer.
2705+
kind = CtorInitializerKind::Factory;
2706+
} else {
2707+
// Not imported as an initializer.
2708+
return false;
2709+
}
2710+
2711+
return true;
2712+
}
2713+
26282714
#pragma mark Name lookup
26292715
void ClangImporter::lookupValue(Identifier name, VisibleDeclConsumer &consumer){
26302716
auto &pp = Impl.Instance->getPreprocessor();

lib/ClangImporter/ImporterImpl.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,20 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
430430
/// that will be imported as a Swift initializer.
431431
bool isInitMethod(const clang::ObjCMethodDecl *method);
432432

433+
/// Determine whether this Objective-C method should be imported as
434+
/// an initializer.
435+
///
436+
/// \param prefixLength Will be set to the length of the prefix that
437+
/// should be stripped from the first selector piece, e.g., "init"
438+
/// or the restated name of the class in a factory method.
439+
///
440+
/// \param kind Will be set to the kind of initializer being
441+
/// imported. Note that this does not distinguish designated
442+
/// vs. convenience; both will be classified as "designated".
443+
bool shouldImportAsInitializer(const clang::ObjCMethodDecl *method,
444+
unsigned &prefixLength,
445+
CtorInitializerKind &kind);
446+
433447
private:
434448
/// \brief Generation number that is used for crude versioning.
435449
///

test/IDE/Inputs/swift_name_objc.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ SWIFT_NAME(SomeClass)
1919
- (instancetype)initWithDefault;
2020
- (void)instanceMethodWithX:(float)x y:(float)y z:(float)z;
2121
+ (instancetype)someClassWithDouble:(double)d;
22+
+ (instancetype)someClassWithTry:(BOOL)shouldTry;
23+
+ (NSObject *)buildWithObject:(NSObject *)object SWIFT_NAME(init(object:));
2224

2325
@property (readonly,nonatomic) float floatProperty;
2426
@property (readwrite,nonatomic) double doubleProperty;

test/IDE/dump_swift_lookup_tables_objc.swift

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,10 @@
1414
// CHECK-NEXT: doubleProperty --> doubleProperty{{$}}
1515
// CHECK-NEXT: extensionMethodWithX --> extensionMethodWithX(_:y:)
1616
// CHECK-NEXT: floatProperty --> floatProperty{{$}}
17-
// CHECK-NEXT: init --> init(float:), init(withDefault:)
17+
// CHECK-NEXT: init --> init(float:), init(withDefault:), init(double:), init(withTry:){{$}}
1818
// CHECK-NEXT: instanceMethodWithX --> instanceMethodWithX(_:y:z:)
1919
// CHECK-NEXT: protoInstanceMethodWithX --> protoInstanceMethodWithX(_:y:)
2020
// CHECK-NEXT: setAccessibilityFloat --> setAccessibilityFloat(_:)
21-
// CHECK-NEXT: someClassWithDouble --> someClassWithDouble(_:)
2221

2322
// CHECK: Full name -> entry mappings:
2423
// CHECK-NEXT: NSAccessibility:
@@ -43,15 +42,17 @@
4342
// CHECK-NEXT: SNSomeClass: -[SNSomeClass extensionMethodWithX:y:]
4443
// CHECK-NEXT: floatProperty:
4544
// CHECK-NEXT: SNSomeClass: SNSomeClass.floatProperty
45+
// CHECK-NEXT: init(double:):
46+
// CHECK-NEXT: SNSomeClass: +[SNSomeClass someClassWithDouble:]
4647
// CHECK-NEXT: init(float:):
4748
// CHECK-NEXT: SNSomeClass: -[SNSomeClass initWithFloat:]
4849
// CHECK-NEXT: init(withDefault:):
4950
// CHECK-NEXT: SNSomeClass: -[SNSomeClass initWithDefault]
51+
// CHECK-NEXT: init(withTry:):
52+
// CHECK-NEXT: SNSomeClass: +[SNSomeClass someClassWithTry:]
5053
// CHECK-NEXT: instanceMethodWithX(_:y:z:):
5154
// CHECK-NEXT: SNSomeClass: -[SNSomeClass instanceMethodWithX:y:z:]
5255
// CHECK-NEXT: protoInstanceMethodWithX(_:y:):
5356
// CHECK-NEXT: SNSomeProtocol: -[SNSomeProtocol protoInstanceMethodWithX:y:]
5457
// CHECK-NEXT: setAccessibilityFloat(_:):
5558
// CHECK-NEXT: NSAccessibility: -[NSAccessibility setAccessibilityFloat:]
56-
// CHECK-NEXT: someClassWithDouble(_:):
57-
// CHECK-NEXT: SNSomeClass: +[SNSomeClass someClassWithDouble:]

0 commit comments

Comments
 (0)