1 //===--- TransAPIUses.cpp - Transformations to ARC mode -------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // checkAPIUses: 10 // 11 // Emits error/fix with some API uses that are obsolete or not safe in ARC mode: 12 // 13 // - NSInvocation's [get/set]ReturnValue and [get/set]Argument are only safe 14 // with __unsafe_unretained objects. 15 // - Calling -zone gets replaced with 'nil'. 16 // 17 //===----------------------------------------------------------------------===// 18 19 #include "Transforms.h" 20 #include "Internals.h" 21 #include "clang/AST/ASTContext.h" 22 #include "clang/Sema/SemaDiagnostic.h" 23 24 using namespace clang; 25 using namespace arcmt; 26 using namespace trans; 27 28 namespace { 29 30 class APIChecker : public RecursiveASTVisitor<APIChecker> { 31 MigrationPass &Pass; 32 33 Selector getReturnValueSel, setReturnValueSel; 34 Selector getArgumentSel, setArgumentSel; 35 36 Selector zoneSel; 37 public: 38 APIChecker(MigrationPass &pass) : Pass(pass) { 39 SelectorTable &sels = Pass.Ctx.Selectors; 40 IdentifierTable &ids = Pass.Ctx.Idents; 41 getReturnValueSel = sels.getUnarySelector(&ids.get("getReturnValue")); 42 setReturnValueSel = sels.getUnarySelector(&ids.get("setReturnValue")); 43 44 const IdentifierInfo *selIds[2]; 45 selIds[0] = &ids.get("getArgument"); 46 selIds[1] = &ids.get("atIndex"); 47 getArgumentSel = sels.getSelector(2, selIds); 48 selIds[0] = &ids.get("setArgument"); 49 setArgumentSel = sels.getSelector(2, selIds); 50 51 zoneSel = sels.getNullarySelector(&ids.get("zone")); 52 } 53 54 bool VisitObjCMessageExpr(ObjCMessageExpr *E) { 55 // NSInvocation. 56 if (E->isInstanceMessage() && 57 E->getReceiverInterface() && 58 E->getReceiverInterface()->getName() == "NSInvocation") { 59 StringRef selName; 60 if (E->getSelector() == getReturnValueSel) 61 selName = "getReturnValue"; 62 else if (E->getSelector() == setReturnValueSel) 63 selName = "setReturnValue"; 64 else if (E->getSelector() == getArgumentSel) 65 selName = "getArgument"; 66 else if (E->getSelector() == setArgumentSel) 67 selName = "setArgument"; 68 else 69 return true; 70 71 Expr *parm = E->getArg(0)->IgnoreParenCasts(); 72 QualType pointee = parm->getType()->getPointeeType(); 73 if (pointee.isNull()) 74 return true; 75 76 if (pointee.getObjCLifetime() > Qualifiers::OCL_ExplicitNone) 77 Pass.TA.report(parm->getBeginLoc(), 78 diag::err_arcmt_nsinvocation_ownership, 79 parm->getSourceRange()) 80 << selName; 81 82 return true; 83 } 84 85 // -zone. 86 if (E->isInstanceMessage() && 87 E->getInstanceReceiver() && 88 E->getSelector() == zoneSel && 89 Pass.TA.hasDiagnostic(diag::err_unavailable, 90 diag::err_unavailable_message, 91 E->getSelectorLoc(0))) { 92 // Calling -zone is meaningless in ARC, change it to nil. 93 Transaction Trans(Pass.TA); 94 Pass.TA.clearDiagnostic(diag::err_unavailable, 95 diag::err_unavailable_message, 96 E->getSelectorLoc(0)); 97 Pass.TA.replace(E->getSourceRange(), getNilString(Pass)); 98 } 99 return true; 100 } 101 }; 102 103 } // anonymous namespace 104 105 void trans::checkAPIUses(MigrationPass &pass) { 106 APIChecker(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl()); 107 } 108