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
25using namespace clang;
26using namespace arcmt;
27using namespace trans;
28
29namespace {
30
31class APIChecker : public RecursiveASTVisitor<APIChecker> {
32  MigrationPass &Pass;
33
34  Selector getReturnValueSel, setReturnValueSel;
35  Selector getArgumentSel, setArgumentSel;
36
37  Selector zoneSel;
38public:
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
70      if (selName.empty())
71        return true;
72
73      Expr *parm = E->getArg(0)->IgnoreParenCasts();
74      QualType pointee = parm->getType()->getPointeeType();
75      if (pointee.isNull())
76        return true;
77
78      if (pointee.getObjCLifetime() > Qualifiers::OCL_ExplicitNone) {
79        std::string err = "NSInvocation's ";
80        err += selName;
81        err += " is not safe to be used with an object with ownership other "
82            "than __unsafe_unretained";
83        Pass.TA.reportError(err, parm->getLocStart(), parm->getSourceRange());
84      }
85      return true;
86    }
87
88    // -zone.
89    if (E->isInstanceMessage() &&
90        E->getInstanceReceiver() &&
91        E->getSelector() == zoneSel &&
92        Pass.TA.hasDiagnostic(diag::err_unavailable,
93                              diag::err_unavailable_message,
94                              E->getSelectorLoc(0))) {
95      // Calling -zone is meaningless in ARC, change it to nil.
96      Transaction Trans(Pass.TA);
97      Pass.TA.clearDiagnostic(diag::err_unavailable,
98                              diag::err_unavailable_message,
99                              E->getSelectorLoc(0));
100      Pass.TA.replace(E->getSourceRange(), getNilString(Pass.Ctx));
101    }
102    return true;
103  }
104};
105
106} // anonymous namespace
107
108void trans::checkAPIUses(MigrationPass &pass) {
109  APIChecker(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl());
110}
111