| //===--- TransUnbridgedCasts.cpp - Transformations to ARC mode ------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // rewriteUnbridgedCasts: |
| // |
| // A cast of non-objc pointer to an objc one is checked. If the non-objc pointer |
| // is from a file-level variable, __bridge cast is used to convert it. |
| // For the result of a function call that we know is +1/+0, |
| // __bridge/CFBridgingRelease is used. |
| // |
| // NSString *str = (NSString *)kUTTypePlainText; |
| // str = b ? kUTTypeRTF : kUTTypePlainText; |
| // NSString *_uuidString = (NSString *)CFUUIDCreateString(kCFAllocatorDefault, |
| // _uuid); |
| // ----> |
| // NSString *str = (__bridge NSString *)kUTTypePlainText; |
| // str = (__bridge NSString *)(b ? kUTTypeRTF : kUTTypePlainText); |
| // NSString *_uuidString = (NSString *) |
| // CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, _uuid)); |
| // |
| // For a C pointer to ObjC, for casting 'self', __bridge is used. |
| // |
| // CFStringRef str = (CFStringRef)self; |
| // ----> |
| // CFStringRef str = (__bridge CFStringRef)self; |
| // |
| // Uses of Block_copy/Block_release macros are rewritten: |
| // |
| // c = Block_copy(b); |
| // Block_release(c); |
| // ----> |
| // c = [b copy]; |
| // <removed> |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "Transforms.h" |
| #include "Internals.h" |
| #include "clang/AST/ASTContext.h" |
| #include "clang/AST/Attr.h" |
| #include "clang/AST/ParentMap.h" |
| #include "clang/Analysis/DomainSpecific/CocoaConventions.h" |
| #include "clang/Basic/SourceManager.h" |
| #include "clang/Lex/Lexer.h" |
| #include "clang/Sema/SemaDiagnostic.h" |
| #include "llvm/ADT/SmallString.h" |
| |
| using namespace clang; |
| using namespace arcmt; |
| using namespace trans; |
| |
| namespace { |
| |
| class UnbridgedCastRewriter : public RecursiveASTVisitor<UnbridgedCastRewriter>{ |
| MigrationPass &Pass; |
| IdentifierInfo *SelfII; |
| std::unique_ptr<ParentMap> StmtMap; |
| Decl *ParentD; |
| Stmt *Body; |
| mutable std::unique_ptr<ExprSet> Removables; |
| |
| public: |
| UnbridgedCastRewriter(MigrationPass &pass) |
| : Pass(pass), ParentD(nullptr), Body(nullptr) { |
| SelfII = &Pass.Ctx.Idents.get("self"); |
| } |
| |
| void transformBody(Stmt *body, Decl *ParentD) { |
| this->ParentD = ParentD; |
| Body = body; |
| StmtMap.reset(new ParentMap(body)); |
| TraverseStmt(body); |
| } |
| |
| bool TraverseBlockDecl(BlockDecl *D) { |
| // ParentMap does not enter into a BlockDecl to record its stmts, so use a |
| // new UnbridgedCastRewriter to handle the block. |
| UnbridgedCastRewriter(Pass).transformBody(D->getBody(), D); |
| return true; |
| } |
| |
| bool VisitCastExpr(CastExpr *E) { |
| if (E->getCastKind() != CK_CPointerToObjCPointerCast && |
| E->getCastKind() != CK_BitCast && |
| E->getCastKind() != CK_AnyPointerToBlockPointerCast) |
| return true; |
| |
| QualType castType = E->getType(); |
| Expr *castExpr = E->getSubExpr(); |
| QualType castExprType = castExpr->getType(); |
| |
| if (castType->isObjCRetainableType() == castExprType->isObjCRetainableType()) |
| return true; |
| |
| bool exprRetainable = castExprType->isObjCIndirectLifetimeType(); |
| bool castRetainable = castType->isObjCIndirectLifetimeType(); |
| if (exprRetainable == castRetainable) return true; |
| |
| if (castExpr->isNullPointerConstant(Pass.Ctx, |
| Expr::NPC_ValueDependentIsNull)) |
| return true; |
| |
| SourceLocation loc = castExpr->getExprLoc(); |
| if (loc.isValid() && Pass.Ctx.getSourceManager().isInSystemHeader(loc)) |
| return true; |
| |
| if (castType->isObjCRetainableType()) |
| transformNonObjCToObjCCast(E); |
| else |
| transformObjCToNonObjCCast(E); |
| |
| return true; |
| } |
| |
| private: |
| void transformNonObjCToObjCCast(CastExpr *E) { |
| if (!E) return; |
| |
| // Global vars are assumed that are cast as unretained. |
| if (isGlobalVar(E)) |
| if (E->getSubExpr()->getType()->isPointerType()) { |
| castToObjCObject(E, /*retained=*/false); |
| return; |
| } |
| |
| // If the cast is directly over the result of a Core Foundation function |
| // try to figure out whether it should be cast as retained or unretained. |
| Expr *inner = E->IgnoreParenCasts(); |
| if (CallExpr *callE = dyn_cast<CallExpr>(inner)) { |
| if (FunctionDecl *FD = callE->getDirectCallee()) { |
| if (FD->hasAttr<CFReturnsRetainedAttr>()) { |
| castToObjCObject(E, /*retained=*/true); |
| return; |
| } |
| if (FD->hasAttr<CFReturnsNotRetainedAttr>()) { |
| castToObjCObject(E, /*retained=*/false); |
| return; |
| } |
| if (FD->isGlobal() && |
| FD->getIdentifier() && |
| ento::cocoa::isRefType(E->getSubExpr()->getType(), "CF", |
| FD->getIdentifier()->getName())) { |
| StringRef fname = FD->getIdentifier()->getName(); |
| if (fname.endswith("Retain") || fname.contains("Create") || |
| fname.contains("Copy")) { |
| // Do not migrate to couple of bridge transfer casts which |
| // cancel each other out. Leave it unchanged so error gets user |
| // attention instead. |
| if (FD->getName() == "CFRetain" && |
| FD->getNumParams() == 1 && |
| FD->getParent()->isTranslationUnit() && |
| FD->isExternallyVisible()) { |
| Expr *Arg = callE->getArg(0); |
| if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) { |
| const Expr *sub = ICE->getSubExpr(); |
| QualType T = sub->getType(); |
| if (T->isObjCObjectPointerType()) |
| return; |
| } |
| } |
| castToObjCObject(E, /*retained=*/true); |
| return; |
| } |
| |
| if (fname.contains("Get")) { |
| castToObjCObject(E, /*retained=*/false); |
| return; |
| } |
| } |
| } |
| } |
| |
| // If returning an ivar or a member of an ivar from a +0 method, use |
| // a __bridge cast. |
| Expr *base = inner->IgnoreParenImpCasts(); |
| while (isa<MemberExpr>(base)) |
| base = cast<MemberExpr>(base)->getBase()->IgnoreParenImpCasts(); |
| if (isa<ObjCIvarRefExpr>(base) && |
| isa<ReturnStmt>(StmtMap->getParentIgnoreParenCasts(E))) { |
| if (ObjCMethodDecl *method = dyn_cast_or_null<ObjCMethodDecl>(ParentD)) { |
| if (!method->hasAttr<NSReturnsRetainedAttr>()) { |
| castToObjCObject(E, /*retained=*/false); |
| return; |
| } |
| } |
| } |
| } |
| |
| void castToObjCObject(CastExpr *E, bool retained) { |
| rewriteToBridgedCast(E, retained ? OBC_BridgeTransfer : OBC_Bridge); |
| } |
| |
| void rewriteToBridgedCast(CastExpr *E, ObjCBridgeCastKind Kind) { |
| Transaction Trans(Pass.TA); |
| rewriteToBridgedCast(E, Kind, Trans); |
| } |
| |
| void rewriteToBridgedCast(CastExpr *E, ObjCBridgeCastKind Kind, |
| Transaction &Trans) { |
| TransformActions &TA = Pass.TA; |
| |
| // We will remove the compiler diagnostic. |
| if (!TA.hasDiagnostic(diag::err_arc_mismatched_cast, |
| diag::err_arc_cast_requires_bridge, |
| E->getBeginLoc())) { |
| Trans.abort(); |
| return; |
| } |
| |
| StringRef bridge; |
| switch(Kind) { |
| case OBC_Bridge: |
| bridge = "__bridge "; break; |
| case OBC_BridgeTransfer: |
| bridge = "__bridge_transfer "; break; |
| case OBC_BridgeRetained: |
| bridge = "__bridge_retained "; break; |
| } |
| |
| TA.clearDiagnostic(diag::err_arc_mismatched_cast, |
| diag::err_arc_cast_requires_bridge, E->getBeginLoc()); |
| if (Kind == OBC_Bridge || !Pass.CFBridgingFunctionsDefined()) { |
| if (CStyleCastExpr *CCE = dyn_cast<CStyleCastExpr>(E)) { |
| TA.insertAfterToken(CCE->getLParenLoc(), bridge); |
| } else { |
| SourceLocation insertLoc = E->getSubExpr()->getBeginLoc(); |
| SmallString<128> newCast; |
| newCast += '('; |
| newCast += bridge; |
| newCast += E->getType().getAsString(Pass.Ctx.getPrintingPolicy()); |
| newCast += ')'; |
| |
| if (isa<ParenExpr>(E->getSubExpr())) { |
| TA.insert(insertLoc, newCast.str()); |
| } else { |
| newCast += '('; |
| TA.insert(insertLoc, newCast.str()); |
| TA.insertAfterToken(E->getEndLoc(), ")"); |
| } |
| } |
| } else { |
| assert(Kind == OBC_BridgeTransfer || Kind == OBC_BridgeRetained); |
| SmallString<32> BridgeCall; |
| |
| Expr *WrapE = E->getSubExpr(); |
| SourceLocation InsertLoc = WrapE->getBeginLoc(); |
| |
| SourceManager &SM = Pass.Ctx.getSourceManager(); |
| char PrevChar = *SM.getCharacterData(InsertLoc.getLocWithOffset(-1)); |
| if (Lexer::isAsciiIdentifierContinueChar(PrevChar, |
| Pass.Ctx.getLangOpts())) |
| BridgeCall += ' '; |
| |
| if (Kind == OBC_BridgeTransfer) |
| BridgeCall += "CFBridgingRelease"; |
| else |
| BridgeCall += "CFBridgingRetain"; |
| |
| if (isa<ParenExpr>(WrapE)) { |
| TA.insert(InsertLoc, BridgeCall); |
| } else { |
| BridgeCall += '('; |
| TA.insert(InsertLoc, BridgeCall); |
| TA.insertAfterToken(WrapE->getEndLoc(), ")"); |
| } |
| } |
| } |
| |
| void rewriteCastForCFRetain(CastExpr *castE, CallExpr *callE) { |
| Transaction Trans(Pass.TA); |
| Pass.TA.replace(callE->getSourceRange(), callE->getArg(0)->getSourceRange()); |
| rewriteToBridgedCast(castE, OBC_BridgeRetained, Trans); |
| } |
| |
| void getBlockMacroRanges(CastExpr *E, SourceRange &Outer, SourceRange &Inner) { |
| SourceManager &SM = Pass.Ctx.getSourceManager(); |
| SourceLocation Loc = E->getExprLoc(); |
| assert(Loc.isMacroID()); |
| CharSourceRange MacroRange = SM.getImmediateExpansionRange(Loc); |
| SourceRange SubRange = E->getSubExpr()->IgnoreParenImpCasts()->getSourceRange(); |
| SourceLocation InnerBegin = SM.getImmediateMacroCallerLoc(SubRange.getBegin()); |
| SourceLocation InnerEnd = SM.getImmediateMacroCallerLoc(SubRange.getEnd()); |
| |
| Outer = MacroRange.getAsRange(); |
| Inner = SourceRange(InnerBegin, InnerEnd); |
| } |
| |
| void rewriteBlockCopyMacro(CastExpr *E) { |
| SourceRange OuterRange, InnerRange; |
| getBlockMacroRanges(E, OuterRange, InnerRange); |
| |
| Transaction Trans(Pass.TA); |
| Pass.TA.replace(OuterRange, InnerRange); |
| Pass.TA.insert(InnerRange.getBegin(), "["); |
| Pass.TA.insertAfterToken(InnerRange.getEnd(), " copy]"); |
| Pass.TA.clearDiagnostic(diag::err_arc_mismatched_cast, |
| diag::err_arc_cast_requires_bridge, |
| OuterRange); |
| } |
| |
| void removeBlockReleaseMacro(CastExpr *E) { |
| SourceRange OuterRange, InnerRange; |
| getBlockMacroRanges(E, OuterRange, InnerRange); |
| |
| Transaction Trans(Pass.TA); |
| Pass.TA.clearDiagnostic(diag::err_arc_mismatched_cast, |
| diag::err_arc_cast_requires_bridge, |
| OuterRange); |
| if (!hasSideEffects(E, Pass.Ctx)) { |
| if (tryRemoving(cast<Expr>(StmtMap->getParentIgnoreParenCasts(E)))) |
| return; |
| } |
| Pass.TA.replace(OuterRange, InnerRange); |
| } |
| |
| bool tryRemoving(Expr *E) const { |
| if (!Removables) { |
| Removables.reset(new ExprSet); |
| collectRemovables(Body, *Removables); |
| } |
| |
| if (Removables->count(E)) { |
| Pass.TA.removeStmt(E); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| void transformObjCToNonObjCCast(CastExpr *E) { |
| SourceLocation CastLoc = E->getExprLoc(); |
| if (CastLoc.isMacroID()) { |
| StringRef MacroName = Lexer::getImmediateMacroName(CastLoc, |
| Pass.Ctx.getSourceManager(), |
| Pass.Ctx.getLangOpts()); |
| if (MacroName == "Block_copy") { |
| rewriteBlockCopyMacro(E); |
| return; |
| } |
| if (MacroName == "Block_release") { |
| removeBlockReleaseMacro(E); |
| return; |
| } |
| } |
| |
| if (isSelf(E->getSubExpr())) |
| return rewriteToBridgedCast(E, OBC_Bridge); |
| |
| CallExpr *callE; |
| if (isPassedToCFRetain(E, callE)) |
| return rewriteCastForCFRetain(E, callE); |
| |
| ObjCMethodFamily family = getFamilyOfMessage(E->getSubExpr()); |
| if (family == OMF_retain) |
| return rewriteToBridgedCast(E, OBC_BridgeRetained); |
| |
| if (family == OMF_autorelease || family == OMF_release) { |
| std::string err = "it is not safe to cast to '"; |
| err += E->getType().getAsString(Pass.Ctx.getPrintingPolicy()); |
| err += "' the result of '"; |
| err += family == OMF_autorelease ? "autorelease" : "release"; |
| err += "' message; a __bridge cast may result in a pointer to a " |
| "destroyed object and a __bridge_retained may leak the object"; |
| Pass.TA.reportError(err, E->getBeginLoc(), |
| E->getSubExpr()->getSourceRange()); |
| Stmt *parent = E; |
| do { |
| parent = StmtMap->getParentIgnoreParenImpCasts(parent); |
| } while (parent && isa<FullExpr>(parent)); |
| |
| if (ReturnStmt *retS = dyn_cast_or_null<ReturnStmt>(parent)) { |
| std::string note = "remove the cast and change return type of function " |
| "to '"; |
| note += E->getSubExpr()->getType().getAsString(Pass.Ctx.getPrintingPolicy()); |
| note += "' to have the object automatically autoreleased"; |
| Pass.TA.reportNote(note, retS->getBeginLoc()); |
| } |
| } |
| |
| Expr *subExpr = E->getSubExpr(); |
| |
| // Look through pseudo-object expressions. |
| if (PseudoObjectExpr *pseudo = dyn_cast<PseudoObjectExpr>(subExpr)) { |
| subExpr = pseudo->getResultExpr(); |
| assert(subExpr && "no result for pseudo-object of non-void type?"); |
| } |
| |
| if (ImplicitCastExpr *implCE = dyn_cast<ImplicitCastExpr>(subExpr)) { |
| if (implCE->getCastKind() == CK_ARCConsumeObject) |
| return rewriteToBridgedCast(E, OBC_BridgeRetained); |
| if (implCE->getCastKind() == CK_ARCReclaimReturnedObject) |
| return rewriteToBridgedCast(E, OBC_Bridge); |
| } |
| |
| bool isConsumed = false; |
| if (isPassedToCParamWithKnownOwnership(E, isConsumed)) |
| return rewriteToBridgedCast(E, isConsumed ? OBC_BridgeRetained |
| : OBC_Bridge); |
| } |
| |
| static ObjCMethodFamily getFamilyOfMessage(Expr *E) { |
| E = E->IgnoreParenCasts(); |
| if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E)) |
| return ME->getMethodFamily(); |
| |
| return OMF_None; |
| } |
| |
| bool isPassedToCFRetain(Expr *E, CallExpr *&callE) const { |
| if ((callE = dyn_cast_or_null<CallExpr>( |
| StmtMap->getParentIgnoreParenImpCasts(E)))) |
| if (FunctionDecl * |
| FD = dyn_cast_or_null<FunctionDecl>(callE->getCalleeDecl())) |
| if (FD->getName() == "CFRetain" && FD->getNumParams() == 1 && |
| FD->getParent()->isTranslationUnit() && |
| FD->isExternallyVisible()) |
| return true; |
| |
| return false; |
| } |
| |
| bool isPassedToCParamWithKnownOwnership(Expr *E, bool &isConsumed) const { |
| if (CallExpr *callE = dyn_cast_or_null<CallExpr>( |
| StmtMap->getParentIgnoreParenImpCasts(E))) |
| if (FunctionDecl * |
| FD = dyn_cast_or_null<FunctionDecl>(callE->getCalleeDecl())) { |
| unsigned i = 0; |
| for (unsigned e = callE->getNumArgs(); i != e; ++i) { |
| Expr *arg = callE->getArg(i); |
| if (arg == E || arg->IgnoreParenImpCasts() == E) |
| break; |
| } |
| if (i < callE->getNumArgs() && i < FD->getNumParams()) { |
| ParmVarDecl *PD = FD->getParamDecl(i); |
| if (PD->hasAttr<CFConsumedAttr>()) { |
| isConsumed = true; |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| bool isSelf(Expr *E) const { |
| E = E->IgnoreParenLValueCasts(); |
| if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) |
| if (ImplicitParamDecl *IPD = dyn_cast<ImplicitParamDecl>(DRE->getDecl())) |
| if (IPD->getIdentifier() == SelfII) |
| return true; |
| |
| return false; |
| } |
| }; |
| |
| } // end anonymous namespace |
| |
| void trans::rewriteUnbridgedCasts(MigrationPass &pass) { |
| BodyTransform<UnbridgedCastRewriter> trans(pass); |
| trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl()); |
| } |