Skip to content

Commit ffc998a

Browse files
authored
Merge pull request #63668 from beccadax/checkmate
Improve @objcImplementation member checking
2 parents 613ec88 + 7f688ef commit ffc998a

16 files changed

+842
-126
lines changed

include/swift/AST/Decl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1633,6 +1633,10 @@ class ExtensionDecl final : public GenericContext, public Decl,
16331633
/// \c isObjCImplementation() returns \c true.
16341634
Optional<Identifier> getCategoryNameForObjCImplementation() const;
16351635

1636+
/// If this extension represents an imported Objective-C category, returns the
1637+
/// category's name. Otherwise returns the empty identifier.
1638+
Identifier getObjCCategoryName() const;
1639+
16361640
// Implement isa/cast/dyncast/etc.
16371641
static bool classof(const Decl *D) {
16381642
return D->getKind() == DeclKind::Extension;

include/swift/AST/DiagnosticsSema.def

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1587,14 +1587,56 @@ ERROR(member_of_objc_implementation_not_objc_or_final,none,
15871587
"%0 %1 does not match any %0 declared in the headers for %2; did you use "
15881588
"the %0's Swift name?",
15891589
(DescriptiveDeclKind, ValueDecl *, ValueDecl *))
1590-
NOTE(fixit_add_objc_for_objc_implementation,none,
1591-
"add '@objc' to define an Objective-C-compatible %0 not declared in "
1592-
"the header",
1590+
NOTE(fixit_add_private_for_objc_implementation,none,
1591+
"add 'private' or 'fileprivate' to define an Objective-C-compatible %0 "
1592+
"not declared in the header",
15931593
(DescriptiveDeclKind))
15941594
NOTE(fixit_add_final_for_objc_implementation,none,
15951595
"add 'final' to define a Swift %0 that cannot be overridden",
15961596
(DescriptiveDeclKind))
15971597

1598+
ERROR(objc_implementation_wrong_category,none,
1599+
"%0 %1 should be implemented in extension for "
1600+
"%select{main class interface|category %2}2, not "
1601+
"%select{main class interface|category %3}3",
1602+
(DescriptiveDeclKind, ValueDecl *, Identifier, Identifier))
1603+
1604+
ERROR(objc_implementation_wrong_objc_name,none,
1605+
"selector %0 for %1 %2 not found in header; did you mean %3?",
1606+
(ObjCSelector, DescriptiveDeclKind, ValueDecl *, ObjCSelector))
1607+
1608+
ERROR(objc_implementation_wrong_swift_name,none,
1609+
"selector %0 used in header by an %1 with a different name; did you "
1610+
"mean %2?",
1611+
(ObjCSelector, DescriptiveDeclKind, DeclName))
1612+
1613+
ERROR(objc_implementation_missing_impl,none,
1614+
"extension for %select{main class interface|category %0}0 should "
1615+
"provide implementation for %1 %2",
1616+
(Identifier, DescriptiveDeclKind, ValueDecl *))
1617+
1618+
ERROR(objc_implementation_class_or_instance_mismatch,none,
1619+
"%0 %1 does not match %2 declared in header",
1620+
(DescriptiveDeclKind, ValueDecl *, DescriptiveDeclKind))
1621+
1622+
ERROR(objc_implementation_multiple_matching_candidates,none,
1623+
"found multiple implementations that could match %0 %1 with selector %2",
1624+
(DescriptiveDeclKind, ValueDecl *, ObjCSelector))
1625+
NOTE(objc_implementation_candidate_impl_here,none,
1626+
"%0 %1 is a potential match%select{|; insert '@objc(%3)' to use it}2",
1627+
(DescriptiveDeclKind, ValueDecl *, bool, StringRef))
1628+
NOTE(objc_implementation_requirement_here,none,
1629+
"%0 %1 declared in header here",
1630+
(DescriptiveDeclKind, ValueDecl *))
1631+
1632+
ERROR(objc_implementation_multiple_matching_requirements,none,
1633+
"%0 %1 could match several different members declared in the header",
1634+
(DescriptiveDeclKind, ValueDecl *))
1635+
NOTE(objc_implementation_one_matched_requirement,none,
1636+
"%0 %1 (with selector %2) is a potential match%select{|; insert "
1637+
"'@objc(%4)' to use it}3",
1638+
(DescriptiveDeclKind, ValueDecl *, ObjCSelector, bool, StringRef))
1639+
15981640
ERROR(cdecl_not_at_top_level,none,
15991641
"@_cdecl can only be applied to global functions", ())
16001642
ERROR(cdecl_empty_name,none,

include/swift/AST/TypeCheckRequests.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4065,6 +4065,28 @@ class LocalDiscriminatorsRequest
40654065
bool isCached() const { return true; }
40664066
};
40674067

4068+
/// Checks that all of a class's \c \@objcImplementation extensions provide
4069+
/// complete and correct implementations for their corresponding interfaces.
4070+
/// This is done on all of a class's implementations at once to improve diagnostics.
4071+
class TypeCheckObjCImplementationRequest
4072+
: public SimpleRequest<TypeCheckObjCImplementationRequest,
4073+
evaluator::SideEffect(ExtensionDecl *),
4074+
RequestFlags::Cached> {
4075+
public:
4076+
using SimpleRequest::SimpleRequest;
4077+
4078+
private:
4079+
friend SimpleRequest;
4080+
4081+
// Evaluation.
4082+
evaluator::SideEffect
4083+
evaluate(Evaluator &evaluator, ExtensionDecl *ED) const;
4084+
4085+
public:
4086+
// Separate caching.
4087+
bool isCached() const { return true; }
4088+
};
4089+
40684090
void simple_display(llvm::raw_ostream &out, ASTNode node);
40694091
void simple_display(llvm::raw_ostream &out, Type value);
40704092
void simple_display(llvm::raw_ostream &out, const TypeRepr *TyR);

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,3 +460,6 @@ SWIFT_REQUEST(TypeChecker, GetRuntimeDiscoverableAttributes,
460460
SWIFT_REQUEST(TypeChecker, LocalDiscriminatorsRequest,
461461
unsigned(DeclContext *),
462462
Cached, NoLocationInfo)
463+
SWIFT_REQUEST(TypeChecker, TypeCheckObjCImplementationRequest,
464+
unsigned(ExtensionDecl *),
465+
Cached, NoLocationInfo)

lib/AST/Decl.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5269,8 +5269,10 @@ synthesizeEmptyFunctionBody(AbstractFunctionDecl *afd, void *context) {
52695269

52705270
DestructorDecl *
52715271
GetDestructorRequest::evaluate(Evaluator &evaluator, ClassDecl *CD) const {
5272+
auto dc = CD->getImplementationContext();
5273+
52725274
auto &ctx = CD->getASTContext();
5273-
auto *DD = new (ctx) DestructorDecl(CD->getLoc(), CD);
5275+
auto *DD = new (ctx) DestructorDecl(CD->getLoc(), dc->getAsGenericContext());
52745276

52755277
DD->setImplicit();
52765278

lib/AST/NameLookupRequests.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ Optional<DestructorDecl *> GetDestructorRequest::getCachedResult() const {
219219

220220
void GetDestructorRequest::cacheResult(DestructorDecl *value) const {
221221
auto *classDecl = std::get<0>(getStorage());
222-
classDecl->addMember(value);
222+
classDecl->getImplementationContext()->addMember(value);
223223
}
224224

225225
//----------------------------------------------------------------------------//

lib/ClangImporter/ClangImporter.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5136,20 +5136,20 @@ findImplsGivenInterface(ClassDecl *classDecl, Identifier categoryName) {
51365136
return impls;
51375137
}
51385138

5139-
static Identifier getCategoryName(ExtensionDecl *ext) {
5139+
Identifier ExtensionDecl::getObjCCategoryName() const {
51405140
// Could it be an imported category?
5141-
if (!ext || !ext->hasClangNode())
5141+
if (!hasClangNode())
51425142
// Nope, not imported.
51435143
return Identifier();
51445144

5145-
auto category = dyn_cast<clang::ObjCCategoryDecl>(ext->getClangDecl());
5145+
auto category = dyn_cast<clang::ObjCCategoryDecl>(getClangDecl());
51465146
if (!category)
51475147
// Nope, not a category.
51485148
return Identifier();
51495149

51505150
// We'll look for an implementation with this category name.
51515151
auto clangCategoryName = category->getName();
5152-
return ext->getASTContext().getIdentifier(clangCategoryName);
5152+
return getASTContext().getIdentifier(clangCategoryName);
51535153
}
51545154

51555155
static IterableDeclContext *
@@ -5199,7 +5199,7 @@ findContextInterfaceAndImplementation(DeclContext *dc) {
51995199

52005200
// Is this an imported category? If so, extract its name so we can look for
52015201
// implementations of that category.
5202-
categoryName = getCategoryName(ext);
5202+
categoryName = ext->getObjCCategoryName();
52035203
if (categoryName.empty())
52045204
return {};
52055205

lib/Parse/ParseDecl.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9047,13 +9047,14 @@ parseDeclDeinit(ParseDeclOptions Flags, DeclAttributes &Attributes) {
90479047

90489048
DD->getAttrs() = Attributes;
90499049

9050-
// Reject 'destructor' functions outside of structs, enums, and classes.
9050+
// Reject 'destructor' functions outside of structs, enums, classes, and
9051+
// @objcImplementation extensions.
90519052
//
90529053
// Later in the type checker, we validate that structs/enums only do this if
9053-
// they are move only.
9054-
auto *nom = dyn_cast<NominalTypeDecl>(CurDeclContext);
9055-
if (!nom ||
9056-
(!isa<StructDecl>(nom) && !isa<EnumDecl>(nom) && !isa<ClassDecl>(nom))) {
9054+
// they are move only, and that @objcImplementations are main-body.
9055+
auto *ED = dyn_cast<ExtensionDecl>(CurDeclContext);
9056+
if (!(isa<StructDecl>(CurDeclContext) || isa<EnumDecl>(CurDeclContext) ||
9057+
isa<ClassDecl>(CurDeclContext) || (ED && ED->isObjCImplementation()))) {
90579058
diagnose(DestructorLoc, diag::destructor_decl_outside_class);
90589059

90599060
// Tell the type checker not to touch this destructor.

0 commit comments

Comments
 (0)