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      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.Ctx));
99    }
100    return true;
101  }
102};
103
104} // anonymous namespace
105
106void trans::checkAPIUses(MigrationPass &pass) {
107  APIChecker(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl());
108}
109