Home | History | Annotate | Download | only in ARCMigrate
      1 //===--- TransAPIUses.cpp - Tranformations to ARC mode --------------------===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 //
     10 // checkAPIUses:
     11 //
     12 // Emits error/fix with some API uses that are obsolete or not safe in ARC mode:
     13 //
     14 // - NSInvocation's [get/set]ReturnValue and [get/set]Argument are only safe
     15 //   with __unsafe_unretained objects.
     16 // - Calling -zone gets replaced with 'nil'.
     17 //
     18 //===----------------------------------------------------------------------===//
     19 
     20 #include "Transforms.h"
     21 #include "Internals.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 
     69       if (selName.empty())
     70         return true;
     71 
     72       Expr *parm = E->getArg(0)->IgnoreParenCasts();
     73       QualType pointee = parm->getType()->getPointeeType();
     74       if (pointee.isNull())
     75         return true;
     76 
     77       if (pointee.getObjCLifetime() > Qualifiers::OCL_ExplicitNone) {
     78         std::string err = "NSInvocation's ";
     79         err += selName;
     80         err += " is not safe to be used with an object with ownership other "
     81             "than __unsafe_unretained";
     82         Pass.TA.reportError(err, parm->getLocStart(), parm->getSourceRange());
     83       }
     84       return true;
     85     }
     86 
     87     // -zone.
     88     if (E->isInstanceMessage() &&
     89         E->getInstanceReceiver() &&
     90         E->getSelector() == zoneSel &&
     91         Pass.TA.hasDiagnostic(diag::err_unavailable,
     92                               diag::err_unavailable_message,
     93                               E->getInstanceReceiver()->getExprLoc())) {
     94       // Calling -zone is meaningless in ARC, change it to nil.
     95       Transaction Trans(Pass.TA);
     96       Pass.TA.clearDiagnostic(diag::err_unavailable,
     97                               diag::err_unavailable_message,
     98                               E->getInstanceReceiver()->getExprLoc());
     99       Pass.TA.replace(E->getSourceRange(), getNilString(Pass.Ctx));
    100     }
    101     return true;
    102   }
    103 };
    104 
    105 } // anonymous namespace
    106 
    107 void trans::checkAPIUses(MigrationPass &pass) {
    108   APIChecker(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl());
    109 }
    110