Skip to content

Commit 6fe6266

Browse files
committed
[ObjC Interop] Map Swift @objc properties named isFoo to ObjC Cocoa conventions
The Objective-C Cocoa convention eschew "is" on property names, but use it on the getter, while the Swift API guidelines state that Boolean properties should read as assertions (e.g., "isEmpty" rather than "empty"). Map Swift properties named "isFoo" to Objective-C by removing the "is" from the resulting Objective-C property name (so it will be named "foo") and from the setter (which will have the Objective-C selector "setFoo:") while retaining the "is" for the getter selector ("isFoo"). Fixes rdar://problem/17090661.
1 parent e18d70a commit 6fe6266

File tree

4 files changed

+38
-27
lines changed

4 files changed

+38
-27
lines changed

include/swift/AST/Decl.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4141,11 +4141,6 @@ class VarDecl : public AbstractStorageDecl {
41414141
/// Return the Objective-C runtime name for this property.
41424142
Identifier getObjCPropertyName() const;
41434143

4144-
/// Retrieve the default Objective-C selector for the getter of a
4145-
/// property of the given name.
4146-
static ObjCSelector getDefaultObjCGetterSelector(ASTContext &ctx,
4147-
Identifier propertyName);
4148-
41494144
/// Retrieve the default Objective-C selector for the setter of a
41504145
/// property of the given name.
41514146
static ObjCSelector getDefaultObjCSetterSelector(ASTContext &ctx,

lib/AST/Decl.cpp

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2936,9 +2936,15 @@ ObjCSelector AbstractStorageDecl::getObjCGetterSelector(
29362936
}
29372937
}
29382938

2939-
// The getter selector is the property name itself.
2939+
// If the Swift name starts with the word "is", use that Swift name as the
2940+
// getter name.
29402941
auto var = cast<VarDecl>(this);
2941-
return VarDecl::getDefaultObjCGetterSelector(ctx, var->getObjCPropertyName());
2942+
if (ctx.LangOpts.OmitNeedlessWords &&
2943+
camel_case::getFirstWord(var->getName().str()) == "is")
2944+
return ObjCSelector(ctx, 0, { var->getName() });
2945+
2946+
// The getter selector is the property name itself.
2947+
return ObjCSelector(ctx, 0, { var->getObjCPropertyName() });
29422948
}
29432949

29442950
ObjCSelector AbstractStorageDecl::getObjCSetterSelector(
@@ -3217,21 +3223,26 @@ Identifier VarDecl::getObjCPropertyName() const {
32173223
return name->getSelectorPieces()[0];
32183224
}
32193225

3220-
return getName();
3221-
}
3226+
// If the Swift property name starts with the word "is", strip the
3227+
// "is" and lowercase the rest when forming the Objective-C property
3228+
// name.
3229+
ASTContext &ctx = getASTContext();
3230+
StringRef nameStr = getName().str();
3231+
if (ctx.LangOpts.OmitNeedlessWords &&
3232+
camel_case::getFirstWord(nameStr) == "is") {
3233+
SmallString<16> scratch;
3234+
return ctx.getIdentifier(camel_case::toLowercaseWord(nameStr.substr(2),
3235+
scratch));
3236+
}
32223237

3223-
ObjCSelector VarDecl::getDefaultObjCGetterSelector(ASTContext &ctx,
3224-
Identifier propertyName) {
3225-
return ObjCSelector(ctx, 0, propertyName);
3238+
return getName();
32263239
}
32273240

3228-
32293241
ObjCSelector VarDecl::getDefaultObjCSetterSelector(ASTContext &ctx,
32303242
Identifier propertyName) {
32313243
llvm::SmallString<16> scratch;
32323244
scratch += "set";
32333245
camel_case::appendSentenceCase(scratch, propertyName.str());
3234-
32353246
return ObjCSelector(ctx, 1, ctx.getIdentifier(scratch));
32363247
}
32373248

