1*7330f729Sjoerg //===--- TransAPIUses.cpp - Transformations to ARC mode -------------------===//
2*7330f729Sjoerg //
3*7330f729Sjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*7330f729Sjoerg // See https://llvm.org/LICENSE.txt for license information.
5*7330f729Sjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*7330f729Sjoerg //
7*7330f729Sjoerg //===----------------------------------------------------------------------===//
8*7330f729Sjoerg //
9*7330f729Sjoerg // checkAPIUses:
10*7330f729Sjoerg //
11*7330f729Sjoerg // Emits error/fix with some API uses that are obsolete or not safe in ARC mode:
12*7330f729Sjoerg //
13*7330f729Sjoerg // - NSInvocation's [get/set]ReturnValue and [get/set]Argument are only safe
14*7330f729Sjoerg // with __unsafe_unretained objects.
15*7330f729Sjoerg // - Calling -zone gets replaced with 'nil'.
16*7330f729Sjoerg //
17*7330f729Sjoerg //===----------------------------------------------------------------------===//
18*7330f729Sjoerg
19*7330f729Sjoerg #include "Transforms.h"
20*7330f729Sjoerg #include "Internals.h"
21*7330f729Sjoerg #include "clang/AST/ASTContext.h"
22*7330f729Sjoerg #include "clang/Sema/SemaDiagnostic.h"
23*7330f729Sjoerg
24*7330f729Sjoerg using namespace clang;
25*7330f729Sjoerg using namespace arcmt;
26*7330f729Sjoerg using namespace trans;
27*7330f729Sjoerg
28*7330f729Sjoerg namespace {
29*7330f729Sjoerg
30*7330f729Sjoerg class APIChecker : public RecursiveASTVisitor<APIChecker> {
31*7330f729Sjoerg MigrationPass &Pass;
32*7330f729Sjoerg
33*7330f729Sjoerg Selector getReturnValueSel, setReturnValueSel;
34*7330f729Sjoerg Selector getArgumentSel, setArgumentSel;
35*7330f729Sjoerg
36*7330f729Sjoerg Selector zoneSel;
37*7330f729Sjoerg public:
APIChecker(MigrationPass & pass)38*7330f729Sjoerg APIChecker(MigrationPass &pass) : Pass(pass) {
39*7330f729Sjoerg SelectorTable &sels = Pass.Ctx.Selectors;
40*7330f729Sjoerg IdentifierTable &ids = Pass.Ctx.Idents;
41*7330f729Sjoerg getReturnValueSel = sels.getUnarySelector(&ids.get("getReturnValue"));
42*7330f729Sjoerg setReturnValueSel = sels.getUnarySelector(&ids.get("setReturnValue"));
43*7330f729Sjoerg
44*7330f729Sjoerg IdentifierInfo *selIds[2];
45*7330f729Sjoerg selIds[0] = &ids.get("getArgument");
46*7330f729Sjoerg selIds[1] = &ids.get("atIndex");
47*7330f729Sjoerg getArgumentSel = sels.getSelector(2, selIds);
48*7330f729Sjoerg selIds[0] = &ids.get("setArgument");
49*7330f729Sjoerg setArgumentSel = sels.getSelector(2, selIds);
50*7330f729Sjoerg
51*7330f729Sjoerg zoneSel = sels.getNullarySelector(&ids.get("zone"));
52*7330f729Sjoerg }
53*7330f729Sjoerg
VisitObjCMessageExpr(ObjCMessageExpr * E)54*7330f729Sjoerg bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
55*7330f729Sjoerg // NSInvocation.
56*7330f729Sjoerg if (E->isInstanceMessage() &&
57*7330f729Sjoerg E->getReceiverInterface() &&
58*7330f729Sjoerg E->getReceiverInterface()->getName() == "NSInvocation") {
59*7330f729Sjoerg StringRef selName;
60*7330f729Sjoerg if (E->getSelector() == getReturnValueSel)
61*7330f729Sjoerg selName = "getReturnValue";
62*7330f729Sjoerg else if (E->getSelector() == setReturnValueSel)
63*7330f729Sjoerg selName = "setReturnValue";
64*7330f729Sjoerg else if (E->getSelector() == getArgumentSel)
65*7330f729Sjoerg selName = "getArgument";
66*7330f729Sjoerg else if (E->getSelector() == setArgumentSel)
67*7330f729Sjoerg selName = "setArgument";
68*7330f729Sjoerg else
69*7330f729Sjoerg return true;
70*7330f729Sjoerg
71*7330f729Sjoerg Expr *parm = E->getArg(0)->IgnoreParenCasts();
72*7330f729Sjoerg QualType pointee = parm->getType()->getPointeeType();
73*7330f729Sjoerg if (pointee.isNull())
74*7330f729Sjoerg return true;
75*7330f729Sjoerg
76*7330f729Sjoerg if (pointee.getObjCLifetime() > Qualifiers::OCL_ExplicitNone)
77*7330f729Sjoerg Pass.TA.report(parm->getBeginLoc(),
78*7330f729Sjoerg diag::err_arcmt_nsinvocation_ownership,
79*7330f729Sjoerg parm->getSourceRange())
80*7330f729Sjoerg << selName;
81*7330f729Sjoerg
82*7330f729Sjoerg return true;
83*7330f729Sjoerg }
84*7330f729Sjoerg
85*7330f729Sjoerg // -zone.
86*7330f729Sjoerg if (E->isInstanceMessage() &&
87*7330f729Sjoerg E->getInstanceReceiver() &&
88*7330f729Sjoerg E->getSelector() == zoneSel &&
89*7330f729Sjoerg Pass.TA.hasDiagnostic(diag::err_unavailable,
90*7330f729Sjoerg diag::err_unavailable_message,
91*7330f729Sjoerg E->getSelectorLoc(0))) {
92*7330f729Sjoerg // Calling -zone is meaningless in ARC, change it to nil.
93*7330f729Sjoerg Transaction Trans(Pass.TA);
94*7330f729Sjoerg Pass.TA.clearDiagnostic(diag::err_unavailable,
95*7330f729Sjoerg diag::err_unavailable_message,
96*7330f729Sjoerg E->getSelectorLoc(0));
97*7330f729Sjoerg Pass.TA.replace(E->getSourceRange(), getNilString(Pass));
98*7330f729Sjoerg }
99*7330f729Sjoerg return true;
100*7330f729Sjoerg }
101*7330f729Sjoerg };
102*7330f729Sjoerg
103*7330f729Sjoerg } // anonymous namespace
104*7330f729Sjoerg
checkAPIUses(MigrationPass & pass)105*7330f729Sjoerg void trans::checkAPIUses(MigrationPass &pass) {
106*7330f729Sjoerg APIChecker(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl());
107*7330f729Sjoerg }
108