Home | History | Annotate | Download | only in ARCMigrate
      1 //===--- TransAPIUses.cpp - Transformations 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/AST/ASTContext.h"
     23 #include "clang/Sema/SemaDiagnostic.h"
     24 
     25 using namespace clang;
     26 using namespace arcmt;
     27 using namespace trans;
     28 
     29 namespace {
     30 
     31 class APIChecker : public RecursiveASTVisitor<APIChecker> {
     32   MigrationPass &Pass;
     33 
     34   Selector getReturnValueSel, setReturnValueSel;
     35   Selector getArgumentSel, setArgumentSel;
     36 
     37   Selector zoneSel;
     38 public:
     39   APIChecker(MigrationPass &pass) : Pass(pass) {
     40     SelectorTable &sels = Pass.Ctx.Selectors;
     41     IdentifierTable &ids = Pass.Ctx.Idents;
     42     getReturnValueSel = sels.getUnarySelector(&ids.get("getReturnValue"));
     43     setReturnValueSel = sels.getUnarySelector(&ids.get("setReturnValue"));
     44 
     45     IdentifierInfo *selIds[2];
     46     selIds[0] = &ids.get("getArgument");
     47     selIds[1] = &ids.get("atIndex");
     48     getArgumentSel = sels.getSelector(2, selIds);
     49     selIds[0] = &ids.get("setArgument");
     50     setArgumentSel = sels.getSelector(2, selIds);
     51 
     52     zoneSel = sels.getNullarySelector(&ids.get("zone"));
     53   }
     54 
     55   bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
     56     // NSInvocation.
     57     if (E->isInstanceMessage() &&
     58         E->getReceiverInterface() &&
     59         E->getReceiverInterface()->getName() == "NSInvocation") {
     60       StringRef selName;
     61       if (E->getSelector() == getReturnValueSel)
     62         selName = "getReturnValue";
     63       else if (E->getSelector() == setReturnValueSel)
     64         selName = "setReturnValue";
     65       else if (E->getSelector() == getArgumentSel)
     66         selName = "getArgument";
     67       else if (E->getSelector() == setArgumentSel)
     68         selName = "setArgument";
     69       else
     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         Pass.TA.report(parm->getLocStart(),
     79                        diag::err_arcmt_nsinvocation_ownership,
     80                        parm->getSourceRange())
     81             << selName;
     82 
     83       return true;
     84     }
     85 
     86     // -zone.
     87     if (E->isInstanceMessage() &&
     88         E->getInstanceReceiver() &&
     89         E->getSelector() == zoneSel &&
     90         Pass.TA.hasDiagnostic(diag::err_unavailable,
     91                               diag::err_unavailable_message,
     92                               E->getSelectorLoc(0))) {
     93       // Calling -zone is meaningless in ARC, change it to nil.
     94       Transaction Trans(Pass.TA);
     95       Pass.TA.clearDiagnostic(diag::err_unavailable,
     96                               diag::err_unavailable_message,
     97                               E->getSelectorLoc(0));
     98       Pass.TA.replace(E->getSourceRange(), getNilString(Pass));
     99     }
    100     return true;
    101   }
    102 };
    103 
    104 } // anonymous namespace
    105 
    106 void trans::checkAPIUses(MigrationPass &pass) {
    107   APIChecker(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl());
    108 }
    109