Home | History | Annotate | Line # | Download | only in ARCMigrate
      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     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