lib/PrintAsObjC/PrintAsObjC.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -587,8 +587,7 @@ class ObjCPrinter : private DeclVisitor<ObjCPrinter>,
587587
// Handle custom accessor names.
588588
llvm::SmallString<64> buffer;
589589
if (hasReservedName ||
590-
VD->getObjCGetterSelector() !=
591-
VarDecl::getDefaultObjCGetterSelector(ctx, objCName)) {
590+
VD->getObjCGetterSelector() != ObjCSelector(ctx, 0, { objCName })) {
592591
os << ", getter=" << VD->getObjCGetterSelector().getString(buffer);
593592
}
594593
if (VD->isSettable(nullptr)) {

test/PrintAsObjC/classes.swift

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@
66
// RUN: mkdir %t
77

88
// FIXME: BEGIN -enable-source-import hackaround
9-
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -emit-module -o %t %S/../Inputs/clang-importer-sdk/swift-modules/ObjectiveC.swift
10-
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -emit-module -o %t %S/../Inputs/clang-importer-sdk/swift-modules/CoreGraphics.swift
11-
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -emit-module -o %t %S/../Inputs/clang-importer-sdk/swift-modules/Foundation.swift
12-
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -emit-module -o %t %S/../Inputs/clang-importer-sdk/swift-modules/AppKit.swift
9+
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -emit-module -o %t -enable-omit-needless-words %S/../Inputs/clang-importer-sdk/swift-modules/ObjectiveC.swift
10+
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -emit-module -o %t -enable-omit-needless-words %S/../Inputs/clang-importer-sdk/swift-modules/CoreGraphics.swift
11+
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -emit-module -o %t -enable-omit-needless-words %S/../Inputs/clang-importer-sdk/swift-modules/Foundation.swift
12+
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -emit-module -o %t -enable-omit-needless-words %S/../Inputs/clang-importer-sdk/swift-modules/AppKit.swift
1313
// FIXME: END -enable-source-import hackaround
1414

1515

16-
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -emit-module -o %t %s -disable-objc-attr-requires-foundation-module
17-
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -parse-as-library %t/classes.swiftmodule -parse -emit-objc-header-path %t/classes.h -import-objc-header %S/../Inputs/empty.h -disable-objc-attr-requires-foundation-module
16+
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -emit-module -o %t %s -enable-omit-needless-words -disable-objc-attr-requires-foundation-module
17+
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -parse-as-library -enable-omit-needless-words %t/classes.swiftmodule -parse -emit-objc-header-path %t/classes.h -import-objc-header %S/../Inputs/empty.h -disable-objc-attr-requires-foundation-module
1818
// RUN: FileCheck %s < %t/classes.h
1919
// RUN: FileCheck --check-prefix=NEGATIVE %s < %t/classes.h
2020
// RUN: %check-in-clang %t/classes.h
@@ -91,8 +91,11 @@ class ClassWithCustomNameSub : ClassWithCustomName {}
9191
// CHECK-NEXT: @end
9292
@objc class ClassWithNSObjectProtocol : NSObjectProtocol {
9393
var description: String { return "me" }
94-
func conformsToProtocol(_: Protocol) -> Bool { return false }
95-
func isKindOfClass(aClass: AnyClass) -> Bool { return false }
94+
@objc(conformsToProtocol:)
95+
func conforms(to _: Protocol) -> Bool { return false }
96+
97+
@objc(isKindOfClass:)
98+
func isKind(of aClass: AnyClass) -> Bool { return false }
9699
}
97100

98101
// CHECK-LABEL: @interface Initializers
@@ -236,7 +239,7 @@ typealias AliasForNSRect = NSRect
236239
func emptyArray() -> NSArray { return NSArray() }
237240
func maybeArray() -> NSArray? { return nil }
238241

239-
func someEnum() -> NSRuncingMode { return .Mince }
242+
func someEnum() -> NSRuncingMode { return .mince }
240243

241244
func zone() -> NSZone { return nil }
242245

@@ -394,6 +397,7 @@ public class NonObjCClass { }
394397
// CHECK-NEXT: + (double)staticDouble;
395398
// CHECK-NEXT: @property (nonatomic, strong) Properties * _Nullable wobble;
396399
// CHECK-NEXT: @property (nonatomic, getter=isEnabled, setter=setIsEnabled:) BOOL enabled;
400+
// CHECK-NEXT: @property (nonatomic, getter=isAnimated) BOOL animated;
397401
// CHECK-NEXT: @property (nonatomic, getter=register, setter=setRegister:) BOOL register_;
398402
// CHECK-NEXT: @property (nonatomic, readonly, strong, getter=this) Properties * _Nonnull this_;
399403
// CHECK-NEXT: init
@@ -425,15 +429,15 @@ public class NonObjCClass { }
425429

426430
weak var weakProto: MyProtocol?
427431
weak var weakCF: CFTypeRef?
428-
weak var weakCFString: CFStringRef?
432+
weak var weakCFString: CFString?
429433

430434
typealias CFTypeRefAlias = CFTypeRef
431435

432436
var strongCF: CFTypeRef?
433437
var strongCFAlias: CFTypeRefAlias?
434438

435439
var anyCF: CFAliasForType?
436-
var anyCF2: CFAliasForTypeRef?
440+
var anyCF2: CFAliasForType?
437441

438442
@IBOutlet weak var outlet: AnyObject!
439443
@IBOutlet var typedOutlet: Properties!
@@ -468,6 +472,8 @@ public class NonObjCClass { }
468472
@objc(setIsEnabled:) set { }
469473
}
470474

475+
var isAnimated: Bool = true
476+
471477
var register: Bool = false
472478
var this: Properties { return self }
473479
}

0 commit comments

Comments
 (0)