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); } } }
static bool checkReturnValueType(const ASTContext &Ctx, const Expr *E, QualType &DeducedType, QualType &AlternateType) { // Handle ReturnStmts with no expressions. if (!E) { if (AlternateType.isNull()) AlternateType = Ctx.VoidTy; return Ctx.hasSameType(DeducedType, Ctx.VoidTy); } QualType StrictType = E->getType(); QualType LooseType = StrictType; // In C, enum constants have the type of their underlying integer type, // not the enum. When inferring block return types, we should allow // the enum type if an enum constant is used, unless the enum is // anonymous (in which case there can be no variables of its type). if (!Ctx.getLangOpts().CPlusPlus) { const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts()); if (DRE) { const Decl *D = DRE->getDecl(); if (const EnumConstantDecl *ECD = dyn_cast<EnumConstantDecl>(D)) { const EnumDecl *Enum = cast<EnumDecl>(ECD->getDeclContext()); if (Enum->getDeclName() || Enum->getTypedefNameForAnonDecl()) LooseType = Ctx.getTypeDeclType(Enum); } } } // Special case for the first return statement we find. // The return type has already been tentatively set, but we might still // have an alternate type we should prefer. if (AlternateType.isNull()) AlternateType = LooseType; if (Ctx.hasSameType(DeducedType, StrictType)) { // FIXME: The loose type is different when there are constants from two // different enums. We could consider warning here. if (AlternateType != Ctx.DependentTy) if (!Ctx.hasSameType(AlternateType, LooseType)) AlternateType = Ctx.VoidTy; return true; } if (Ctx.hasSameType(DeducedType, LooseType)) { // Use DependentTy to signal that we're using an alternate type and may // need to add casts somewhere. AlternateType = Ctx.DependentTy; return true; } if (Ctx.hasSameType(AlternateType, StrictType) || Ctx.hasSameType(AlternateType, LooseType)) { DeducedType = AlternateType; // Use DependentTy to signal that we're using an alternate type and may // need to add casts somewhere. AlternateType = Ctx.DependentTy; return true; } return false; }
/// \brief The LoopFixer callback, which determines if loops discovered by the /// matchers are convertible, printing information about the loops if so. void LoopFixer::run(const MatchFinder::MatchResult &Result) { const BoundNodes &Nodes = Result.Nodes; Confidence ConfidenceLevel(RL_Safe); ASTContext *Context = Result.Context; const ForStmt *TheLoop = Nodes.getStmtAs<ForStmt>(LoopName); if (!Owner.isFileModifiable(Context->getSourceManager(),TheLoop->getForLoc())) return; // Check that we have exactly one index variable and at most one end variable. const VarDecl *LoopVar = Nodes.getDeclAs<VarDecl>(IncrementVarName); const VarDecl *CondVar = Nodes.getDeclAs<VarDecl>(ConditionVarName); const VarDecl *InitVar = Nodes.getDeclAs<VarDecl>(InitVarName); if (!areSameVariable(LoopVar, CondVar) || !areSameVariable(LoopVar, InitVar)) return; const VarDecl *EndVar = Nodes.getDeclAs<VarDecl>(EndVarName); const VarDecl *ConditionEndVar = Nodes.getDeclAs<VarDecl>(ConditionEndVarName); if (EndVar && !areSameVariable(EndVar, ConditionEndVar)) return; // If the end comparison isn't a variable, we can try to work with the // expression the loop variable is being tested against instead. const CXXMemberCallExpr *EndCall = Nodes.getStmtAs<CXXMemberCallExpr>(EndCallName); const Expr *BoundExpr = Nodes.getStmtAs<Expr>(ConditionBoundName); // If the loop calls end()/size() after each iteration, lower our confidence // level. if (FixerKind != LFK_Array && !EndVar) ConfidenceLevel.lowerTo(RL_Reasonable); const Expr *ContainerExpr = nullptr; bool DerefByValue = false; bool DerefByConstRef = false; bool ContainerNeedsDereference = false; // FIXME: Try to put most of this logic inside a matcher. Currently, matchers // don't allow the right-recursive checks in digThroughConstructors. if (FixerKind == LFK_Iterator) { ContainerExpr = findContainer(Context, LoopVar->getInit(), EndVar ? EndVar->getInit() : EndCall, &ContainerNeedsDereference); QualType InitVarType = InitVar->getType(); QualType CanonicalInitVarType = InitVarType.getCanonicalType(); const CXXMemberCallExpr *BeginCall = Nodes.getNodeAs<CXXMemberCallExpr>(BeginCallName); assert(BeginCall && "Bad Callback. No begin call expression."); QualType CanonicalBeginType = BeginCall->getMethodDecl()->getReturnType().getCanonicalType(); if (CanonicalBeginType->isPointerType() && CanonicalInitVarType->isPointerType()) { QualType BeginPointeeType = CanonicalBeginType->getPointeeType(); QualType InitPointeeType = CanonicalInitVarType->getPointeeType(); // If the initializer and the variable are both pointers check if the // un-qualified pointee types match otherwise we don't use auto. if (!Context->hasSameUnqualifiedType(InitPointeeType, BeginPointeeType)) return; } else { // Check for qualified types to avoid conversions from non-const to const // iterator types. if (!Context->hasSameType(CanonicalInitVarType, CanonicalBeginType)) return; } DerefByValue = Nodes.getNodeAs<QualType>(DerefByValueResultName) != nullptr; if (!DerefByValue) { if (const QualType *DerefType = Nodes.getNodeAs<QualType>(DerefByRefResultName)) { // A node will only be bound with DerefByRefResultName if we're dealing // with a user-defined iterator type. Test the const qualification of // the reference type. DerefByConstRef = (*DerefType)->getAs<ReferenceType>()->getPointeeType() .isConstQualified(); } else { // By nature of the matcher this case is triggered only for built-in // iterator types (i.e. pointers). assert(isa<PointerType>(CanonicalInitVarType) && "Non-class iterator type is not a pointer type"); QualType InitPointeeType = CanonicalInitVarType->getPointeeType(); QualType BeginPointeeType = CanonicalBeginType->getPointeeType(); // If the initializer and variable have both the same type just use auto // otherwise we test for const qualification of the pointed-at type. if (!Context->hasSameType(InitPointeeType, BeginPointeeType)) DerefByConstRef = InitPointeeType.isConstQualified(); } } else { // If the dereference operator returns by value then test for the // canonical const qualification of the init variable type. DerefByConstRef = CanonicalInitVarType.isConstQualified(); } } else if (FixerKind == LFK_PseudoArray) { if (!EndCall) return; ContainerExpr = EndCall->getImplicitObjectArgument(); const MemberExpr *Member = dyn_cast<MemberExpr>(EndCall->getCallee()); if (!Member) return; ContainerNeedsDereference = Member->isArrow(); } // We must know the container or an array length bound. if (!ContainerExpr && !BoundExpr) return; findAndVerifyUsages(Context, LoopVar, EndVar, ContainerExpr, BoundExpr, ContainerNeedsDereference, DerefByValue, DerefByConstRef, TheLoop, ConfidenceLevel); }
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; }