1249423Sdim//===--- TransAPIUses.cpp - Transformations to ARC mode -------------------===//
2226586Sdim//
3226586Sdim//                     The LLVM Compiler Infrastructure
4226586Sdim//
5226586Sdim// This file is distributed under the University of Illinois Open Source
6226586Sdim// License. See LICENSE.TXT for details.
7226586Sdim//
8226586Sdim//===----------------------------------------------------------------------===//
9226586Sdim//
10226586Sdim// checkAPIUses:
11226586Sdim//
12226586Sdim// Emits error/fix with some API uses that are obsolete or not safe in ARC mode:
13226586Sdim//
14226586Sdim// - NSInvocation's [get/set]ReturnValue and [get/set]Argument are only safe
15226586Sdim//   with __unsafe_unretained objects.
16226586Sdim// - Calling -zone gets replaced with 'nil'.
17226586Sdim//
18226586Sdim//===----------------------------------------------------------------------===//
19226586Sdim
20226586Sdim#include "Transforms.h"
21226586Sdim#include "Internals.h"
22239462Sdim#include "clang/AST/ASTContext.h"
23226586Sdim#include "clang/Sema/SemaDiagnostic.h"
24226586Sdim
25226586Sdimusing namespace clang;
26226586Sdimusing namespace arcmt;
27226586Sdimusing namespace trans;
28226586Sdim
29226586Sdimnamespace {
30226586Sdim
31226586Sdimclass APIChecker : public RecursiveASTVisitor<APIChecker> {
32226586Sdim  MigrationPass &Pass;
33226586Sdim
34226586Sdim  Selector getReturnValueSel, setReturnValueSel;
35226586Sdim  Selector getArgumentSel, setArgumentSel;
36226586Sdim
37226586Sdim  Selector zoneSel;
38226586Sdimpublic:
39226586Sdim  APIChecker(MigrationPass &pass) : Pass(pass) {
40226586Sdim    SelectorTable &sels = Pass.Ctx.Selectors;
41226586Sdim    IdentifierTable &ids = Pass.Ctx.Idents;
42226586Sdim    getReturnValueSel = sels.getUnarySelector(&ids.get("getReturnValue"));
43226586Sdim    setReturnValueSel = sels.getUnarySelector(&ids.get("setReturnValue"));
44226586Sdim
45226586Sdim    IdentifierInfo *selIds[2];
46226586Sdim    selIds[0] = &ids.get("getArgument");
47226586Sdim    selIds[1] = &ids.get("atIndex");
48226586Sdim    getArgumentSel = sels.getSelector(2, selIds);
49226586Sdim    selIds[0] = &ids.get("setArgument");
50226586Sdim    setArgumentSel = sels.getSelector(2, selIds);
51226586Sdim
52226586Sdim    zoneSel = sels.getNullarySelector(&ids.get("zone"));
53226586Sdim  }
54226586Sdim
55226586Sdim  bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
56226586Sdim    // NSInvocation.
57226586Sdim    if (E->isInstanceMessage() &&
58226586Sdim        E->getReceiverInterface() &&
59226586Sdim        E->getReceiverInterface()->getName() == "NSInvocation") {
60226586Sdim      StringRef selName;
61226586Sdim      if (E->getSelector() == getReturnValueSel)
62226586Sdim        selName = "getReturnValue";
63226586Sdim      else if (E->getSelector() == setReturnValueSel)
64226586Sdim        selName = "setReturnValue";
65226586Sdim      else if (E->getSelector() == getArgumentSel)
66226586Sdim        selName = "getArgument";
67226586Sdim      else if (E->getSelector() == setArgumentSel)
68226586Sdim        selName = "setArgument";
69226586Sdim
70226586Sdim      if (selName.empty())
71226586Sdim        return true;
72226586Sdim
73226586Sdim      Expr *parm = E->getArg(0)->IgnoreParenCasts();
74226586Sdim      QualType pointee = parm->getType()->getPointeeType();
75226586Sdim      if (pointee.isNull())
76226586Sdim        return true;
77226586Sdim
78226586Sdim      if (pointee.getObjCLifetime() > Qualifiers::OCL_ExplicitNone) {
79226586Sdim        std::string err = "NSInvocation's ";
80226586Sdim        err += selName;
81226586Sdim        err += " is not safe to be used with an object with ownership other "
82226586Sdim            "than __unsafe_unretained";
83226586Sdim        Pass.TA.reportError(err, parm->getLocStart(), parm->getSourceRange());
84226586Sdim      }
85226586Sdim      return true;
86226586Sdim    }
87226586Sdim
88226586Sdim    // -zone.
89226586Sdim    if (E->isInstanceMessage() &&
90226586Sdim        E->getInstanceReceiver() &&
91226586Sdim        E->getSelector() == zoneSel &&
92226586Sdim        Pass.TA.hasDiagnostic(diag::err_unavailable,
93226586Sdim                              diag::err_unavailable_message,
94251662Sdim                              E->getSelectorLoc(0))) {
95226586Sdim      // Calling -zone is meaningless in ARC, change it to nil.
96226586Sdim      Transaction Trans(Pass.TA);
97226586Sdim      Pass.TA.clearDiagnostic(diag::err_unavailable,
98226586Sdim                              diag::err_unavailable_message,
99251662Sdim                              E->getSelectorLoc(0));
100226586Sdim      Pass.TA.replace(E->getSourceRange(), getNilString(Pass.Ctx));
101226586Sdim    }
102226586Sdim    return true;
103226586Sdim  }
104226586Sdim};
105226586Sdim
106226586Sdim} // anonymous namespace
107226586Sdim
108226586Sdimvoid trans::checkAPIUses(MigrationPass &pass) {
109226586Sdim  APIChecker(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl());
110226586Sdim}
111