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