Skip to content

Commit 0505c38

Browse files
committed
Tweak @_objcImpl deprecation warning behavior
• Simpler wording • Now emitted only if there are no other @objcImpl diagnostics (so @implementation would not emit any new errors)
1 parent d2e9fb2 commit 0505c38

File tree

6 files changed

+59
-26
lines changed

6 files changed

+59
-26
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1740,8 +1740,8 @@ ERROR(swift_native_objc_runtime_base_not_on_root_class,none,
17401740
"to root classes", ())
17411741

17421742
WARNING(objc_implementation_early_spelling_deprecated,none,
1743-
"warning-only '@_objcImplementation' spelling is deprecated; use "
1744-
"'@implementation' instead", ())
1743+
"'@_objcImplementation' is deprecated; use '@implementation' instead",
1744+
())
17451745
ERROR(attr_objc_implementation_must_be_unconditional,none,
17461746
"only unconditional extensions can implement an Objective-C '@interface'",
17471747
())

lib/Sema/TypeCheckAttr.cpp

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1518,18 +1518,8 @@ void AttributeChecker::visitNonObjCAttr(NonObjCAttr *attr) {
15181518

15191519
static bool hasObjCImplementationFeature(Decl *D, ObjCImplementationAttr *attr,
15201520
Feature requiredFeature) {
1521-
if (D->getASTContext().LangOpts.hasFeature(requiredFeature)) {
1522-
// Encourage early ObjCImplementation adopters to adopt @implementation.
1523-
// (We'll do this for CImplementation when we're ready to stabilize it.)
1524-
if (requiredFeature == Feature::ObjCImplementation
1525-
&& attr->isEarlyAdopter())
1526-
D->getASTContext().Diags.diagnose(
1527-
attr->getLocation(),
1528-
diag::objc_implementation_early_spelling_deprecated)
1529-
.fixItReplace(attr->getLocation(), "implementation");
1530-
1521+
if (D->getASTContext().LangOpts.hasFeature(requiredFeature))
15311522
return true;
1532-
}
15331523

15341524
// Allow the use of @_objcImplementation *without* Feature::ObjCImplementation
15351525
// as long as you're using the early adopter syntax. (Avoids breaking existing

lib/Sema/TypeCheckDeclObjC.cpp

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2803,17 +2803,25 @@ fixDeclarationStaticSpelling(InFlightDiagnostic &diag, ValueDecl *VD,
28032803

28042804
namespace {
28052805
class ObjCImplementationChecker {
2806-
DiagnosticEngine &diags;
2806+
Decl *decl;
2807+
2808+
ObjCImplementationAttr *getAttr() const {
2809+
return decl->getAttrs()
2810+
.getAttribute<ObjCImplementationAttr>(/*AllowInvalid=*/true);
2811+
}
28072812

28082813
template<typename Loc, typename ...ArgTypes>
28092814
InFlightDiagnostic diagnose(Loc loc, Diag<ArgTypes...> diagID,
28102815
typename detail::PassArgument<ArgTypes>::type... Args) {
2816+
hasDiagnosed = true;
2817+
2818+
auto &diags = decl->getASTContext().Diags;
28112819
auto diag = diags.diagnose(loc, diagID, std::forward<ArgTypes>(Args)...);
28122820

28132821
// Early adopters using the '@_objcImplementation' syntax may have had the
28142822
// ObjCImplementationChecker evolve out from under them. Soften their errors
28152823
// to warnings so we don't break their projects.
2816-
if (isEarlyAdopter
2824+
if (getAttr()->isEarlyAdopter()
28172825
&& diags.declaredDiagnosticKindFor(diagID.ID) == DiagnosticKind::Error)
28182826
diag.wrapIn(diag::wrap_objc_implementation_will_become_error);
28192827

@@ -2825,18 +2833,14 @@ class ObjCImplementationChecker {
28252833
/// Candidates with their explicit ObjC names, if any.
28262834
llvm::SmallDenseMap<ValueDecl *, ObjCSelector, 16> unmatchedCandidates;
28272835

2828-
bool isEarlyAdopter;
2836+
bool hasDiagnosed = false;
28292837

28302838
public:
28312839
ObjCImplementationChecker(Decl *D)
2832-
: diags(D->getASTContext().Diags)
2840+
: decl(D), hasDiagnosed(getAttr()->isInvalid())
28332841
{
28342842
assert(!D->hasClangNode() && "passed interface, not impl, to checker");
28352843

2836-
isEarlyAdopter = D->getAttrs()
2837-
.getAttribute<ObjCImplementationAttr>(/*AllowInvalid=*/true)
2838-
->isEarlyAdopter();
2839-
28402844
if (auto func = dyn_cast<AbstractFunctionDecl>(D)) {
28412845
addCandidate(D);
28422846
addRequirement(D->getImplementedObjCDecl());
@@ -3607,6 +3611,31 @@ class ObjCImplementationChecker {
36073611
.fixItInsert(cand->getAttributeInsertionLoc(true), "final ");
36083612
}
36093613
}
3614+
3615+
void diagnoseEarlyAdopterDeprecation() {
3616+
// Only encourage use of @implementation for early adopters, and only when
3617+
// there are no mismatches that they might be working around with it.
3618+
if (hasDiagnosed || !getAttr()->isEarlyAdopter())
3619+
return;
3620+
3621+
// Only encourage adoption if the corresponding language feature is enabled.
3622+
if (isa<ExtensionDecl>(decl) &&
3623+
!decl->getASTContext().LangOpts.hasFeature(Feature::ObjCImplementation))
3624+
return;
3625+
3626+
if (isa<AbstractFunctionDecl>(decl) &&
3627+
!decl->getASTContext().LangOpts.hasFeature(Feature::CImplementation))
3628+
return;
3629+
3630+
// Only encourage @_objcImplementation *extension* adopters to adopt
3631+
// @implementation; @_objcImplementation @_cdecl hasn't been stabilized yet.
3632+
if (!isa<ExtensionDecl>(decl))
3633+
return;
3634+
3635+
diagnose(getAttr()->getLocation(),
3636+
diag::objc_implementation_early_spelling_deprecated)
3637+
.fixItReplace(getAttr()->getLocation(), "implementation");
3638+
}
36103639
};
36113640
}
36123641

@@ -3626,6 +3655,7 @@ evaluate(Evaluator &evaluator, Decl *D) const {
36263655
checker.matchRequirements();
36273656
checker.diagnoseUnmatchedCandidates();
36283657
checker.diagnoseUnmatchedRequirements();
3658+
checker.diagnoseEarlyAdopterDeprecation();
36293659

36303660
return evaluator::SideEffect();
36313661
}

test/decl/ext/Inputs/objc_implementation.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,9 @@
127127

128128
@end
129129

130+
@interface ObjCClass (EmptyCategory)
131+
@end
132+
130133
@protocol PartiallyOptionalProtocol
131134

132135
- (void)requiredMethod1;

test/decl/ext/objc_implementation.swift

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-typecheck-verify-swift -import-objc-header %S/Inputs/objc_implementation.h -enable-experimental-feature CImplementation
1+
// RUN: %target-typecheck-verify-swift -import-objc-header %S/Inputs/objc_implementation.h -enable-experimental-feature ObjCImplementation -enable-experimental-feature CImplementation
22
// REQUIRES: objc_interop
33

44
protocol EmptySwiftProto {}
@@ -432,14 +432,21 @@ protocol EmptySwiftProto {}
432432
func nonPointerArgument(_: CInt!) {} // expected-error {{method cannot be in an @_objcImplementation extension of a class (without final or @nonobjc) because the type of the parameter cannot be represented in Objective-C}}
433433
}
434434

435+
// Intentionally using `@_objcImplementation` for this test; do not upgrade!
436+
@_objcImplementation(EmptyCategory) extension ObjCClass {
437+
// expected-warning@-1 {{'@_objcImplementation' is deprecated; use '@implementation' instead}} {{2-21=implementation}}
438+
}
439+
435440
@_objcImplementation extension ObjCImplSubclass {
436-
@objc(initFromProtocol1:)
441+
// expected-warning@-1 {{'@_objcImplementation' is deprecated; use '@implementation' instead}} {{2-21=implementation}}
442+
@objc(initFromProtocol1:)
437443
required public init?(fromProtocol1: CInt) {
438444
// OK
439445
}
440446
}
441447

442448
@_objcImplementation extension ObjCBasicInitClass {
449+
// expected-warning@-1 {{'@_objcImplementation' is deprecated; use '@implementation' instead}} {{2-21=implementation}}
443450
init() {
444451
// OK
445452
}

test/decl/ext/objc_implementation_features.swift

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@
33
// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk-nosource) -typecheck %s -import-objc-header %S/Inputs/objc_implementation.h 2>&1 | %FileCheck --check-prefixes NO,CHECK %s
44
// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk-nosource) -typecheck %s -import-objc-header %S/Inputs/objc_implementation.h -enable-experimental-feature ObjCImplementation 2>&1 | %FileCheck --check-prefixes YES,CHECK %s
55

6-
// CHECK-DAG: objc_implementation_features.swift:[[@LINE+3]]:{{[0-9]+}}: warning: extension for main class interface should provide implementation for instance method 'subclassMethod(fromHeader1:)'; this will become an error after adopting '@implementation'
7-
// NO-NOT: objc_implementation_features.swift:[[@LINE+2]]:{{[0-9]+}}: warning: warning-only '@_objcImplementation' spelling is deprecated; use '@implementation' instead
8-
// YES-DAG: objc_implementation_features.swift:[[@LINE+1]]:{{[0-9]+}}: warning: warning-only '@_objcImplementation' spelling is deprecated; use '@implementation' instead
6+
// NO-NOT: objc_implementation_features.swift:[[@LINE+2]]:{{[0-9]+}}: warning: '@_objcImplementation' is deprecated; use '@implementation' instead
7+
// YES-DAG: objc_implementation_features.swift:[[@LINE+1]]:{{[0-9]+}}: warning: '@_objcImplementation' is deprecated; use '@implementation' instead
8+
@_objcImplementation(EmptyCategory) extension ObjCClass {}
9+
10+
// CHECK-DAG: objc_implementation_features.swift:[[@LINE+2]]:{{[0-9]+}}: warning: extension for main class interface should provide implementation for instance method 'subclassMethod(fromHeader1:)'; this will become an error after adopting '@implementation'
11+
// CHECK-NOT: objc_implementation_features.swift:[[@LINE+1]]:{{[0-9]+}}: warning: '@_objcImplementation' is deprecated; use '@implementation' instead
912
@_objcImplementation extension ObjCSubclass {}
1013

1114
// CHECK-DAG: objc_implementation_features.swift:[[@LINE+3]]:{{[0-9]+}}: error: extension for main class interface should provide implementation for initializer 'init()'{{$}}

0 commit comments

Comments
 (0)