Path: blob/main/contrib/llvm-project/clang/lib/ARCMigrate/TransUnbridgedCasts.cpp
35236 views
//===--- TransUnbridgedCasts.cpp - Transformations to ARC mode ------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// rewriteUnbridgedCasts:9//10// A cast of non-objc pointer to an objc one is checked. If the non-objc pointer11// is from a file-level variable, __bridge cast is used to convert it.12// For the result of a function call that we know is +1/+0,13// __bridge/CFBridgingRelease is used.14//15// NSString *str = (NSString *)kUTTypePlainText;16// str = b ? kUTTypeRTF : kUTTypePlainText;17// NSString *_uuidString = (NSString *)CFUUIDCreateString(kCFAllocatorDefault,18// _uuid);19// ---->20// NSString *str = (__bridge NSString *)kUTTypePlainText;21// str = (__bridge NSString *)(b ? kUTTypeRTF : kUTTypePlainText);22// NSString *_uuidString = (NSString *)23// CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, _uuid));24//25// For a C pointer to ObjC, for casting 'self', __bridge is used.26//27// CFStringRef str = (CFStringRef)self;28// ---->29// CFStringRef str = (__bridge CFStringRef)self;30//31// Uses of Block_copy/Block_release macros are rewritten:32//33// c = Block_copy(b);34// Block_release(c);35// ---->36// c = [b copy];37// <removed>38//39//===----------------------------------------------------------------------===//4041#include "Transforms.h"42#include "Internals.h"43#include "clang/AST/ASTContext.h"44#include "clang/AST/Attr.h"45#include "clang/AST/ParentMap.h"46#include "clang/Analysis/DomainSpecific/CocoaConventions.h"47#include "clang/Basic/SourceManager.h"48#include "clang/Lex/Lexer.h"49#include "clang/Sema/SemaDiagnostic.h"50#include "llvm/ADT/SmallString.h"5152using namespace clang;53using namespace arcmt;54using namespace trans;5556namespace {5758class UnbridgedCastRewriter : public RecursiveASTVisitor<UnbridgedCastRewriter>{59MigrationPass &Pass;60IdentifierInfo *SelfII;61std::unique_ptr<ParentMap> StmtMap;62Decl *ParentD;63Stmt *Body;64mutable std::unique_ptr<ExprSet> Removables;6566public:67UnbridgedCastRewriter(MigrationPass &pass)68: Pass(pass), ParentD(nullptr), Body(nullptr) {69SelfII = &Pass.Ctx.Idents.get("self");70}7172void transformBody(Stmt *body, Decl *ParentD) {73this->ParentD = ParentD;74Body = body;75StmtMap.reset(new ParentMap(body));76TraverseStmt(body);77}7879bool TraverseBlockDecl(BlockDecl *D) {80// ParentMap does not enter into a BlockDecl to record its stmts, so use a81// new UnbridgedCastRewriter to handle the block.82UnbridgedCastRewriter(Pass).transformBody(D->getBody(), D);83return true;84}8586bool VisitCastExpr(CastExpr *E) {87if (E->getCastKind() != CK_CPointerToObjCPointerCast &&88E->getCastKind() != CK_BitCast &&89E->getCastKind() != CK_AnyPointerToBlockPointerCast)90return true;9192QualType castType = E->getType();93Expr *castExpr = E->getSubExpr();94QualType castExprType = castExpr->getType();9596if (castType->isObjCRetainableType() == castExprType->isObjCRetainableType())97return true;9899bool exprRetainable = castExprType->isObjCIndirectLifetimeType();100bool castRetainable = castType->isObjCIndirectLifetimeType();101if (exprRetainable == castRetainable) return true;102103if (castExpr->isNullPointerConstant(Pass.Ctx,104Expr::NPC_ValueDependentIsNull))105return true;106107SourceLocation loc = castExpr->getExprLoc();108if (loc.isValid() && Pass.Ctx.getSourceManager().isInSystemHeader(loc))109return true;110111if (castType->isObjCRetainableType())112transformNonObjCToObjCCast(E);113else114transformObjCToNonObjCCast(E);115116return true;117}118119private:120void transformNonObjCToObjCCast(CastExpr *E) {121if (!E) return;122123// Global vars are assumed that are cast as unretained.124if (isGlobalVar(E))125if (E->getSubExpr()->getType()->isPointerType()) {126castToObjCObject(E, /*retained=*/false);127return;128}129130// If the cast is directly over the result of a Core Foundation function131// try to figure out whether it should be cast as retained or unretained.132Expr *inner = E->IgnoreParenCasts();133if (CallExpr *callE = dyn_cast<CallExpr>(inner)) {134if (FunctionDecl *FD = callE->getDirectCallee()) {135if (FD->hasAttr<CFReturnsRetainedAttr>()) {136castToObjCObject(E, /*retained=*/true);137return;138}139if (FD->hasAttr<CFReturnsNotRetainedAttr>()) {140castToObjCObject(E, /*retained=*/false);141return;142}143if (FD->isGlobal() &&144FD->getIdentifier() &&145ento::cocoa::isRefType(E->getSubExpr()->getType(), "CF",146FD->getIdentifier()->getName())) {147StringRef fname = FD->getIdentifier()->getName();148if (fname.ends_with("Retain") || fname.contains("Create") ||149fname.contains("Copy")) {150// Do not migrate to couple of bridge transfer casts which151// cancel each other out. Leave it unchanged so error gets user152// attention instead.153if (FD->getName() == "CFRetain" &&154FD->getNumParams() == 1 &&155FD->getParent()->isTranslationUnit() &&156FD->isExternallyVisible()) {157Expr *Arg = callE->getArg(0);158if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) {159const Expr *sub = ICE->getSubExpr();160QualType T = sub->getType();161if (T->isObjCObjectPointerType())162return;163}164}165castToObjCObject(E, /*retained=*/true);166return;167}168169if (fname.contains("Get")) {170castToObjCObject(E, /*retained=*/false);171return;172}173}174}175}176177// If returning an ivar or a member of an ivar from a +0 method, use178// a __bridge cast.179Expr *base = inner->IgnoreParenImpCasts();180while (isa<MemberExpr>(base))181base = cast<MemberExpr>(base)->getBase()->IgnoreParenImpCasts();182if (isa<ObjCIvarRefExpr>(base) &&183isa<ReturnStmt>(StmtMap->getParentIgnoreParenCasts(E))) {184if (ObjCMethodDecl *method = dyn_cast_or_null<ObjCMethodDecl>(ParentD)) {185if (!method->hasAttr<NSReturnsRetainedAttr>()) {186castToObjCObject(E, /*retained=*/false);187return;188}189}190}191}192193void castToObjCObject(CastExpr *E, bool retained) {194rewriteToBridgedCast(E, retained ? OBC_BridgeTransfer : OBC_Bridge);195}196197void rewriteToBridgedCast(CastExpr *E, ObjCBridgeCastKind Kind) {198Transaction Trans(Pass.TA);199rewriteToBridgedCast(E, Kind, Trans);200}201202void rewriteToBridgedCast(CastExpr *E, ObjCBridgeCastKind Kind,203Transaction &Trans) {204TransformActions &TA = Pass.TA;205206// We will remove the compiler diagnostic.207if (!TA.hasDiagnostic(diag::err_arc_mismatched_cast,208diag::err_arc_cast_requires_bridge,209E->getBeginLoc())) {210Trans.abort();211return;212}213214StringRef bridge;215switch(Kind) {216case OBC_Bridge:217bridge = "__bridge "; break;218case OBC_BridgeTransfer:219bridge = "__bridge_transfer "; break;220case OBC_BridgeRetained:221bridge = "__bridge_retained "; break;222}223224TA.clearDiagnostic(diag::err_arc_mismatched_cast,225diag::err_arc_cast_requires_bridge, E->getBeginLoc());226if (Kind == OBC_Bridge || !Pass.CFBridgingFunctionsDefined()) {227if (CStyleCastExpr *CCE = dyn_cast<CStyleCastExpr>(E)) {228TA.insertAfterToken(CCE->getLParenLoc(), bridge);229} else {230SourceLocation insertLoc = E->getSubExpr()->getBeginLoc();231SmallString<128> newCast;232newCast += '(';233newCast += bridge;234newCast += E->getType().getAsString(Pass.Ctx.getPrintingPolicy());235newCast += ')';236237if (isa<ParenExpr>(E->getSubExpr())) {238TA.insert(insertLoc, newCast.str());239} else {240newCast += '(';241TA.insert(insertLoc, newCast.str());242TA.insertAfterToken(E->getEndLoc(), ")");243}244}245} else {246assert(Kind == OBC_BridgeTransfer || Kind == OBC_BridgeRetained);247SmallString<32> BridgeCall;248249Expr *WrapE = E->getSubExpr();250SourceLocation InsertLoc = WrapE->getBeginLoc();251252SourceManager &SM = Pass.Ctx.getSourceManager();253char PrevChar = *SM.getCharacterData(InsertLoc.getLocWithOffset(-1));254if (Lexer::isAsciiIdentifierContinueChar(PrevChar,255Pass.Ctx.getLangOpts()))256BridgeCall += ' ';257258if (Kind == OBC_BridgeTransfer)259BridgeCall += "CFBridgingRelease";260else261BridgeCall += "CFBridgingRetain";262263if (isa<ParenExpr>(WrapE)) {264TA.insert(InsertLoc, BridgeCall);265} else {266BridgeCall += '(';267TA.insert(InsertLoc, BridgeCall);268TA.insertAfterToken(WrapE->getEndLoc(), ")");269}270}271}272273void rewriteCastForCFRetain(CastExpr *castE, CallExpr *callE) {274Transaction Trans(Pass.TA);275Pass.TA.replace(callE->getSourceRange(), callE->getArg(0)->getSourceRange());276rewriteToBridgedCast(castE, OBC_BridgeRetained, Trans);277}278279void getBlockMacroRanges(CastExpr *E, SourceRange &Outer, SourceRange &Inner) {280SourceManager &SM = Pass.Ctx.getSourceManager();281SourceLocation Loc = E->getExprLoc();282assert(Loc.isMacroID());283CharSourceRange MacroRange = SM.getImmediateExpansionRange(Loc);284SourceRange SubRange = E->getSubExpr()->IgnoreParenImpCasts()->getSourceRange();285SourceLocation InnerBegin = SM.getImmediateMacroCallerLoc(SubRange.getBegin());286SourceLocation InnerEnd = SM.getImmediateMacroCallerLoc(SubRange.getEnd());287288Outer = MacroRange.getAsRange();289Inner = SourceRange(InnerBegin, InnerEnd);290}291292void rewriteBlockCopyMacro(CastExpr *E) {293SourceRange OuterRange, InnerRange;294getBlockMacroRanges(E, OuterRange, InnerRange);295296Transaction Trans(Pass.TA);297Pass.TA.replace(OuterRange, InnerRange);298Pass.TA.insert(InnerRange.getBegin(), "[");299Pass.TA.insertAfterToken(InnerRange.getEnd(), " copy]");300Pass.TA.clearDiagnostic(diag::err_arc_mismatched_cast,301diag::err_arc_cast_requires_bridge,302OuterRange);303}304305void removeBlockReleaseMacro(CastExpr *E) {306SourceRange OuterRange, InnerRange;307getBlockMacroRanges(E, OuterRange, InnerRange);308309Transaction Trans(Pass.TA);310Pass.TA.clearDiagnostic(diag::err_arc_mismatched_cast,311diag::err_arc_cast_requires_bridge,312OuterRange);313if (!hasSideEffects(E, Pass.Ctx)) {314if (tryRemoving(cast<Expr>(StmtMap->getParentIgnoreParenCasts(E))))315return;316}317Pass.TA.replace(OuterRange, InnerRange);318}319320bool tryRemoving(Expr *E) const {321if (!Removables) {322Removables.reset(new ExprSet);323collectRemovables(Body, *Removables);324}325326if (Removables->count(E)) {327Pass.TA.removeStmt(E);328return true;329}330331return false;332}333334void transformObjCToNonObjCCast(CastExpr *E) {335SourceLocation CastLoc = E->getExprLoc();336if (CastLoc.isMacroID()) {337StringRef MacroName = Lexer::getImmediateMacroName(CastLoc,338Pass.Ctx.getSourceManager(),339Pass.Ctx.getLangOpts());340if (MacroName == "Block_copy") {341rewriteBlockCopyMacro(E);342return;343}344if (MacroName == "Block_release") {345removeBlockReleaseMacro(E);346return;347}348}349350if (isSelf(E->getSubExpr()))351return rewriteToBridgedCast(E, OBC_Bridge);352353CallExpr *callE;354if (isPassedToCFRetain(E, callE))355return rewriteCastForCFRetain(E, callE);356357ObjCMethodFamily family = getFamilyOfMessage(E->getSubExpr());358if (family == OMF_retain)359return rewriteToBridgedCast(E, OBC_BridgeRetained);360361if (family == OMF_autorelease || family == OMF_release) {362std::string err = "it is not safe to cast to '";363err += E->getType().getAsString(Pass.Ctx.getPrintingPolicy());364err += "' the result of '";365err += family == OMF_autorelease ? "autorelease" : "release";366err += "' message; a __bridge cast may result in a pointer to a "367"destroyed object and a __bridge_retained may leak the object";368Pass.TA.reportError(err, E->getBeginLoc(),369E->getSubExpr()->getSourceRange());370Stmt *parent = E;371do {372parent = StmtMap->getParentIgnoreParenImpCasts(parent);373} while (isa_and_nonnull<FullExpr>(parent));374375if (ReturnStmt *retS = dyn_cast_or_null<ReturnStmt>(parent)) {376std::string note = "remove the cast and change return type of function "377"to '";378note += E->getSubExpr()->getType().getAsString(Pass.Ctx.getPrintingPolicy());379note += "' to have the object automatically autoreleased";380Pass.TA.reportNote(note, retS->getBeginLoc());381}382}383384Expr *subExpr = E->getSubExpr();385386// Look through pseudo-object expressions.387if (PseudoObjectExpr *pseudo = dyn_cast<PseudoObjectExpr>(subExpr)) {388subExpr = pseudo->getResultExpr();389assert(subExpr && "no result for pseudo-object of non-void type?");390}391392if (ImplicitCastExpr *implCE = dyn_cast<ImplicitCastExpr>(subExpr)) {393if (implCE->getCastKind() == CK_ARCConsumeObject)394return rewriteToBridgedCast(E, OBC_BridgeRetained);395if (implCE->getCastKind() == CK_ARCReclaimReturnedObject)396return rewriteToBridgedCast(E, OBC_Bridge);397}398399bool isConsumed = false;400if (isPassedToCParamWithKnownOwnership(E, isConsumed))401return rewriteToBridgedCast(E, isConsumed ? OBC_BridgeRetained402: OBC_Bridge);403}404405static ObjCMethodFamily getFamilyOfMessage(Expr *E) {406E = E->IgnoreParenCasts();407if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E))408return ME->getMethodFamily();409410return OMF_None;411}412413bool isPassedToCFRetain(Expr *E, CallExpr *&callE) const {414if ((callE = dyn_cast_or_null<CallExpr>(415StmtMap->getParentIgnoreParenImpCasts(E))))416if (FunctionDecl *417FD = dyn_cast_or_null<FunctionDecl>(callE->getCalleeDecl()))418if (FD->getName() == "CFRetain" && FD->getNumParams() == 1 &&419FD->getParent()->isTranslationUnit() &&420FD->isExternallyVisible())421return true;422423return false;424}425426bool isPassedToCParamWithKnownOwnership(Expr *E, bool &isConsumed) const {427if (CallExpr *callE = dyn_cast_or_null<CallExpr>(428StmtMap->getParentIgnoreParenImpCasts(E)))429if (FunctionDecl *430FD = dyn_cast_or_null<FunctionDecl>(callE->getCalleeDecl())) {431unsigned i = 0;432for (unsigned e = callE->getNumArgs(); i != e; ++i) {433Expr *arg = callE->getArg(i);434if (arg == E || arg->IgnoreParenImpCasts() == E)435break;436}437if (i < callE->getNumArgs() && i < FD->getNumParams()) {438ParmVarDecl *PD = FD->getParamDecl(i);439if (PD->hasAttr<CFConsumedAttr>()) {440isConsumed = true;441return true;442}443}444}445446return false;447}448449bool isSelf(Expr *E) const {450E = E->IgnoreParenLValueCasts();451if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))452if (ImplicitParamDecl *IPD = dyn_cast<ImplicitParamDecl>(DRE->getDecl()))453if (IPD->getIdentifier() == SelfII)454return true;455456return false;457}458};459460} // end anonymous namespace461462void trans::rewriteUnbridgedCasts(MigrationPass &pass) {463BodyTransform<UnbridgedCastRewriter> trans(pass);464trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl());465}466467468