Skip to content

Commit ef7b803

Browse files
committed
[Type checker] Warn about classes conforming to AnyObject in Swift 3 mode.
Swift 3 allowed a class to explicitly conform to AnyObject, although it was meaningless. Recent AnyObject-related changes started rejecting such conformances as ill-formed; allow them with a warning + Fix-It in Swift 3 compatibility mode.
1 parent 4db0a4a commit ef7b803

File tree

4 files changed

+81
-28
lines changed

4 files changed

+81
-28
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1909,7 +1909,8 @@ ERROR(inheritance_from_cf_class,none,
19091909
ERROR(inheritance_from_objc_runtime_visible_class,none,
19101910
"cannot inherit from class %0 because it is only visible via the "
19111911
"Objective-C runtime", (Identifier))
1912-
1912+
WARNING(class_inherits_anyobject,none,
1913+
"conformance of class %0 to 'AnyObject' is redundant", (Type))
19131914

19141915
// Enums
19151916
ERROR(enum_case_access,none,

lib/Sema/TypeCheckDecl.cpp

Lines changed: 67 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,55 @@ void TypeChecker::checkInheritanceClause(Decl *decl,
385385
}
386386
}
387387

388+
// Retrieve the location of the start of the inheritance clause.
389+
auto getStartLocOfInheritanceClause = [&] {
390+
if (auto genericTypeDecl = dyn_cast<GenericTypeDecl>(decl)) {
391+
if (auto genericParams = genericTypeDecl->getGenericParams())
392+
return genericParams->getSourceRange().End;
393+
394+
return genericTypeDecl->getNameLoc();
395+
}
396+
397+
if (auto typeDecl = dyn_cast<TypeDecl>(decl))
398+
return typeDecl->getNameLoc();
399+
400+
if (auto ext = dyn_cast<ExtensionDecl>(decl))
401+
return ext->getSourceRange().End;
402+
403+
return SourceLoc();
404+
};
405+
406+
// Compute the source range to be used when removing something from an
407+
// inheritance clause.
408+
auto getRemovalRange = [&](unsigned i) {
409+
// If there is just one entry, remove the entire inheritance clause.
410+
if (inheritedClause.size() == 1) {
411+
SourceLoc start = getStartLocOfInheritanceClause();
412+
SourceLoc end = inheritedClause[i].getSourceRange().End;
413+
return SourceRange(Lexer::getLocForEndOfToken(Context.SourceMgr, start),
414+
Lexer::getLocForEndOfToken(Context.SourceMgr, end));
415+
}
416+
417+
// If we're at the first entry, remove from the start of this entry to the
418+
// start of the next entry.
419+
if (i == 0) {
420+
return SourceRange(inheritedClause[i].getSourceRange().Start,
421+
inheritedClause[i+1].getSourceRange().Start);
422+
}
423+
424+
// Otherwise, remove from the end of the previous entry to the end of this
425+
// entry.
426+
SourceLoc afterPriorLoc =
427+
Lexer::getLocForEndOfToken(Context.SourceMgr,
428+
inheritedClause[i-1].getSourceRange().End);
429+
430+
SourceLoc afterMyEndLoc =
431+
Lexer::getLocForEndOfToken(Context.SourceMgr,
432+
inheritedClause[i].getSourceRange().End);
433+
434+
return SourceRange(afterPriorLoc, afterMyEndLoc);
435+
};
436+
388437
// Check all of the types listed in the inheritance clause.
389438
Type superclassTy;
390439
SourceRange superclassRange;
@@ -424,16 +473,10 @@ void TypeChecker::checkInheritanceClause(Decl *decl,
424473
CanType inheritedCanTy = inheritedTy->getCanonicalType();
425474
auto knownType = inheritedTypes.find(inheritedCanTy);
426475
if (knownType != inheritedTypes.end()) {
427-
SourceLoc afterPriorLoc
428-
= Lexer::getLocForEndOfToken(Context.SourceMgr,
429-
inheritedClause[i-1].getSourceRange().End);
430-
SourceLoc afterMyEndLoc
431-
= Lexer::getLocForEndOfToken(Context.SourceMgr,
432-
inherited.getSourceRange().Start);
433-
476+
auto removeRange = getRemovalRange(i);
434477
diagnose(inherited.getSourceRange().Start,
435478
diag::duplicate_inheritance, inheritedTy)
436-
.fixItRemoveChars(afterPriorLoc, afterMyEndLoc)
479+
.fixItRemoveChars(removeRange.Start, removeRange.End)
437480
.highlight(knownType->second);
438481
inherited.setInvalidType(Context);
439482
continue;
@@ -457,6 +500,18 @@ void TypeChecker::checkInheritanceClause(Decl *decl,
457500
}
458501
continue;
459502
}
503+
504+
// Swift 3 compatibility:
505+
if (Context.LangOpts.isSwiftVersion3() && isa<ClassDecl>(decl) &&
506+
inheritedTy->isAnyObject()) {
507+
auto classDecl = cast<ClassDecl>(decl);
508+
auto removeRange = getRemovalRange(i);
509+
diagnose(inherited.getSourceRange().Start,
510+
diag::class_inherits_anyobject,
511+
classDecl->getDeclaredInterfaceType())
512+
.fixItRemoveChars(removeRange.Start, removeRange.End);
513+
continue;
514+
}
460515
}
461516

462517
// If this is an enum inheritance clause, check for a raw type.
@@ -472,17 +527,11 @@ void TypeChecker::checkInheritanceClause(Decl *decl,
472527

473528
// If this is not the first entry in the inheritance clause, complain.
474529
if (i > 0) {
475-
SourceLoc afterPriorLoc
476-
= Lexer::getLocForEndOfToken(
477-
Context.SourceMgr,
478-
inheritedClause[i-1].getSourceRange().End);
479-
SourceLoc afterMyEndLoc
480-
= Lexer::getLocForEndOfToken(Context.SourceMgr,
481-
inherited.getSourceRange().End);
530+
auto removeRange = getRemovalRange(i);
482531

483532
diagnose(inherited.getSourceRange().Start,
484533
diag::raw_type_not_first, inheritedTy)
485-
.fixItRemoveChars(afterPriorLoc, afterMyEndLoc)
534+
.fixItRemoveChars(removeRange.Start, removeRange.End)
486535
.fixItInsert(inheritedClause[0].getSourceRange().Start,
487536
inheritedTy.getString() + ", ");
488537

@@ -532,17 +581,10 @@ void TypeChecker::checkInheritanceClause(Decl *decl,
532581

533582
// If this is not the first entry in the inheritance clause, complain.
534583
if (i > 0) {
535-
SourceLoc afterPriorLoc
536-
= Lexer::getLocForEndOfToken(
537-
Context.SourceMgr,
538-
inheritedClause[i-1].getSourceRange().End);
539-
SourceLoc afterMyEndLoc
540-
= Lexer::getLocForEndOfToken(Context.SourceMgr,
541-
inherited.getSourceRange().End);
542-
584+
auto removeRange = getRemovalRange(i);
543585
diagnose(inherited.getSourceRange().Start,
544586
diag::superclass_not_first, inheritedTy)
545-
.fixItRemoveChars(afterPriorLoc, afterMyEndLoc)
587+
.fixItRemoveChars(removeRange.Start, removeRange.End)
546588
.fixItInsert(inheritedClause[0].getSourceRange().Start,
547589
inheritedTy.getString() + ", ");
548590

test/Compatibility/anyobject.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// RUN: %target-typecheck-verify-swift -swift-version 3
2+
3+
protocol P { }
4+
protocol Q { }
5+
6+
class Foo: AnyObject { } // expected-warning{{conformance of class 'Foo' to 'AnyObject' is redundant}}{{10-21=}}
7+
8+
class Bar: AnyObject, P { } // expected-warning{{conformance of class 'Bar' to 'AnyObject' is redundant}}{{12-23=}}
9+
10+
class Wibble: Bar, AnyObject, Q { } // expected-warning{{conformance of class 'Wibble' to 'AnyObject' is redundant}}{{18-29=}}

test/decl/protocol/conforms/placement.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ extension ExplicitSub1 : P1 { } // expected-error{{redundant conformance of 'Exp
106106
// ---------------------------------------------------------------------------
107107
// Suppression of synthesized conformances
108108
// ---------------------------------------------------------------------------
109-
class SynthesizedClass1 : AnyObject { } // expected-error{{inheritance from non-protocol, non-class type 'AnyObject'}}
109+
class SynthesizedClass1 : AnyObject { } // expected-warning{{conformance of class 'SynthesizedClass1' to 'AnyObject' is redundant}}
110110

111111
class SynthesizedClass2 { }
112112
extension SynthesizedClass2 : AnyObject { } // expected-error{{inheritance from non-protocol type 'AnyObject'}}
@@ -116,7 +116,7 @@ class SynthesizedClass3 : AnyObjectRefinement { }
116116
class SynthesizedClass4 { }
117117
extension SynthesizedClass4 : AnyObjectRefinement { }
118118

119-
class SynthesizedSubClass1 : SynthesizedClass1, AnyObject { } // expected-error{{inheritance from non-protocol, non-class type 'AnyObject'}}
119+
class SynthesizedSubClass1 : SynthesizedClass1, AnyObject { } // expected-warning{{conformance of class 'SynthesizedSubClass1' to 'AnyObject' is redundant}}
120120

121121
class SynthesizedSubClass2 : SynthesizedClass2 { }
122122
extension SynthesizedSubClass2 : AnyObject { } // expected-error{{inheritance from non-protocol type 'AnyObject'}}

0 commit comments

Comments
 (0)