blob: 781ad7742dccb79036134c6cf93b23e209665fdf [file] [log] [blame]
Argyrios Kyrtzidis73a0d322011-07-18 07:44:45 +00001//===--- TransAPIUses.cpp - Tranformations 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//
Argyrios Kyrtzidis93907472011-07-27 05:28:18 +000012// Emits error/fix with some API uses that are obsolete or not safe in ARC mode:
Argyrios Kyrtzidis73a0d322011-07-18 07:44:45 +000013//
14// - NSInvocation's [get/set]ReturnValue and [get/set]Argument are only safe
15// with __unsafe_unretained objects.
Argyrios Kyrtzidis91c62bf2011-07-18 07:44:50 +000016// - When a NSData's 'bytes' family of methods are used on a local var,
17// add __attribute__((objc_precise_lifetime)) to make it safer.
Argyrios Kyrtzidis93907472011-07-27 05:28:18 +000018// - Calling -zone gets replaced with 'nil'.
Argyrios Kyrtzidis73a0d322011-07-18 07:44:45 +000019//
20//===----------------------------------------------------------------------===//
21
22#include "Transforms.h"
23#include "Internals.h"
Argyrios Kyrtzidis93907472011-07-27 05:28:18 +000024#include "clang/Sema/SemaDiagnostic.h"
Argyrios Kyrtzidis73a0d322011-07-18 07:44:45 +000025
26using namespace clang;
27using namespace arcmt;
28using namespace trans;
Argyrios Kyrtzidis73a0d322011-07-18 07:44:45 +000029
30namespace {
31
32class APIChecker : public RecursiveASTVisitor<APIChecker> {
33 MigrationPass &Pass;
Argyrios Kyrtzidis91c62bf2011-07-18 07:44:50 +000034
Argyrios Kyrtzidis73a0d322011-07-18 07:44:45 +000035 Selector getReturnValueSel, setReturnValueSel;
36 Selector getArgumentSel, setArgumentSel;
37
Argyrios Kyrtzidis91c62bf2011-07-18 07:44:50 +000038 Selector bytesSel, getBytesSel, getBytesLengthSel, getBytesRangeSel;
39
Argyrios Kyrtzidis93907472011-07-27 05:28:18 +000040 Selector zoneSel;
41
Argyrios Kyrtzidis91c62bf2011-07-18 07:44:50 +000042 llvm::DenseSet<VarDecl *> ChangedNSDataVars;
Argyrios Kyrtzidis73a0d322011-07-18 07:44:45 +000043public:
44 APIChecker(MigrationPass &pass) : Pass(pass) {
45 SelectorTable &sels = Pass.Ctx.Selectors;
46 IdentifierTable &ids = Pass.Ctx.Idents;
47 getReturnValueSel = sels.getUnarySelector(&ids.get("getReturnValue"));
48 setReturnValueSel = sels.getUnarySelector(&ids.get("setReturnValue"));
49
50 IdentifierInfo *selIds[2];
51 selIds[0] = &ids.get("getArgument");
52 selIds[1] = &ids.get("atIndex");
53 getArgumentSel = sels.getSelector(2, selIds);
54 selIds[0] = &ids.get("setArgument");
55 setArgumentSel = sels.getSelector(2, selIds);
Argyrios Kyrtzidis91c62bf2011-07-18 07:44:50 +000056
57 bytesSel = sels.getNullarySelector(&ids.get("bytes"));
58 getBytesSel = sels.getUnarySelector(&ids.get("getBytes"));
59 selIds[0] = &ids.get("getBytes");
60 selIds[1] = &ids.get("length");
61 getBytesLengthSel = sels.getSelector(2, selIds);
62 selIds[1] = &ids.get("range");
63 getBytesRangeSel = sels.getSelector(2, selIds);
Argyrios Kyrtzidis93907472011-07-27 05:28:18 +000064
65 zoneSel = sels.getNullarySelector(&ids.get("zone"));
Argyrios Kyrtzidis73a0d322011-07-18 07:44:45 +000066 }
67
68 bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
Argyrios Kyrtzidis93907472011-07-27 05:28:18 +000069 // NSInvocation.
Argyrios Kyrtzidis73a0d322011-07-18 07:44:45 +000070 if (E->isInstanceMessage() &&
71 E->getReceiverInterface() &&
72 E->getReceiverInterface()->getName() == "NSInvocation") {
73 StringRef selName;
74 if (E->getSelector() == getReturnValueSel)
75 selName = "getReturnValue";
76 else if (E->getSelector() == setReturnValueSel)
77 selName = "setReturnValue";
78 else if (E->getSelector() == getArgumentSel)
79 selName = "getArgument";
80 else if (E->getSelector() == setArgumentSel)
81 selName = "setArgument";
82
83 if (selName.empty())
84 return true;
85
86 Expr *parm = E->getArg(0)->IgnoreParenCasts();
87 QualType pointee = parm->getType()->getPointeeType();
88 if (pointee.isNull())
89 return true;
90
91 if (pointee.getObjCLifetime() > Qualifiers::OCL_ExplicitNone) {
92 std::string err = "NSInvocation's ";
93 err += selName;
94 err += " is not safe to be used with an object with ownership other "
95 "than __unsafe_unretained";
96 Pass.TA.reportError(err, parm->getLocStart(), parm->getSourceRange());
97 }
98 return true;
99 }
100
Argyrios Kyrtzidis93907472011-07-27 05:28:18 +0000101 // NSData.
Argyrios Kyrtzidis91c62bf2011-07-18 07:44:50 +0000102 if (E->isInstanceMessage() &&
103 E->getReceiverInterface() &&
104 E->getReceiverInterface()->getName() == "NSData" &&
105 E->getInstanceReceiver() &&
106 (E->getSelector() == bytesSel ||
107 E->getSelector() == getBytesSel ||
108 E->getSelector() == getBytesLengthSel ||
109 E->getSelector() == getBytesRangeSel)) {
110 Expr *rec = E->getInstanceReceiver();
111 rec = rec->IgnoreParenCasts();
112 if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(rec))
113 if (VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl()))
114 if (VD->hasLocalStorage() && !ChangedNSDataVars.count(VD)) {
115 Transaction Trans(Pass.TA);
116 Pass.TA.insertAfterToken(VD->getLocation(),
117 " __attribute__((objc_precise_lifetime))");
118 ChangedNSDataVars.insert(VD);
119 }
120 }
121
Argyrios Kyrtzidis93907472011-07-27 05:28:18 +0000122 // -zone.
123 if (E->isInstanceMessage() &&
124 E->getInstanceReceiver() &&
125 E->getSelector() == zoneSel &&
126 Pass.TA.hasDiagnostic(diag::err_unavailable,
127 diag::err_unavailable_message,
128 E->getInstanceReceiver()->getExprLoc())) {
129 // Calling -zone is meaningless in ARC, change it to nil.
130 Transaction Trans(Pass.TA);
131 Pass.TA.clearDiagnostic(diag::err_unavailable,
132 diag::err_unavailable_message,
133 E->getInstanceReceiver()->getExprLoc());
134 Pass.TA.replace(E->getSourceRange(), getNilString(Pass.Ctx));
135 }
Argyrios Kyrtzidis73a0d322011-07-18 07:44:45 +0000136 return true;
137 }
138};
139
140} // anonymous namespace
141
142void trans::checkAPIUses(MigrationPass &pass) {
143 APIChecker(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl());
144}