@@ -1643,6 +1643,19 @@ DeclName ClangImporter::Implementation::importFullName(
1643
1643
if (auto *nameAttr = D->getAttr <clang::SwiftNameAttr>()) {
1644
1644
if (hasCustomName)
1645
1645
*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
+
1646
1659
return parseDeclName (SwiftContext, nameAttr->getName ());
1647
1660
}
1648
1661
@@ -1656,6 +1669,8 @@ DeclName ClangImporter::Implementation::importFullName(
1656
1669
// / Whether the result is a function name.
1657
1670
bool isFunction = false ;
1658
1671
bool isInitializer = false ;
1672
+ unsigned initializerPrefixLen;
1673
+ CtorInitializerKind initKind;
1659
1674
StringRef baseName;
1660
1675
SmallVector<StringRef, 4 > argumentNames;
1661
1676
SmallString<16 > selectorSplitScratch;
@@ -1678,7 +1693,8 @@ DeclName ClangImporter::Implementation::importFullName(
1678
1693
case clang::DeclarationName::ObjCOneArgSelector:
1679
1694
case clang::DeclarationName::ObjCZeroArgSelector: {
1680
1695
auto objcMethod = cast<clang::ObjCMethodDecl>(D);
1681
- isInitializer = isInitMethod (objcMethod);
1696
+ isInitializer = shouldImportAsInitializer (objcMethod, initializerPrefixLen,
1697
+ initKind);
1682
1698
1683
1699
// Map the Objective-C selector directly.
1684
1700
auto selector = D->getDeclName ().getObjCSelector ();
@@ -1697,8 +1713,8 @@ DeclName ClangImporter::Implementation::importFullName(
1697
1713
1698
1714
// For initializers, compute the first argument name.
1699
1715
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 );
1702
1718
1703
1719
// Drop "With" if present after the "init".
1704
1720
bool droppedWith = false ;
@@ -1714,7 +1730,8 @@ DeclName ClangImporter::Implementation::importFullName(
1714
1730
// put "with" back.
1715
1731
if (droppedWith && isSwiftReservedName (argName)) {
1716
1732
selectorSplitScratch = " with" ;
1717
- selectorSplitScratch += selector.getNameForSlot (0 ).substr (8 );
1733
+ selectorSplitScratch += selector.getNameForSlot (0 ).substr (
1734
+ initializerPrefixLen + 4 );
1718
1735
argName = selectorSplitScratch;
1719
1736
}
1720
1737
@@ -2625,6 +2642,75 @@ bool ClangImporter::Implementation::isInitMethod(
2625
2642
return camel_case::getFirstWord (selector.getNameForSlot (0 )) == " init" ;
2626
2643
}
2627
2644
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
+
2628
2714
#pragma mark Name lookup
2629
2715
void ClangImporter::lookupValue (Identifier name, VisibleDeclConsumer &consumer){
2630
2716
auto &pp = Impl.Instance ->getPreprocessor ();
0 commit comments