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