/// Try to capture an implicit reference to 'self'. ObjCMethodDecl *Sema::tryCaptureObjCSelf() { // Ignore block scopes: we can capture through them. DeclContext *DC = CurContext; while (true) { if (isa<BlockDecl>(DC)) DC = cast<BlockDecl>(DC)->getDeclContext(); else if (isa<EnumDecl>(DC)) DC = cast<EnumDecl>(DC)->getDeclContext(); else break; } // If we're not in an ObjC method, error out. Note that, unlike the // C++ case, we don't require an instance method --- class methods // still have a 'self', and we really do still need to capture it! ObjCMethodDecl *method = dyn_cast<ObjCMethodDecl>(DC); if (!method) return 0; ImplicitParamDecl *self = method->getSelfDecl(); assert(self && "capturing 'self' in non-definition?"); // Mark that we're closing on 'this' in all the block scopes, if applicable. for (unsigned idx = FunctionScopes.size() - 1; isa<BlockScopeInfo>(FunctionScopes[idx]); --idx) { BlockScopeInfo *blockScope = cast<BlockScopeInfo>(FunctionScopes[idx]); unsigned &captureIndex = blockScope->CaptureMap[self]; if (captureIndex) break; bool nested = isa<BlockScopeInfo>(FunctionScopes[idx-1]); blockScope->Captures.push_back( BlockDecl::Capture(self, /*byref*/ false, nested, /*copy*/ 0)); captureIndex = blockScope->Captures.size(); // +1 } return method; }
void cxcursor::getOverriddenCursors(CXCursor cursor, SmallVectorImpl<CXCursor> &overridden) { if (!clang_isDeclaration(cursor.kind)) return; Decl *D = getCursorDecl(cursor); if (!D) return; // Handle C++ member functions. CXTranslationUnit TU = getCursorTU(cursor); if (CXXMethodDecl *CXXMethod = dyn_cast<CXXMethodDecl>(D)) { for (CXXMethodDecl::method_iterator M = CXXMethod->begin_overridden_methods(), MEnd = CXXMethod->end_overridden_methods(); M != MEnd; ++M) overridden.push_back(MakeCXCursor(const_cast<CXXMethodDecl*>(*M), TU)); return; } ObjCMethodDecl *Method = dyn_cast<ObjCMethodDecl>(D); if (!Method) return; // Handle Objective-C methods. CollectOverriddenMethods(TU, Method->getDeclContext(), Method, overridden); }
void ObjCMigrateASTConsumer::migrateObjCInterfaceDecl(ASTContext &Ctx, ObjCInterfaceDecl *D) { for (ObjCContainerDecl::method_iterator M = D->meth_begin(), MEnd = D->meth_end(); M != MEnd; ++M) { ObjCMethodDecl *Method = (*M); if (Method->isPropertyAccessor() || Method->param_size() != 0) continue; // Is this method candidate to be a getter? QualType GRT = Method->getResultType(); if (GRT->isVoidType()) continue; Selector GetterSelector = Method->getSelector(); IdentifierInfo *getterName = GetterSelector.getIdentifierInfoForSlot(0); Selector SetterSelector = SelectorTable::constructSetterSelector(PP.getIdentifierTable(), PP.getSelectorTable(), getterName); if (ObjCMethodDecl *SetterMethod = D->lookupMethod(SetterSelector, true)) { // Is this a valid setter, matching the target getter? QualType SRT = SetterMethod->getResultType(); if (!SRT->isVoidType()) continue; const ParmVarDecl *argDecl = *SetterMethod->param_begin(); QualType ArgType = argDecl->getType(); if (!Ctx.hasSameType(ArgType, GRT)) continue; edit::Commit commit(*Editor); edit::rewriteToObjCProperty(Method, SetterMethod, *NSAPIObj, commit); Editor->commit(commit); } } }
TEST(InitializerCheck, find_nonnull_ivars) { ASTBuilder builder("@interface Test : NSObject\n" "@property (nonatomic, nonnull) NSString *hello;\n" "@property (nonatomic, nullable) NSNumber *good;\n" "@end\n" "@interface Test()\n" "@property (nonatomic, nonnull) NSString *extension;\n" "@property (nonatomic, nullable) NSNumber *extension2;\n" "@end\n" "@interface Test (Cat)\n" "@property (nonatomic, nonnull) NSString *category;\n" "@property (nonatomic, nullable) NSNumber *category2;\n" "@end\n" "@implementation Test {\n" " NSString * _Nonnull _impl1;\n" " NSString * _Nullable _impl2;\n" "}\n" "- (nonnull instancetype)init1 __attribute__((annotate(\"hoge\"))) {\n" " return self;\n" "}\n" "@end\n"); std::shared_ptr<VariableNullabilityMapping> map(new VariableNullabilityMapping); std::shared_ptr<VariableNullabilityEnvironment> env(new VariableNullabilityEnvironment(builder.getASTContext(), map)); ExpressionNullabilityCalculator calculator(builder.getASTContext(), env); VariableNullabilityPropagation prop(calculator, env); ObjCMethodDecl *method = builder.getMethodDecl("init1"); for (auto attr : method->attrs()) { auto kind = attr->getKind(); if (kind == clang::attr::Annotate) { auto annot = llvm::dyn_cast<AnnotateAttr>(attr); std::string name = annot->getAnnotation(); ASSERT_EQ(name, "hoge"); } } ObjCImplementationDecl *impl = builder.getImplementationDecl("Test"); InitializerChecker checker(builder.getASTContext(), impl); auto ivars = checker.getNonnullIvars(); std::set<std::string> actualIvarNames; for (auto ivar : ivars) { actualIvarNames.insert(ivar->getIvarDecl()->getNameAsString()); } std::set<std::string> expectedIvarNames; expectedIvarNames.insert("_hello"); expectedIvarNames.insert("_extension"); expectedIvarNames.insert("_impl1"); ASSERT_EQ(expectedIvarNames, actualIvarNames); }
bool ObjCMethodCall::canBeOverridenInSubclass(ObjCInterfaceDecl *IDecl, Selector Sel) const { assert(IDecl); const SourceManager &SM = getState()->getStateManager().getContext().getSourceManager(); // If the class interface is declared inside the main file, assume it is not // subcassed. // TODO: It could actually be subclassed if the subclass is private as well. // This is probably very rare. SourceLocation InterfLoc = IDecl->getEndOfDefinitionLoc(); if (InterfLoc.isValid() && SM.isFromMainFile(InterfLoc)) return false; // Assume that property accessors are not overridden. if (getMessageKind() == OCM_PropertyAccess) return false; // We assume that if the method is public (declared outside of main file) or // has a parent which publicly declares the method, the method could be // overridden in a subclass. // Find the first declaration in the class hierarchy that declares // the selector. ObjCMethodDecl *D = 0; while (true) { D = IDecl->lookupMethod(Sel, true); // Cannot find a public definition. if (!D) return false; // If outside the main file, if (D->getLocation().isValid() && !SM.isFromMainFile(D->getLocation())) return true; if (D->isOverriding()) { // Search in the superclass on the next iteration. IDecl = D->getClassInterface(); if (!IDecl) return false; IDecl = IDecl->getSuperClass(); if (!IDecl) return false; continue; } return false; }; llvm_unreachable("The while loop should always terminate."); }
void StmtDumper::VisitObjCImplicitSetterGetterRefExpr( ObjCImplicitSetterGetterRefExpr *Node) { DumpExpr(Node); ObjCMethodDecl *Getter = Node->getGetterMethod(); ObjCMethodDecl *Setter = Node->getSetterMethod(); OS << " Kind=MethodRef Getter=\"" << Getter->getSelector().getAsString() << "\" Setter=\""; if (Setter) OS << Setter->getSelector().getAsString(); else OS << "(null)"; OS << "\""; }
void ento::CheckObjCInstMethSignature(const ObjCImplementationDecl* ID, BugReporter& BR) { const ObjCInterfaceDecl* D = ID->getClassInterface(); const ObjCInterfaceDecl* C = D->getSuperClass(); if (!C) return; ASTContext& Ctx = BR.getContext(); // Build a DenseMap of the methods for quick querying. typedef llvm::DenseMap<Selector,ObjCMethodDecl*> MapTy; MapTy IMeths; unsigned NumMethods = 0; for (ObjCImplementationDecl::instmeth_iterator I=ID->instmeth_begin(), E=ID->instmeth_end(); I!=E; ++I) { ObjCMethodDecl* M = *I; IMeths[M->getSelector()] = M; ++NumMethods; } // Now recurse the class hierarchy chain looking for methods with the // same signatures. while (C && NumMethods) { for (ObjCInterfaceDecl::instmeth_iterator I=C->instmeth_begin(), E=C->instmeth_end(); I!=E; ++I) { ObjCMethodDecl* M = *I; Selector S = M->getSelector(); MapTy::iterator MI = IMeths.find(S); if (MI == IMeths.end() || MI->second == 0) continue; --NumMethods; ObjCMethodDecl* MethDerived = MI->second; MI->second = 0; CompareReturnTypes(MethDerived, M, BR, Ctx, ID); } C = C->getSuperClass(); } }
ExprResult Sema::ActOnSuperMessage(Scope *S, SourceLocation SuperLoc, Selector Sel, SourceLocation LBracLoc, SourceLocation SelectorLoc, SourceLocation RBracLoc, MultiExprArg Args) { // Determine whether we are inside a method or not. ObjCMethodDecl *Method = tryCaptureObjCSelf(); if (!Method) { Diag(SuperLoc, diag::err_invalid_receiver_to_message_super); return ExprError(); } ObjCInterfaceDecl *Class = Method->getClassInterface(); if (!Class) { Diag(SuperLoc, diag::error_no_super_class_message) << Method->getDeclName(); return ExprError(); } ObjCInterfaceDecl *Super = Class->getSuperClass(); if (!Super) { // The current class does not have a superclass. Diag(SuperLoc, diag::error_root_class_cannot_use_super) << Class->getIdentifier(); return ExprError(); } // We are in a method whose class has a superclass, so 'super' // is acting as a keyword. if (Method->isInstanceMethod()) { // Since we are in an instance method, this is an instance // message to the superclass instance. QualType SuperTy = Context.getObjCInterfaceType(Super); SuperTy = Context.getObjCObjectPointerType(SuperTy); return BuildInstanceMessage(0, SuperTy, SuperLoc, Sel, /*Method=*/0, LBracLoc, SelectorLoc, RBracLoc, move(Args)); } // Since we are in a class method, this is a class message to // the superclass. return BuildClassMessage(/*ReceiverTypeInfo=*/0, Context.getObjCInterfaceType(Super), SuperLoc, Sel, /*Method=*/0, LBracLoc, SelectorLoc, RBracLoc, move(Args)); }
/// ObjCGetTypeForMethodDefinition - Builds the type for a method definition /// declarator QualType Sema::ObjCGetTypeForMethodDefinition(DeclPtrTy D) { ObjCMethodDecl *MDecl = cast<ObjCMethodDecl>(D.getAs<Decl>()); QualType T = MDecl->getResultType(); llvm::SmallVector<QualType, 16> ArgTys; // Add the first two invisible argument types for self and _cmd. if (MDecl->isInstanceMethod()) { QualType selfTy = Context.getObjCInterfaceType(MDecl->getClassInterface()); selfTy = Context.getPointerType(selfTy); ArgTys.push_back(selfTy); } else ArgTys.push_back(Context.getObjCIdType()); ArgTys.push_back(Context.getObjCSelType()); for (ObjCMethodDecl::param_iterator PI = MDecl->param_begin(), E = MDecl->param_end(); PI != E; ++PI) { QualType ArgTy = (*PI)->getType(); assert(!ArgTy.isNull() && "Couldn't parse type?"); ArgTy = adjustParameterType(ArgTy); ArgTys.push_back(ArgTy); } T = Context.getFunctionType(T, &ArgTys[0], ArgTys.size(), MDecl->isVariadic(), 0); return T; }
void NSErrorChecker::CheckSignature(const ObjCMethodDecl& M, QualType& ResultTy, llvm::SmallVectorImpl<VarDecl*>& ErrorParams) { ResultTy = M.getResultType(); for (ObjCMethodDecl::param_iterator I=M.param_begin(), E=M.param_end(); I!=E; ++I) { QualType T = (*I)->getType(); if (isNSErrorWarning) { if (CheckNSErrorArgument(T)) ErrorParams.push_back(*I); } else if (CheckCFErrorArgument(T)) ErrorParams.push_back(*I); } }
// Get the local instance/class method declared in this interface. ObjCMethodDecl * ObjCContainerDecl::getMethod(Selector Sel, bool isInstance) const { // Since instance & class methods can have the same name, the loop below // ensures we get the correct method. // // @interface Whatever // - (int) class_method; // + (float) class_method; // @end // lookup_const_iterator Meth, MethEnd; for (llvm::tie(Meth, MethEnd) = lookup(Sel); Meth != MethEnd; ++Meth) { ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(*Meth); if (MD && MD->isInstanceMethod() == isInstance) return MD; } return 0; }
void DeclPrinter::PrintObjCImplementationDecl(ObjCImplementationDecl *OID) { std::string I = OID->getName(); ObjCInterfaceDecl *SID = OID->getSuperClass(); if (SID) Out << "@implementation " << I << " : " << SID->getName(); else Out << "@implementation " << I; for (ObjCImplementationDecl::instmeth_iterator I = OID->instmeth_begin(), E = OID->instmeth_end(); I != E; ++I) { ObjCMethodDecl *OMD = *I; PrintObjCMethodDecl(OMD); if (OMD->getBody()) { Out << ' '; OMD->getBody()->printPretty(Out); Out << '\n'; } } for (ObjCImplementationDecl::classmeth_iterator I = OID->classmeth_begin(), E = OID->classmeth_end(); I != E; ++I) { ObjCMethodDecl *OMD = *I; PrintObjCMethodDecl(OMD); if (OMD->getBody()) { Out << ' '; OMD->getBody()->printPretty(Out); Out << '\n'; } } Out << "@end\n"; }
bool VisitObjCMessageExpr(ObjCMessageExpr *messageExpr) { ObjCMethodDecl *decl = messageExpr->getMethodDecl(); auto info = findIvarInfo(_NonnullIvars, decl); if (!info.expired()) { _NonnullIvars.erase(info.lock()); } if (isInitializerMethod(decl)) { auto receiver = messageExpr->getInstanceReceiver(); if (receiver) { auto varRef = llvm::dyn_cast<DeclRefExpr>(receiver->IgnoreParenImpCasts()); if (varRef) { if (varRef->getDecl()->getNameAsString() == "self" && decl->getClassInterface() == _MethodDecl->getClassInterface()) { _NonnullIvars.clear(); } } } } return true; }
static void removeDeallocMethod(MigrationPass &pass) { ASTContext &Ctx = pass.Ctx; TransformActions &TA = pass.TA; DeclContext *DC = Ctx.getTranslationUnitDecl(); typedef DeclContext::specific_decl_iterator<ObjCImplementationDecl> impl_iterator; for (impl_iterator I = impl_iterator(DC->decls_begin()), E = impl_iterator(DC->decls_end()); I != E; ++I) { for (ObjCImplementationDecl::instmeth_iterator MI = (*I)->instmeth_begin(), ME = (*I)->instmeth_end(); MI != ME; ++MI) { ObjCMethodDecl *MD = *MI; if (MD->getMethodFamily() == OMF_dealloc) { if (MD->hasBody() && isBodyEmpty(MD->getCompoundBody(), Ctx, pass.ARCMTMacroLocs)) { Transaction Trans(TA); TA.remove(MD->getSourceRange()); } break; } } } }
static void GCRewriteFinalize(MigrationPass &pass) { ASTContext &Ctx = pass.Ctx; TransformActions &TA = pass.TA; DeclContext *DC = Ctx.getTranslationUnitDecl(); Selector FinalizeSel = Ctx.Selectors.getNullarySelector(&pass.Ctx.Idents.get("finalize")); typedef DeclContext::specific_decl_iterator<ObjCImplementationDecl> impl_iterator; for (impl_iterator I = impl_iterator(DC->decls_begin()), E = impl_iterator(DC->decls_end()); I != E; ++I) { for (ObjCImplementationDecl::instmeth_iterator MI = I->instmeth_begin(), ME = I->instmeth_end(); MI != ME; ++MI) { ObjCMethodDecl *MD = *MI; if (!MD->hasBody()) continue; if (MD->isInstanceMethod() && MD->getSelector() == FinalizeSel) { ObjCMethodDecl *FinalizeM = MD; Transaction Trans(TA); TA.insert(FinalizeM->getSourceRange().getBegin(), "#if !__has_feature(objc_arc)\n"); CharSourceRange::getTokenRange(FinalizeM->getSourceRange()); const SourceManager &SM = pass.Ctx.getSourceManager(); const LangOptions &LangOpts = pass.Ctx.getLangOpts(); bool Invalid; std::string str = "\n#endif\n"; str += Lexer::getSourceText( CharSourceRange::getTokenRange(FinalizeM->getSourceRange()), SM, LangOpts, &Invalid); TA.insertAfterToken(FinalizeM->getSourceRange().getEnd(), str); break; } } } }
void DeclPrinter::PrintObjCImplementationDecl(ObjCImplementationDecl *OID) { std::string I = OID->getNameAsString(); ObjCInterfaceDecl *SID = OID->getSuperClass(); if (SID) Out << "@implementation " << I << " : " << SID->getNameAsString(); else Out << "@implementation " << I; // FIXME: Don't use a NULL context ASTContext *Context = 0; for (ObjCImplementationDecl::instmeth_iterator I = OID->instmeth_begin(*Context), E = OID->instmeth_end(*Context); I != E; ++I) { ObjCMethodDecl *OMD = *I; PrintObjCMethodDecl(OMD); if (OMD->getBody()) { Out << ' '; OMD->getBody()->printPretty(Out); Out << '\n'; } } for (ObjCImplementationDecl::classmeth_iterator I = OID->classmeth_begin(*Context), E = OID->classmeth_end(*Context); I != E; ++I) { ObjCMethodDecl *OMD = *I; PrintObjCMethodDecl(OMD); if (OMD->getBody()) { Out << ' '; OMD->getBody()->printPretty(Out); Out << '\n'; } } for (ObjCImplementationDecl::propimpl_iterator I = OID->propimpl_begin(*Context), E = OID->propimpl_end(*Context); I != E; ++I) PrintObjCPropertyImplDecl(*I); Out << "@end\n"; }
void ObjCSuperCallChecker::checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager &Mgr, BugReporter &BR) const { ASTContext &Ctx = BR.getContext(); if (!isUIViewControllerSubclass(Ctx, D)) return; const char *SelectorNames[] = {"addChildViewController", "viewDidAppear", "viewDidDisappear", "viewWillAppear", "viewWillDisappear", "removeFromParentViewController", "didReceiveMemoryWarning", "viewDidUnload", "viewWillUnload", "viewDidLoad"}; const unsigned SelectorArgumentCounts[] = {1, 1, 1, 1, 1, 0, 0, 0, 0, 0}; const size_t SelectorCount = llvm::array_lengthof(SelectorNames); assert(llvm::array_lengthof(SelectorArgumentCounts) == SelectorCount); // Fill the Selectors SmallSet with all selectors we want to check. llvm::SmallSet<Selector, 16> Selectors; for (size_t i = 0; i < SelectorCount; i++) { unsigned ArgumentCount = SelectorArgumentCounts[i]; const char *SelectorCString = SelectorNames[i]; // Get the selector. IdentifierInfo *II = &Ctx.Idents.get(SelectorCString); Selectors.insert(Ctx.Selectors.getSelector(ArgumentCount, &II)); } // Iterate over all instance methods. for (ObjCImplementationDecl::instmeth_iterator I = D->instmeth_begin(), E = D->instmeth_end(); I != E; ++I) { Selector S = (*I)->getSelector(); // Find out whether this is a selector that we want to check. if (!Selectors.count(S)) continue; ObjCMethodDecl *MD = *I; // Check if the method calls its superclass implementation. if (MD->getBody()) { FindSuperCallVisitor Visitor(S); Visitor.TraverseDecl(MD); // It doesn't call super, emit a diagnostic. if (!Visitor.DoesCallSuper) { PathDiagnosticLocation DLoc = PathDiagnosticLocation::createEnd(MD->getBody(), BR.getSourceManager(), Mgr.getAnalysisDeclContext(D)); const char *Name = "Missing call to superclass"; SmallString<256> Buf; llvm::raw_svector_ostream os(Buf); os << "The '" << S.getAsString() << "' instance method in UIViewController subclass '" << *D << "' is missing a [super " << S.getAsString() << "] call"; BR.EmitBasicReport(MD, Name, categories::CoreFoundationObjectiveC, os.str(), DLoc); } } } }
/// HandleExprPropertyRefExpr - Handle foo.bar where foo is a pointer to an /// objective C interface. This is a property reference expression. Action::OwningExprResult Sema:: HandleExprPropertyRefExpr(const ObjCObjectPointerType *OPT, Expr *BaseExpr, DeclarationName MemberName, SourceLocation MemberLoc) { const ObjCInterfaceType *IFaceT = OPT->getInterfaceType(); ObjCInterfaceDecl *IFace = IFaceT->getDecl(); IdentifierInfo *Member = MemberName.getAsIdentifierInfo(); // Search for a declared property first. if (ObjCPropertyDecl *PD = IFace->FindPropertyDeclaration(Member)) { // Check whether we can reference this property. if (DiagnoseUseOfDecl(PD, MemberLoc)) return ExprError(); QualType ResTy = PD->getType(); Selector Sel = PP.getSelectorTable().getNullarySelector(Member); ObjCMethodDecl *Getter = IFace->lookupInstanceMethod(Sel); if (DiagnosePropertyAccessorMismatch(PD, Getter, MemberLoc)) ResTy = Getter->getSendResultType(); return Owned(new (Context) ObjCPropertyRefExpr(PD, ResTy, MemberLoc, BaseExpr)); } // Check protocols on qualified interfaces. for (ObjCObjectPointerType::qual_iterator I = OPT->qual_begin(), E = OPT->qual_end(); I != E; ++I) if (ObjCPropertyDecl *PD = (*I)->FindPropertyDeclaration(Member)) { // Check whether we can reference this property. if (DiagnoseUseOfDecl(PD, MemberLoc)) return ExprError(); return Owned(new (Context) ObjCPropertyRefExpr(PD, PD->getType(), MemberLoc, BaseExpr)); } // If that failed, look for an "implicit" property by seeing if the nullary // selector is implemented. // FIXME: The logic for looking up nullary and unary selectors should be // shared with the code in ActOnInstanceMessage. Selector Sel = PP.getSelectorTable().getNullarySelector(Member); ObjCMethodDecl *Getter = IFace->lookupInstanceMethod(Sel); // If this reference is in an @implementation, check for 'private' methods. if (!Getter) Getter = IFace->lookupPrivateInstanceMethod(Sel); // Look through local category implementations associated with the class. if (!Getter) Getter = IFace->getCategoryInstanceMethod(Sel); if (Getter) { // Check if we can reference this property. if (DiagnoseUseOfDecl(Getter, MemberLoc)) return ExprError(); } // If we found a getter then this may be a valid dot-reference, we // will look for the matching setter, in case it is needed. Selector SetterSel = SelectorTable::constructSetterName(PP.getIdentifierTable(), PP.getSelectorTable(), Member); ObjCMethodDecl *Setter = IFace->lookupInstanceMethod(SetterSel); if (!Setter) { // If this reference is in an @implementation, also check for 'private' // methods. Setter = IFace->lookupPrivateInstanceMethod(SetterSel); } // Look through local category implementations associated with the class. if (!Setter) Setter = IFace->getCategoryInstanceMethod(SetterSel); if (Setter && DiagnoseUseOfDecl(Setter, MemberLoc)) return ExprError(); if (Getter) { QualType PType; PType = Getter->getSendResultType(); return Owned(new (Context) ObjCImplicitSetterGetterRefExpr(Getter, PType, Setter, MemberLoc, BaseExpr)); } // Attempt to correct for typos in property names. LookupResult Res(*this, MemberName, MemberLoc, LookupOrdinaryName); if (CorrectTypo(Res, 0, 0, IFace, false, CTC_NoKeywords, OPT) && Res.getAsSingle<ObjCPropertyDecl>()) { DeclarationName TypoResult = Res.getLookupName(); Diag(MemberLoc, diag::err_property_not_found_suggest) << MemberName << QualType(OPT, 0) << TypoResult << FixItHint::CreateReplacement(MemberLoc, TypoResult.getAsString()); ObjCPropertyDecl *Property = Res.getAsSingle<ObjCPropertyDecl>(); Diag(Property->getLocation(), diag::note_previous_decl) << Property->getDeclName(); return HandleExprPropertyRefExpr(OPT, BaseExpr, TypoResult, MemberLoc); } Diag(MemberLoc, diag::err_property_not_found) << MemberName << QualType(OPT, 0); if (Setter && !Getter) Diag(Setter->getLocation(), diag::note_getter_unavailable) << MemberName << BaseExpr->getSourceRange(); return ExprError(); }
static void cleanupDeallocOrFinalize(MigrationPass &pass) { ASTContext &Ctx = pass.Ctx; TransformActions &TA = pass.TA; DeclContext *DC = Ctx.getTranslationUnitDecl(); Selector FinalizeSel = Ctx.Selectors.getNullarySelector(&pass.Ctx.Idents.get("finalize")); typedef DeclContext::specific_decl_iterator<ObjCImplementationDecl> impl_iterator; for (impl_iterator I = impl_iterator(DC->decls_begin()), E = impl_iterator(DC->decls_end()); I != E; ++I) { ObjCMethodDecl *DeallocM = 0; ObjCMethodDecl *FinalizeM = 0; for (ObjCImplementationDecl::instmeth_iterator MI = I->instmeth_begin(), ME = I->instmeth_end(); MI != ME; ++MI) { ObjCMethodDecl *MD = *MI; if (!MD->hasBody()) continue; if (MD->getMethodFamily() == OMF_dealloc) { DeallocM = MD; } else if (MD->isInstanceMethod() && MD->getSelector() == FinalizeSel) { FinalizeM = MD; } } if (DeallocM) { if (isBodyEmpty(DeallocM->getCompoundBody(), Ctx, pass.ARCMTMacroLocs)) { Transaction Trans(TA); TA.remove(DeallocM->getSourceRange()); } if (FinalizeM) { Transaction Trans(TA); TA.remove(FinalizeM->getSourceRange()); } } else if (FinalizeM) { if (isBodyEmpty(FinalizeM->getCompoundBody(), Ctx, pass.ARCMTMacroLocs)) { Transaction Trans(TA); TA.remove(FinalizeM->getSourceRange()); } else { Transaction Trans(TA); TA.replaceText(FinalizeM->getSelectorStartLoc(), "finalize", "dealloc"); } } } }
static void checkObjCDealloc(const ObjCImplementationDecl *D, const LangOptions& LOpts, BugReporter& BR) { assert (LOpts.getGC() != LangOptions::GCOnly); ASTContext &Ctx = BR.getContext(); const ObjCInterfaceDecl *ID = D->getClassInterface(); // Does the class contain any ivars that are pointers (or id<...>)? // If not, skip the check entirely. // NOTE: This is motivated by PR 2517: // http://llvm.org/bugs/show_bug.cgi?id=2517 bool containsPointerIvar = false; for (ObjCInterfaceDecl::ivar_iterator I=ID->ivar_begin(), E=ID->ivar_end(); I!=E; ++I) { ObjCIvarDecl *ID = *I; QualType T = ID->getType(); if (!T->isObjCObjectPointerType() || ID->getAttr<IBOutletAttr>() || // Skip IBOutlets. ID->getAttr<IBOutletCollectionAttr>()) // Skip IBOutletCollections. continue; containsPointerIvar = true; break; } if (!containsPointerIvar) return; // Determine if the class subclasses NSObject. IdentifierInfo* NSObjectII = &Ctx.Idents.get("NSObject"); IdentifierInfo* SenTestCaseII = &Ctx.Idents.get("SenTestCase"); for ( ; ID ; ID = ID->getSuperClass()) { IdentifierInfo *II = ID->getIdentifier(); if (II == NSObjectII) break; // FIXME: For now, ignore classes that subclass SenTestCase, as these don't // need to implement -dealloc. They implement tear down in another way, // which we should try and catch later. // http://llvm.org/bugs/show_bug.cgi?id=3187 if (II == SenTestCaseII) return; } if (!ID) return; // Get the "dealloc" selector. IdentifierInfo* II = &Ctx.Idents.get("dealloc"); Selector S = Ctx.Selectors.getSelector(0, &II); ObjCMethodDecl *MD = 0; // Scan the instance methods for "dealloc". for (ObjCImplementationDecl::instmeth_iterator I = D->instmeth_begin(), E = D->instmeth_end(); I!=E; ++I) { if ((*I)->getSelector() == S) { MD = *I; break; } } PathDiagnosticLocation DLoc = PathDiagnosticLocation::createBegin(D, BR.getSourceManager()); if (!MD) { // No dealloc found. const char* name = LOpts.getGC() == LangOptions::NonGC ? "missing -dealloc" : "missing -dealloc (Hybrid MM, non-GC)"; std::string buf; llvm::raw_string_ostream os(buf); os << "Objective-C class '" << *D << "' lacks a 'dealloc' instance method"; BR.EmitBasicReport(D, name, categories::CoreFoundationObjectiveC, os.str(), DLoc); return; } // dealloc found. Scan for missing [super dealloc]. if (MD->getBody() && !scan_dealloc(MD->getBody(), S)) { const char* name = LOpts.getGC() == LangOptions::NonGC ? "missing [super dealloc]" : "missing [super dealloc] (Hybrid MM, non-GC)"; std::string buf; llvm::raw_string_ostream os(buf); os << "The 'dealloc' instance method in Objective-C class '" << *D << "' does not send a 'dealloc' message to its super class" " (missing [super dealloc])"; BR.EmitBasicReport(MD, name, categories::CoreFoundationObjectiveC, os.str(), DLoc); return; } // Get the "release" selector. IdentifierInfo* RII = &Ctx.Idents.get("release"); Selector RS = Ctx.Selectors.getSelector(0, &RII); // Get the "self" identifier IdentifierInfo* SelfII = &Ctx.Idents.get("self"); // Scan for missing and extra releases of ivars used by implementations // of synthesized properties for (ObjCImplementationDecl::propimpl_iterator I = D->propimpl_begin(), E = D->propimpl_end(); I!=E; ++I) { // We can only check the synthesized properties if (I->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize) continue; ObjCIvarDecl *ID = I->getPropertyIvarDecl(); if (!ID) continue; QualType T = ID->getType(); if (!T->isObjCObjectPointerType()) // Skip non-pointer ivars continue; const ObjCPropertyDecl *PD = I->getPropertyDecl(); if (!PD) continue; // ivars cannot be set via read-only properties, so we'll skip them if (PD->isReadOnly()) continue; // ivar must be released if and only if the kind of setter was not 'assign' bool requiresRelease = PD->getSetterKind() != ObjCPropertyDecl::Assign; if (scan_ivar_release(MD->getBody(), ID, PD, RS, SelfII, Ctx) != requiresRelease) { const char *name = 0; std::string buf; llvm::raw_string_ostream os(buf); if (requiresRelease) { name = LOpts.getGC() == LangOptions::NonGC ? "missing ivar release (leak)" : "missing ivar release (Hybrid MM, non-GC)"; os << "The '" << *ID << "' instance variable was retained by a synthesized property but " "wasn't released in 'dealloc'"; } else { name = LOpts.getGC() == LangOptions::NonGC ? "extra ivar release (use-after-release)" : "extra ivar release (Hybrid MM, non-GC)"; os << "The '" << *ID << "' instance variable was not retained by a synthesized property " "but was released in 'dealloc'"; } PathDiagnosticLocation SDLoc = PathDiagnosticLocation::createBegin(*I, BR.getSourceManager()); BR.EmitBasicReport(MD, name, categories::CoreFoundationObjectiveC, os.str(), SDLoc); } } }
static bool ClassImplementsAllMethodsAndProperties(ASTContext &Ctx, const ObjCImplementationDecl *ImpDecl, const ObjCInterfaceDecl *IDecl, ObjCProtocolDecl *Protocol) { // In auto-synthesis, protocol properties are not synthesized. So, // a conforming protocol must have its required properties declared // in class interface. bool HasAtleastOneRequiredProperty = false; if (const ObjCProtocolDecl *PDecl = Protocol->getDefinition()) for (ObjCProtocolDecl::prop_iterator P = PDecl->prop_begin(), E = PDecl->prop_end(); P != E; ++P) { ObjCPropertyDecl *Property = *P; if (Property->getPropertyImplementation() == ObjCPropertyDecl::Optional) continue; HasAtleastOneRequiredProperty = true; DeclContext::lookup_const_result R = IDecl->lookup(Property->getDeclName()); if (R.size() == 0) { // Relax the rule and look into class's implementation for a synthesize // or dynamic declaration. Class is implementing a property coming from // another protocol. This still makes the target protocol as conforming. if (!ImpDecl->FindPropertyImplDecl( Property->getDeclName().getAsIdentifierInfo())) return false; } else if (ObjCPropertyDecl *ClassProperty = dyn_cast<ObjCPropertyDecl>(R[0])) { if ((ClassProperty->getPropertyAttributes() != Property->getPropertyAttributes()) || !Ctx.hasSameType(ClassProperty->getType(), Property->getType())) return false; } else return false; } // At this point, all required properties in this protocol conform to those // declared in the class. // Check that class implements the required methods of the protocol too. bool HasAtleastOneRequiredMethod = false; if (const ObjCProtocolDecl *PDecl = Protocol->getDefinition()) { if (PDecl->meth_begin() == PDecl->meth_end()) return HasAtleastOneRequiredProperty; for (ObjCContainerDecl::method_iterator M = PDecl->meth_begin(), MEnd = PDecl->meth_end(); M != MEnd; ++M) { ObjCMethodDecl *MD = (*M); if (MD->isImplicit()) continue; if (MD->getImplementationControl() == ObjCMethodDecl::Optional) continue; DeclContext::lookup_const_result R = ImpDecl->lookup(MD->getDeclName()); if (R.size() == 0) return false; bool match = false; HasAtleastOneRequiredMethod = true; for (unsigned I = 0, N = R.size(); I != N; ++I) if (ObjCMethodDecl *ImpMD = dyn_cast<ObjCMethodDecl>(R[0])) if (Ctx.ObjCMethodsAreEqual(MD, ImpMD)) { match = true; break; } if (!match) return false; } } if (HasAtleastOneRequiredProperty || HasAtleastOneRequiredMethod) return true; return false; }
/// HandleExprPropertyRefExpr - Handle foo.bar where foo is a pointer to an /// objective C interface. This is a property reference expression. ExprResult Sema:: HandleExprPropertyRefExpr(const ObjCObjectPointerType *OPT, Expr *BaseExpr, DeclarationName MemberName, SourceLocation MemberLoc, SourceLocation SuperLoc, QualType SuperType, bool Super) { const ObjCInterfaceType *IFaceT = OPT->getInterfaceType(); ObjCInterfaceDecl *IFace = IFaceT->getDecl(); IdentifierInfo *Member = MemberName.getAsIdentifierInfo(); if (IFace->isForwardDecl()) { Diag(MemberLoc, diag::err_property_not_found_forward_class) << MemberName << QualType(OPT, 0); Diag(IFace->getLocation(), diag::note_forward_class); return ExprError(); } // Search for a declared property first. if (ObjCPropertyDecl *PD = IFace->FindPropertyDeclaration(Member)) { // Check whether we can reference this property. if (DiagnoseUseOfDecl(PD, MemberLoc)) return ExprError(); QualType ResTy = PD->getType(); Selector Sel = PP.getSelectorTable().getNullarySelector(Member); ObjCMethodDecl *Getter = IFace->lookupInstanceMethod(Sel); if (DiagnosePropertyAccessorMismatch(PD, Getter, MemberLoc)) ResTy = Getter->getResultType(); if (Super) return Owned(new (Context) ObjCPropertyRefExpr(PD, ResTy, VK_LValue, OK_ObjCProperty, MemberLoc, SuperLoc, SuperType)); else return Owned(new (Context) ObjCPropertyRefExpr(PD, ResTy, VK_LValue, OK_ObjCProperty, MemberLoc, BaseExpr)); } // Check protocols on qualified interfaces. for (ObjCObjectPointerType::qual_iterator I = OPT->qual_begin(), E = OPT->qual_end(); I != E; ++I) if (ObjCPropertyDecl *PD = (*I)->FindPropertyDeclaration(Member)) { // Check whether we can reference this property. if (DiagnoseUseOfDecl(PD, MemberLoc)) return ExprError(); if (Super) return Owned(new (Context) ObjCPropertyRefExpr(PD, PD->getType(), VK_LValue, OK_ObjCProperty, MemberLoc, SuperLoc, SuperType)); else return Owned(new (Context) ObjCPropertyRefExpr(PD, PD->getType(), VK_LValue, OK_ObjCProperty, MemberLoc, BaseExpr)); } // If that failed, look for an "implicit" property by seeing if the nullary // selector is implemented. // FIXME: The logic for looking up nullary and unary selectors should be // shared with the code in ActOnInstanceMessage. Selector Sel = PP.getSelectorTable().getNullarySelector(Member); ObjCMethodDecl *Getter = IFace->lookupInstanceMethod(Sel); // May be founf in property's qualified list. if (!Getter) Getter = LookupMethodInQualifiedType(Sel, OPT, true); // If this reference is in an @implementation, check for 'private' methods. if (!Getter) Getter = IFace->lookupPrivateMethod(Sel); // Look through local category implementations associated with the class. if (!Getter) Getter = IFace->getCategoryInstanceMethod(Sel); if (Getter) { // Check if we can reference this property. if (DiagnoseUseOfDecl(Getter, MemberLoc)) return ExprError(); } // If we found a getter then this may be a valid dot-reference, we // will look for the matching setter, in case it is needed. Selector SetterSel = SelectorTable::constructSetterName(PP.getIdentifierTable(), PP.getSelectorTable(), Member); ObjCMethodDecl *Setter = IFace->lookupInstanceMethod(SetterSel); // May be founf in property's qualified list. if (!Setter) Setter = LookupMethodInQualifiedType(SetterSel, OPT, true); if (!Setter) { // If this reference is in an @implementation, also check for 'private' // methods. Setter = IFace->lookupPrivateMethod(SetterSel); } // Look through local category implementations associated with the class. if (!Setter) Setter = IFace->getCategoryInstanceMethod(SetterSel); if (Setter && DiagnoseUseOfDecl(Setter, MemberLoc)) return ExprError(); if (Getter || Setter) { QualType PType; if (Getter) PType = Getter->getSendResultType(); else { ParmVarDecl *ArgDecl = *Setter->param_begin(); PType = ArgDecl->getType(); } ExprValueKind VK = VK_LValue; ExprObjectKind OK = OK_ObjCProperty; if (!getLangOptions().CPlusPlus && !PType.hasQualifiers() && PType->isVoidType()) VK = VK_RValue, OK = OK_Ordinary; if (Super) return Owned(new (Context) ObjCPropertyRefExpr(Getter, Setter, PType, VK, OK, MemberLoc, SuperLoc, SuperType)); else return Owned(new (Context) ObjCPropertyRefExpr(Getter, Setter, PType, VK, OK, MemberLoc, BaseExpr)); } // Attempt to correct for typos in property names. LookupResult Res(*this, MemberName, MemberLoc, LookupOrdinaryName); if (CorrectTypo(Res, 0, 0, IFace, false, CTC_NoKeywords, OPT) && Res.getAsSingle<ObjCPropertyDecl>()) { DeclarationName TypoResult = Res.getLookupName(); Diag(MemberLoc, diag::err_property_not_found_suggest) << MemberName << QualType(OPT, 0) << TypoResult << FixItHint::CreateReplacement(MemberLoc, TypoResult.getAsString()); ObjCPropertyDecl *Property = Res.getAsSingle<ObjCPropertyDecl>(); Diag(Property->getLocation(), diag::note_previous_decl) << Property->getDeclName(); return HandleExprPropertyRefExpr(OPT, BaseExpr, TypoResult, MemberLoc, SuperLoc, SuperType, Super); } ObjCInterfaceDecl *ClassDeclared; if (ObjCIvarDecl *Ivar = IFace->lookupInstanceVariable(Member, ClassDeclared)) { QualType T = Ivar->getType(); if (const ObjCObjectPointerType * OBJPT = T->getAsObjCInterfacePointerType()) { const ObjCInterfaceType *IFaceT = OBJPT->getInterfaceType(); if (ObjCInterfaceDecl *IFace = IFaceT->getDecl()) if (IFace->isForwardDecl()) { Diag(MemberLoc, diag::err_property_not_as_forward_class) << MemberName << IFace; Diag(IFace->getLocation(), diag::note_forward_class); return ExprError(); } } } Diag(MemberLoc, diag::err_property_not_found) << MemberName << QualType(OPT, 0); if (Setter) Diag(Setter->getLocation(), diag::note_getter_unavailable) << MemberName << BaseExpr->getSourceRange(); return ExprError(); }
ExprResult Sema:: ActOnClassPropertyRefExpr(IdentifierInfo &receiverName, IdentifierInfo &propertyName, SourceLocation receiverNameLoc, SourceLocation propertyNameLoc) { IdentifierInfo *receiverNamePtr = &receiverName; ObjCInterfaceDecl *IFace = getObjCInterfaceDecl(receiverNamePtr, receiverNameLoc); if (IFace == 0) { // If the "receiver" is 'super' in a method, handle it as an expression-like // property reference. if (receiverNamePtr->isStr("super")) { if (ObjCMethodDecl *CurMethod = tryCaptureObjCSelf()) { if (CurMethod->isInstanceMethod()) { QualType T = Context.getObjCInterfaceType(CurMethod->getClassInterface()); T = Context.getObjCObjectPointerType(T); return HandleExprPropertyRefExpr(T->getAsObjCInterfacePointerType(), /*BaseExpr*/0, &propertyName, propertyNameLoc, receiverNameLoc, T, true); } // Otherwise, if this is a class method, try dispatching to our // superclass. IFace = CurMethod->getClassInterface()->getSuperClass(); } } if (IFace == 0) { Diag(receiverNameLoc, diag::err_expected_ident_or_lparen); return ExprError(); } } // Search for a declared property first. Selector Sel = PP.getSelectorTable().getNullarySelector(&propertyName); ObjCMethodDecl *Getter = IFace->lookupClassMethod(Sel); // If this reference is in an @implementation, check for 'private' methods. if (!Getter) if (ObjCMethodDecl *CurMeth = getCurMethodDecl()) if (ObjCInterfaceDecl *ClassDecl = CurMeth->getClassInterface()) if (ObjCImplementationDecl *ImpDecl = ClassDecl->getImplementation()) Getter = ImpDecl->getClassMethod(Sel); if (Getter) { // FIXME: refactor/share with ActOnMemberReference(). // Check if we can reference this property. if (DiagnoseUseOfDecl(Getter, propertyNameLoc)) return ExprError(); } // Look for the matching setter, in case it is needed. Selector SetterSel = SelectorTable::constructSetterName(PP.getIdentifierTable(), PP.getSelectorTable(), &propertyName); ObjCMethodDecl *Setter = IFace->lookupClassMethod(SetterSel); if (!Setter) { // If this reference is in an @implementation, also check for 'private' // methods. if (ObjCMethodDecl *CurMeth = getCurMethodDecl()) if (ObjCInterfaceDecl *ClassDecl = CurMeth->getClassInterface()) if (ObjCImplementationDecl *ImpDecl = ClassDecl->getImplementation()) Setter = ImpDecl->getClassMethod(SetterSel); } // Look through local category implementations associated with the class. if (!Setter) Setter = IFace->getCategoryClassMethod(SetterSel); if (Setter && DiagnoseUseOfDecl(Setter, propertyNameLoc)) return ExprError(); if (Getter || Setter) { QualType PType; ExprValueKind VK = VK_LValue; if (Getter) { PType = Getter->getSendResultType(); if (!getLangOptions().CPlusPlus && !PType.hasQualifiers() && PType->isVoidType()) VK = VK_RValue; } else { for (ObjCMethodDecl::param_iterator PI = Setter->param_begin(), E = Setter->param_end(); PI != E; ++PI) PType = (*PI)->getType(); VK = VK_LValue; } ExprObjectKind OK = (VK == VK_RValue ? OK_Ordinary : OK_ObjCProperty); return Owned(new (Context) ObjCPropertyRefExpr(Getter, Setter, PType, VK, OK, propertyNameLoc, receiverNameLoc, IFace)); } return ExprError(Diag(propertyNameLoc, diag::err_property_not_found) << &propertyName << Context.getObjCInterfaceType(IFace)); }