Commit db226cdf authored by Alex Lorenz's avatar Alex Lorenz
Browse files

[objc] diagnose protocol conformance in categories with direct members

in their corresponding class interfaces

Categories that add protocol conformances to classes with direct members should prohibit protocol
conformances when the methods/properties that the protocol expects are actually declared as 'direct' in the class.

Differential Revision: https://reviews.llvm.org/D92602
parent eddd1d19
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -1062,6 +1062,10 @@ def warn_objc_direct_property_ignored : Warning<
  InGroup<IgnoredAttributes>;
def err_objc_direct_dynamic_property : Error<
  "direct property cannot be @dynamic">;
def err_objc_direct_protocol_conformance : Error<
  "%select{category %1|class extension}0 cannot conform to protocol %2 because "
  "of direct members declared in interface %3">;
def note_direct_member_here : Note<"direct member declared here">;
def warn_conflicting_overriding_ret_types : Warning<
  "conflicting return type in "
+51 −0
Original line number Diff line number Diff line
@@ -3912,6 +3912,55 @@ static void DiagnoseVariableSizedIvars(Sema &S, ObjCContainerDecl *OCD) {
  }
}

static void DiagnoseCategoryDirectMembersProtocolConformance(
    Sema &S, ObjCProtocolDecl *PDecl, ObjCCategoryDecl *CDecl);

static void DiagnoseCategoryDirectMembersProtocolConformance(
    Sema &S, ObjCCategoryDecl *CDecl,
    const llvm::iterator_range<ObjCProtocolList::iterator> &Protocols) {
  for (auto *PI : Protocols)
    DiagnoseCategoryDirectMembersProtocolConformance(S, PI, CDecl);
}

static void DiagnoseCategoryDirectMembersProtocolConformance(
    Sema &S, ObjCProtocolDecl *PDecl, ObjCCategoryDecl *CDecl) {
  if (!PDecl->isThisDeclarationADefinition() && PDecl->getDefinition())
    PDecl = PDecl->getDefinition();

  llvm::SmallVector<const Decl *, 4> DirectMembers;
  const auto *IDecl = CDecl->getClassInterface();
  for (auto *MD : PDecl->methods()) {
    if (!MD->isPropertyAccessor()) {
      if (const auto *CMD =
              IDecl->getMethod(MD->getSelector(), MD->isInstanceMethod())) {
        if (CMD->isDirectMethod())
          DirectMembers.push_back(CMD);
      }
    }
  }
  for (auto *PD : PDecl->properties()) {
    if (const auto *CPD = IDecl->FindPropertyVisibleInPrimaryClass(
            PD->getIdentifier(),
            PD->isClassProperty()
                ? ObjCPropertyQueryKind::OBJC_PR_query_class
                : ObjCPropertyQueryKind::OBJC_PR_query_instance)) {
      if (CPD->isDirectProperty())
        DirectMembers.push_back(CPD);
    }
  }
  if (!DirectMembers.empty()) {
    S.Diag(CDecl->getLocation(), diag::err_objc_direct_protocol_conformance)
        << CDecl->IsClassExtension() << CDecl << PDecl << IDecl;
    for (const auto *MD : DirectMembers)
      S.Diag(MD->getLocation(), diag::note_direct_member_here);
    return;
  }

  // Check on this protocols's referenced protocols, recursively.
  DiagnoseCategoryDirectMembersProtocolConformance(S, CDecl,
                                                   PDecl->protocols());
}

// Note: For class/category implementations, allMethods is always null.
Decl *Sema::ActOnAtEnd(Scope *S, SourceRange AtEnd, ArrayRef<Decl *> allMethods,
                       ArrayRef<DeclGroupPtrTy> allTUVars) {
@@ -4012,6 +4061,8 @@ Decl *Sema::ActOnAtEnd(Scope *S, SourceRange AtEnd, ArrayRef<Decl *> allMethods,
      ObjCInterfaceDecl *CCPrimary = C->getClassInterface();
      DiagnoseClassExtensionDupMethods(C, CCPrimary);
    }

    DiagnoseCategoryDirectMembersProtocolConformance(*this, C, C->protocols());
  }
  if (ObjCContainerDecl *CDecl = dyn_cast<ObjCContainerDecl>(ClassDecl)) {
    if (CDecl->getIdentifier())
+98 −0
Original line number Diff line number Diff line
// RUN: %clang_cc1 -fsyntax-only -verify %s

__attribute__((objc_root_class))
@interface RootClass

- (void)baseMethod;

@end

__attribute__((objc_direct_members))
@interface I : RootClass

- (void)direct; // expected-note {{direct member declared here}}

@end

@protocol P
- (void)direct;
@end

@interface I (Cat1) <P> // expected-error {{category 'Cat1' cannot conform to protocol 'P' because of direct members declared in interface 'I'}}
@end

@protocol BaseP
- (void)baseMethod;
@end

@interface I (CatBase) <BaseP> // OK
@end

@protocol P2
- (void)indirect;
@end

@interface I (Cat2) <P2> // OK
- (void)indirect;
@end

@protocol P3
- (void)indirect3;
@end

@interface I (Cat3) <P3> // OK
@end

@interface ExpDirect : RootClass

- (void)direct __attribute__((objc_direct)); // expected-note {{direct member declared here}}

- (void)directRecursive __attribute__((objc_direct)); // expected-note {{direct member declared here}}

@end

@interface ExpDirect (CatExpDirect) <P> // expected-error {{category 'CatExpDirect' cannot conform to protocol 'P' because of direct members declared in interface 'ExpDirect'}}
@end

@protocol PRecursive1
- (void)directRecursive;
@end

@protocol PRecursiveTop <PRecursive1>
@end

@interface ExpDirect () <PRecursiveTop> // expected-error {{class extension cannot conform to protocol 'PRecursive1' because of direct members declared in interface 'ExpDirect'}}
@end


@protocol PProp

@property (nonatomic, readonly) I *name;

@end

__attribute__((objc_direct_members))
@interface IProp1 : RootClass

@property (nonatomic, readonly) I *name; // expected-note {{direct member declared here}}

@end

@interface IProp1 () <PProp> // expected-error {{class extension cannot conform to protocol 'PProp' because of direct members declared in interface 'IProp1'}}
@end


@protocol PProp2

@property (nonatomic, readonly, class) I *name;

@end

@interface IProp2 : RootClass

@property (nonatomic, readonly, class, direct) I *name; // expected-note {{direct member declared here}}

@end

@interface IProp2 () <PProp2> // expected-error {{class extension cannot conform to protocol 'PProp2' because of direct members declared in interface 'IProp2'}}
@end