diff options
Diffstat (limited to 'clang/lib/Sema/SemaDeclObjC.cpp')
-rw-r--r-- | clang/lib/Sema/SemaDeclObjC.cpp | 182 |
1 files changed, 178 insertions, 4 deletions
diff --git a/clang/lib/Sema/SemaDeclObjC.cpp b/clang/lib/Sema/SemaDeclObjC.cpp index db594bbd21dd..5fdf6aeed5b4 100644 --- a/clang/lib/Sema/SemaDeclObjC.cpp +++ b/clang/lib/Sema/SemaDeclObjC.cpp @@ -2828,6 +2828,9 @@ void Sema::MatchAllMethodDeclarations(const SelectorSet &InsMap, "Expected to find the method through lookup as well"); // ImpMethodDecl may be null as in a @dynamic property. if (ImpMethodDecl) { + // Skip property accessor function stubs. + if (ImpMethodDecl->isSynthesizedAccessorStub()) + continue; if (!WarnCategoryMethodImpl) WarnConflictingTypedMethods(ImpMethodDecl, I, isa<ObjCProtocolDecl>(CDecl)); @@ -2854,6 +2857,9 @@ void Sema::MatchAllMethodDeclarations(const SelectorSet &InsMap, "Expected to find the method through lookup as well"); // ImpMethodDecl may be null as in a @dynamic property. if (ImpMethodDecl) { + // Skip property accessor function stubs. + if (ImpMethodDecl->isSynthesizedAccessorStub()) + continue; if (!WarnCategoryMethodImpl) WarnConflictingTypedMethods(ImpMethodDecl, I, isa<ObjCProtocolDecl>(CDecl)); @@ -3233,6 +3239,9 @@ bool Sema::MatchTwoMethodDeclarations(const ObjCMethodDecl *left, if (left->isHidden() || right->isHidden()) return false; + if (left->isDirectMethod() != right->isDirectMethod()) + return false; + if (getLangOpts().ObjCAutoRefCount && (left->hasAttr<NSReturnsRetainedAttr>() != right->hasAttr<NSReturnsRetainedAttr>() || @@ -3424,6 +3433,9 @@ static bool isAcceptableMethodMismatch(ObjCMethodDecl *chosen, if (!chosen->isInstanceMethod()) return false; + if (chosen->isDirectMethod() != other->isDirectMethod()) + return false; + Selector sel = chosen->getSelector(); if (!sel.isUnarySelector() || sel.getNameForSlot(0) != "length") return false; @@ -3903,6 +3915,25 @@ Decl *Sema::ActOnAtEnd(Scope *S, SourceRange AtEnd, ArrayRef<Decl *> allMethods, || isa<ObjCProtocolDecl>(ClassDecl); bool checkIdenticalMethods = isa<ObjCImplementationDecl>(ClassDecl); + // Make synthesized accessor stub functions visible. + // ActOnPropertyImplDecl() creates them as not visible in case + // they are overridden by an explicit method that is encountered + // later. + if (auto *OID = dyn_cast<ObjCImplementationDecl>(CurContext)) { + for (auto PropImpl : OID->property_impls()) { + if (auto *Getter = PropImpl->getGetterMethodDecl()) + if (Getter->isSynthesizedAccessorStub()) { + OID->makeDeclVisibleInContext(Getter); + OID->addDecl(Getter); + } + if (auto *Setter = PropImpl->getSetterMethodDecl()) + if (Setter->isSynthesizedAccessorStub()) { + OID->makeDeclVisibleInContext(Setter); + OID->addDecl(Setter); + } + } + } + // FIXME: Remove these and use the ObjCContainerDecl/DeclContext. llvm::DenseMap<Selector, const ObjCMethodDecl*> InsMap; llvm::DenseMap<Selector, const ObjCMethodDecl*> ClsMap; @@ -4001,8 +4032,8 @@ Decl *Sema::ActOnAtEnd(Scope *S, SourceRange AtEnd, ArrayRef<Decl *> allMethods, continue; for (const auto *Ext : IDecl->visible_extensions()) { - if (ObjCMethodDecl *GetterMethod - = Ext->getInstanceMethod(Property->getGetterName())) + if (ObjCMethodDecl *GetterMethod = + Ext->getInstanceMethod(Property->getGetterName())) GetterMethod->setPropertyAccessor(true); if (!Property->isReadOnly()) if (ObjCMethodDecl *SetterMethod @@ -4314,6 +4345,18 @@ private: }; } // end anonymous namespace +void Sema::CheckObjCMethodDirectOverrides(ObjCMethodDecl *method, + ObjCMethodDecl *overridden) { + if (const auto *attr = overridden->getAttr<ObjCDirectAttr>()) { + Diag(method->getLocation(), diag::err_objc_override_direct_method); + Diag(attr->getLocation(), diag::note_previous_declaration); + } else if (const auto *attr = method->getAttr<ObjCDirectAttr>()) { + Diag(attr->getLocation(), diag::err_objc_direct_on_override) + << isa<ObjCProtocolDecl>(overridden->getDeclContext()); + Diag(overridden->getLocation(), diag::note_previous_declaration); + } +} + void Sema::CheckObjCMethodOverrides(ObjCMethodDecl *ObjCMethod, ObjCInterfaceDecl *CurrentClass, ResultTypeCompatibilityKind RTC) { @@ -4332,8 +4375,8 @@ void Sema::CheckObjCMethodOverrides(ObjCMethodDecl *ObjCMethod, if (isa<ObjCProtocolDecl>(overridden->getDeclContext()) || CurrentClass != overridden->getClassInterface() || overridden->isOverriding()) { + CheckObjCMethodDirectOverrides(ObjCMethod, overridden); hasOverriddenMethodsInBaseOrProtocol = true; - } else if (isa<ObjCImplDecl>(ObjCMethod->getDeclContext())) { // OverrideSearch will return as "overridden" the same method in the // interface. For hasOverriddenMethodsInBaseOrProtocol, we need to @@ -4357,6 +4400,7 @@ void Sema::CheckObjCMethodOverrides(ObjCMethodDecl *ObjCMethod, for (ObjCMethodDecl *SuperOverridden : overrides) { if (isa<ObjCProtocolDecl>(SuperOverridden->getDeclContext()) || CurrentClass != SuperOverridden->getClassInterface()) { + CheckObjCMethodDirectOverrides(ObjCMethod, SuperOverridden); hasOverriddenMethodsInBaseOrProtocol = true; overridden->setOverriding(true); break; @@ -4551,6 +4595,7 @@ Decl *Sema::ActOnMethodDeclaration( Diag(MethodLoc, diag::err_missing_method_context); return nullptr; } + Decl *ClassDecl = cast<ObjCContainerDecl>(CurContext); QualType resultDeclType; @@ -4574,7 +4619,7 @@ Decl *Sema::ActOnMethodDeclaration( ObjCMethodDecl *ObjCMethod = ObjCMethodDecl::Create( Context, MethodLoc, EndLoc, Sel, resultDeclType, ReturnTInfo, CurContext, MethodType == tok::minus, isVariadic, - /*isPropertyAccessor=*/false, + /*isPropertyAccessor=*/false, /*isSynthesizedAccessorStub=*/false, /*isImplicitlyDeclared=*/false, /*isDefined=*/false, MethodDeclKind == tok::objc_optional ? ObjCMethodDecl::Optional : ObjCMethodDecl::Required, @@ -4666,6 +4711,41 @@ Decl *Sema::ActOnMethodDeclaration( ImpDecl->addClassMethod(ObjCMethod); } + // If this method overrides a previous @synthesize declaration, + // register it with the property. Linear search through all + // properties here, because the autosynthesized stub hasn't been + // made visible yet, so it can be overriden by a later + // user-specified implementation. + for (ObjCPropertyImplDecl *PropertyImpl : ImpDecl->property_impls()) { + if (auto *Setter = PropertyImpl->getSetterMethodDecl()) + if (Setter->getSelector() == Sel && + Setter->isInstanceMethod() == ObjCMethod->isInstanceMethod()) { + assert(Setter->isSynthesizedAccessorStub() && "autosynth stub expected"); + PropertyImpl->setSetterMethodDecl(ObjCMethod); + } + if (auto *Getter = PropertyImpl->getGetterMethodDecl()) + if (Getter->getSelector() == Sel && + Getter->isInstanceMethod() == ObjCMethod->isInstanceMethod()) { + assert(Getter->isSynthesizedAccessorStub() && "autosynth stub expected"); + PropertyImpl->setGetterMethodDecl(ObjCMethod); + break; + } + } + + // A method is either tagged direct explicitly, or inherits it from its + // canonical declaration. + // + // We have to do the merge upfront and not in mergeInterfaceMethodToImpl() + // because IDecl->lookupMethod() returns more possible matches than just + // the canonical declaration. + if (!ObjCMethod->isDirectMethod()) { + const ObjCMethodDecl *CanonicalMD = ObjCMethod->getCanonicalDecl(); + if (const auto *attr = CanonicalMD->getAttr<ObjCDirectAttr>()) { + ObjCMethod->addAttr( + ObjCDirectAttr::CreateImplicit(Context, attr->getLocation())); + } + } + // Merge information from the @interface declaration into the // @implementation. if (ObjCInterfaceDecl *IDecl = ImpDecl->getClassInterface()) { @@ -4673,12 +4753,64 @@ Decl *Sema::ActOnMethodDeclaration( ObjCMethod->isInstanceMethod())) { mergeInterfaceMethodToImpl(*this, ObjCMethod, IMD); + // The Idecl->lookupMethod() above will find declarations for ObjCMethod + // in one of these places: + // + // (1) the canonical declaration in an @interface container paired + // with the ImplDecl, + // (2) non canonical declarations in @interface not paired with the + // ImplDecl for the same Class, + // (3) any superclass container. + // + // Direct methods only allow for canonical declarations in the matching + // container (case 1). + // + // Direct methods overriding a superclass declaration (case 3) is + // handled during overrides checks in CheckObjCMethodOverrides(). + // + // We deal with same-class container mismatches (Case 2) here. + if (IDecl == IMD->getClassInterface()) { + auto diagContainerMismatch = [&] { + int decl = 0, impl = 0; + + if (auto *Cat = dyn_cast<ObjCCategoryDecl>(IMD->getDeclContext())) + decl = Cat->IsClassExtension() ? 1 : 2; + + if (isa<ObjCCategoryImplDecl>(ImpDecl)) + impl = 1 + (decl != 0); + + Diag(ObjCMethod->getLocation(), + diag::err_objc_direct_impl_decl_mismatch) + << decl << impl; + Diag(IMD->getLocation(), diag::note_previous_declaration); + }; + + if (const auto *attr = ObjCMethod->getAttr<ObjCDirectAttr>()) { + if (ObjCMethod->getCanonicalDecl() != IMD) { + diagContainerMismatch(); + } else if (!IMD->isDirectMethod()) { + Diag(attr->getLocation(), diag::err_objc_direct_missing_on_decl); + Diag(IMD->getLocation(), diag::note_previous_declaration); + } + } else if (const auto *attr = IMD->getAttr<ObjCDirectAttr>()) { + if (ObjCMethod->getCanonicalDecl() != IMD) { + diagContainerMismatch(); + } else { + ObjCMethod->addAttr( + ObjCDirectAttr::CreateImplicit(Context, attr->getLocation())); + } + } + } + // Warn about defining -dealloc in a category. if (isa<ObjCCategoryImplDecl>(ImpDecl) && IMD->isOverriding() && ObjCMethod->getSelector().getMethodFamily() == OMF_dealloc) { Diag(ObjCMethod->getLocation(), diag::warn_dealloc_in_category) << ObjCMethod->getDeclName(); } + } else if (ImpDecl->hasAttr<ObjCDirectMembersAttr>()) { + ObjCMethod->addAttr( + ObjCDirectAttr::CreateImplicit(Context, ObjCMethod->getLocation())); } // Warn if a method declared in a protocol to which a category or @@ -4698,6 +4830,42 @@ Decl *Sema::ActOnMethodDeclaration( } } } else { + if (!isa<ObjCProtocolDecl>(ClassDecl)) { + if (!ObjCMethod->isDirectMethod() && + ClassDecl->hasAttr<ObjCDirectMembersAttr>()) { + ObjCMethod->addAttr( + ObjCDirectAttr::CreateImplicit(Context, ObjCMethod->getLocation())); + } + + // There can be a single declaration in any @interface container + // for a given direct method, look for clashes as we add them. + // + // For valid code, we should always know the primary interface + // declaration by now, however for invalid code we'll keep parsing + // but we won't find the primary interface and IDecl will be nil. + ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(ClassDecl); + if (!IDecl) + IDecl = cast<ObjCCategoryDecl>(ClassDecl)->getClassInterface(); + + if (IDecl) + if (auto *IMD = IDecl->lookupMethod(ObjCMethod->getSelector(), + ObjCMethod->isInstanceMethod(), + /*shallowCategoryLookup=*/false, + /*followSuper=*/false)) { + if (isa<ObjCProtocolDecl>(IMD->getDeclContext())) { + // Do not emit a diagnostic for the Protocol case: + // diag::err_objc_direct_on_protocol has already been emitted + // during parsing for these with a nicer diagnostic. + } else if (ObjCMethod->isDirectMethod() || IMD->isDirectMethod()) { + Diag(ObjCMethod->getLocation(), + diag::err_objc_direct_duplicate_decl) + << ObjCMethod->isDirectMethod() << IMD->isDirectMethod() + << ObjCMethod->getDeclName(); + Diag(IMD->getLocation(), diag::note_previous_declaration); + } + } + } + cast<DeclContext>(ClassDecl)->addDecl(ObjCMethod); } @@ -4783,6 +4951,9 @@ Decl *Sema::ActOnMethodDeclaration( } } + // Insert the invisible arguments, self and _cmd! + ObjCMethod->createImplicitParams(Context, ObjCMethod->getClassInterface()); + ActOnDocumentableDecl(ObjCMethod); return ObjCMethod; @@ -5063,6 +5234,9 @@ void Sema::DiagnoseUnusedBackingIvarInAccessor(Scope *S, if (!IV) continue; + if (CurMethod->isSynthesizedAccessorStub()) + continue; + UnusedBackingIvarChecker Checker(*this, CurMethod, IV); Checker.TraverseStmt(CurMethod->getBody()); if (Checker.AccessedIvar) |