Skip to content

Commit a1020d0

Browse files
committed
Diagnose @objcImpl kind and settability errors
Sema now diagnoses @objcImpl implementations with: • The wrong settability (i.e. a `let` used for a `readwrite` property) • The wrong kind (i.e. a method used for a property)
1 parent 7444e62 commit a1020d0

File tree

4 files changed

+56
-5
lines changed

4 files changed

+56
-5
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1624,6 +1624,15 @@ ERROR(objc_implementation_wrong_category,none,
16241624
"%select{main class interface|category %3}3",
16251625
(DescriptiveDeclKind, ValueDecl *, Identifier, Identifier))
16261626

1627+
ERROR(objc_implementation_wrong_decl_kind,none,
1628+
"%0 %1 does not match the %2 declared by the header",
1629+
(DescriptiveDeclKind, ValueDecl *, DescriptiveDeclKind))
1630+
1631+
ERROR(objc_implementation_must_be_settable,none,
1632+
"%0 %1 should be settable to match the settable %2 declared by the "
1633+
"header",
1634+
(DescriptiveDeclKind, ValueDecl *, DescriptiveDeclKind))
1635+
16271636
ERROR(objc_implementation_wrong_objc_name,none,
16281637
"selector %0 for %1 %2 not found in header; did you mean %3?",
16291638
(ObjCSelector, DescriptiveDeclKind, ValueDecl *, ObjCSelector))

lib/Sema/TypeCheckDeclObjC.cpp

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3005,6 +3005,9 @@ class ObjCImplementationChecker {
30053005
WrongImplicitObjCName,
30063006
WrongStaticness,
30073007
WrongCategory,
3008+
WrongDeclKind,
3009+
// WrongType,
3010+
WrongWritability,
30083011

30093012
Match,
30103013
MatchWithExplicitObjCName,
@@ -3228,11 +3231,24 @@ class ObjCImplementationChecker {
32283231
return false;
32293232
}
32303233

3234+
static bool areSwiftNamesEqual(DeclName lhs, DeclName rhs) {
3235+
// Conflate `foo()` and `foo`. This allows us to diagnose
3236+
// method-vs.-property mistakes more nicely.
3237+
3238+
if (lhs.isCompoundName() && lhs.getArgumentNames().empty())
3239+
lhs = lhs.getBaseName();
3240+
3241+
if (rhs.isCompoundName() && rhs.getArgumentNames().empty())
3242+
rhs = rhs.getBaseName();
3243+
3244+
return lhs == rhs;
3245+
}
3246+
32313247
MatchOutcome matches(ValueDecl *req, ValueDecl *cand,
32323248
ObjCSelector explicitObjCName) const {
32333249
bool hasObjCNameMatch =
32343250
req->getObjCRuntimeName() == cand->getObjCRuntimeName();
3235-
bool hasSwiftNameMatch = req->getName() == cand->getName();
3251+
bool hasSwiftNameMatch = areSwiftNamesEqual(req->getName(), cand->getName());
32363252

32373253
// If neither the ObjC nor Swift names match, there's absolutely no reason
32383254
// to think these two methods are related.
@@ -3261,10 +3277,16 @@ class ObjCImplementationChecker {
32613277
!= req->getDeclContext())
32623278
return MatchOutcome::WrongCategory;
32633279

3264-
// FIXME: Diagnose candidate without a required setter
3265-
// FIXME: Diagnose declaration kind mismatches
3280+
if (cand->getKind() != req->getKind())
3281+
return MatchOutcome::WrongDeclKind;
3282+
32663283
// FIXME: Diagnose type mismatches (with allowance for extra optionality)
32673284

3285+
if (auto reqVar = dyn_cast<AbstractStorageDecl>(req))
3286+
if (reqVar->isSettable(nullptr) &&
3287+
!cast<AbstractStorageDecl>(cand)->isSettable(nullptr))
3288+
return MatchOutcome::WrongWritability;
3289+
32683290
// If we got here, everything matched. But at what quality?
32693291
if (explicitObjCName)
32703292
return MatchOutcome::MatchWithExplicitObjCName;
@@ -3325,6 +3347,16 @@ class ObjCImplementationChecker {
33253347
getCategoryName(cand->getDeclContext()->
33263348
getImplementedObjCContext()));
33273349
return;
3350+
3351+
case MatchOutcome::WrongDeclKind:
3352+
diagnose(cand, diag::objc_implementation_wrong_decl_kind,
3353+
cand->getDescriptiveKind(), cand, req->getDescriptiveKind());
3354+
return;
3355+
3356+
case MatchOutcome::WrongWritability:
3357+
diagnose(cand, diag::objc_implementation_must_be_settable,
3358+
cand->getDescriptiveKind(), cand, req->getDescriptiveKind());
3359+
return;
33283360
}
33293361

33303362
llvm_unreachable("Unknown MatchOutcome");

test/decl/ext/Inputs/objc_implementation.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
- (void)methodFromHeader2:(int)param;
1919
- (void)methodFromHeader3:(int)param;
2020
- (void)methodFromHeader4:(int)param;
21+
- (int)methodFromHeader5;
2122

2223
@property int propertyFromHeader1;
2324
@property int propertyFromHeader2;
@@ -28,6 +29,7 @@
2829
@property int propertyFromHeader7;
2930
@property int propertyFromHeader8;
3031
@property int propertyFromHeader9;
32+
@property int propertyFromHeader10;
3133

3234
@property (readonly) int readonlyPropertyFromHeader1;
3335
@property (readonly) int readonlyPropertyFromHeader2;

test/decl/ext/objc_implementation.swift

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@
3838
// expected-note@-3 {{add 'final' to define a Swift instance method that cannot be overridden}} {{3-3=final }}
3939
}
4040

41+
var methodFromHeader5: CInt
42+
// expected-error@-1 {{property 'methodFromHeader5' does not match the instance method declared by the header}}
43+
4144
var propertyFromHeader1: CInt
4245
// OK, provides an implementation with a stored property
4346

@@ -57,16 +60,21 @@
5760
}
5861

5962
@objc let propertyFromHeader5: CInt
60-
// FIXME: bad, needs to be settable
63+
// expected-error@-1 {{property 'propertyFromHeader5' should be settable to match the settable property declared by the header}}
6164

6265
@objc var propertyFromHeader6: CInt {
63-
// FIXME: bad, needs a setter
66+
// expected-error@-1 {{property 'propertyFromHeader6' should be settable to match the settable property declared by the header}}
6467
get { return 1 }
6568
}
6669

6770
final var propertyFromHeader8: CInt
6871
// FIXME: Should complain about final not fulfilling the @objc requirement
6972

73+
func propertyFromHeader10() -> CInt {
74+
// expected-error@-1 {{instance method 'propertyFromHeader10()' does not match the property declared by the header}}
75+
return 1
76+
}
77+
7078
var readonlyPropertyFromHeader1: CInt
7179
// OK, provides an implementation with a stored property that's nonpublicly settable
7280

0 commit comments

Comments
 (0)