blob: 0e7b8779941cd831a504df2e541ba3f42fa21557 [file] [log] [blame]
Ted Kremenekf7639e12012-03-06 20:06:33 +00001//===--- RewriteObjCFoundationAPI.cpp - Foundation API Rewriter -----------===//
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// Rewrites legacy method calls to modern syntax.
11//
12//===----------------------------------------------------------------------===//
13
14#include "clang/Edit/Rewriters.h"
15#include "clang/Edit/Commit.h"
16#include "clang/Lex/Lexer.h"
Benjamin Kramer1ea8e092012-07-04 17:04:04 +000017#include "clang/AST/ASTContext.h"
Ted Kremenekf7639e12012-03-06 20:06:33 +000018#include "clang/AST/ExprObjC.h"
19#include "clang/AST/ExprCXX.h"
20#include "clang/AST/NSAPI.h"
21
22using namespace clang;
23using namespace edit;
24
25static bool checkForLiteralCreation(const ObjCMessageExpr *Msg,
Argyrios Kyrtzidis63aebfb2012-06-06 22:23:12 +000026 IdentifierInfo *&ClassId,
27 const LangOptions &LangOpts) {
Ted Kremenekf7639e12012-03-06 20:06:33 +000028 if (!Msg || Msg->isImplicit() || !Msg->getMethodDecl())
29 return false;
30
31 const ObjCInterfaceDecl *Receiver = Msg->getReceiverInterface();
32 if (!Receiver)
33 return false;
34 ClassId = Receiver->getIdentifier();
35
36 if (Msg->getReceiverKind() == ObjCMessageExpr::Class)
37 return true;
38
Argyrios Kyrtzidis63aebfb2012-06-06 22:23:12 +000039 // When in ARC mode we also convert "[[.. alloc] init]" messages to literals,
40 // since the change from +1 to +0 will be handled fine by ARC.
41 if (LangOpts.ObjCAutoRefCount) {
42 if (Msg->getReceiverKind() == ObjCMessageExpr::Instance) {
43 if (const ObjCMessageExpr *Rec = dyn_cast<ObjCMessageExpr>(
44 Msg->getInstanceReceiver()->IgnoreParenImpCasts())) {
45 if (Rec->getMethodFamily() == OMF_alloc)
46 return true;
47 }
48 }
49 }
50
Ted Kremenekf7639e12012-03-06 20:06:33 +000051 return false;
52}
53
54//===----------------------------------------------------------------------===//
55// rewriteObjCRedundantCallWithLiteral.
56//===----------------------------------------------------------------------===//
57
58bool edit::rewriteObjCRedundantCallWithLiteral(const ObjCMessageExpr *Msg,
59 const NSAPI &NS, Commit &commit) {
60 IdentifierInfo *II = 0;
Argyrios Kyrtzidis63aebfb2012-06-06 22:23:12 +000061 if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
Ted Kremenekf7639e12012-03-06 20:06:33 +000062 return false;
63 if (Msg->getNumArgs() != 1)
64 return false;
65
66 const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts();
67 Selector Sel = Msg->getSelector();
68
69 if ((isa<ObjCStringLiteral>(Arg) &&
70 NS.getNSClassId(NSAPI::ClassId_NSString) == II &&
Argyrios Kyrtzidis63aebfb2012-06-06 22:23:12 +000071 (NS.getNSStringSelector(NSAPI::NSStr_stringWithString) == Sel ||
72 NS.getNSStringSelector(NSAPI::NSStr_initWithString) == Sel)) ||
Ted Kremenekf7639e12012-03-06 20:06:33 +000073
74 (isa<ObjCArrayLiteral>(Arg) &&
75 NS.getNSClassId(NSAPI::ClassId_NSArray) == II &&
Argyrios Kyrtzidis63aebfb2012-06-06 22:23:12 +000076 (NS.getNSArraySelector(NSAPI::NSArr_arrayWithArray) == Sel ||
77 NS.getNSArraySelector(NSAPI::NSArr_initWithArray) == Sel)) ||
Ted Kremenekf7639e12012-03-06 20:06:33 +000078
79 (isa<ObjCDictionaryLiteral>(Arg) &&
80 NS.getNSClassId(NSAPI::ClassId_NSDictionary) == II &&
Argyrios Kyrtzidis63aebfb2012-06-06 22:23:12 +000081 (NS.getNSDictionarySelector(
82 NSAPI::NSDict_dictionaryWithDictionary) == Sel ||
83 NS.getNSDictionarySelector(NSAPI::NSDict_initWithDictionary) == Sel))) {
Ted Kremenekf7639e12012-03-06 20:06:33 +000084
85 commit.replaceWithInner(Msg->getSourceRange(),
86 Msg->getArg(0)->getSourceRange());
87 return true;
88 }
89
90 return false;
91}
92
93//===----------------------------------------------------------------------===//
94// rewriteToObjCSubscriptSyntax.
95//===----------------------------------------------------------------------===//
96
Argyrios Kyrtzidis0bbe94f2012-05-14 23:33:49 +000097static bool subscriptOperatorNeedsParens(const Expr *FullExpr);
98
Ted Kremenekf7639e12012-03-06 20:06:33 +000099static void maybePutParensOnReceiver(const Expr *Receiver, Commit &commit) {
Argyrios Kyrtzidis0bbe94f2012-05-14 23:33:49 +0000100 if (subscriptOperatorNeedsParens(Receiver)) {
Ted Kremenekf7639e12012-03-06 20:06:33 +0000101 SourceRange RecRange = Receiver->getSourceRange();
102 commit.insertWrap("(", RecRange, ")");
103 }
104}
105
Argyrios Kyrtzidis6310fdd2012-06-04 21:23:26 +0000106static bool rewriteToSubscriptGetCommon(const ObjCMessageExpr *Msg,
107 Commit &commit) {
Ted Kremenekf7639e12012-03-06 20:06:33 +0000108 if (Msg->getNumArgs() != 1)
109 return false;
110 const Expr *Rec = Msg->getInstanceReceiver();
111 if (!Rec)
112 return false;
113
114 SourceRange MsgRange = Msg->getSourceRange();
115 SourceRange RecRange = Rec->getSourceRange();
116 SourceRange ArgRange = Msg->getArg(0)->getSourceRange();
117
118 commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
119 ArgRange.getBegin()),
120 CharSourceRange::getTokenRange(RecRange));
121 commit.replaceWithInner(SourceRange(ArgRange.getBegin(), MsgRange.getEnd()),
122 ArgRange);
123 commit.insertWrap("[", ArgRange, "]");
124 maybePutParensOnReceiver(Rec, commit);
125 return true;
126}
127
Argyrios Kyrtzidis6310fdd2012-06-04 21:23:26 +0000128static bool rewriteToArraySubscriptGet(const ObjCInterfaceDecl *IFace,
129 const ObjCMessageExpr *Msg,
130 const NSAPI &NS,
131 Commit &commit) {
Argyrios Kyrtzidisa3fcbeb2012-06-19 02:22:02 +0000132 if (!IFace->lookupInstanceMethod(NS.getObjectAtIndexedSubscriptSelector()))
Argyrios Kyrtzidis6310fdd2012-06-04 21:23:26 +0000133 return false;
134 return rewriteToSubscriptGetCommon(Msg, commit);
135}
136
137static bool rewriteToDictionarySubscriptGet(const ObjCInterfaceDecl *IFace,
138 const ObjCMessageExpr *Msg,
139 const NSAPI &NS,
140 Commit &commit) {
Argyrios Kyrtzidisa3fcbeb2012-06-19 02:22:02 +0000141 if (!IFace->lookupInstanceMethod(NS.getObjectForKeyedSubscriptSelector()))
Argyrios Kyrtzidis6310fdd2012-06-04 21:23:26 +0000142 return false;
143 return rewriteToSubscriptGetCommon(Msg, commit);
144}
145
146static bool rewriteToArraySubscriptSet(const ObjCInterfaceDecl *IFace,
147 const ObjCMessageExpr *Msg,
148 const NSAPI &NS,
Ted Kremenekf7639e12012-03-06 20:06:33 +0000149 Commit &commit) {
150 if (Msg->getNumArgs() != 2)
151 return false;
152 const Expr *Rec = Msg->getInstanceReceiver();
153 if (!Rec)
154 return false;
Argyrios Kyrtzidisa3fcbeb2012-06-19 02:22:02 +0000155 if (!IFace->lookupInstanceMethod(NS.getSetObjectAtIndexedSubscriptSelector()))
Argyrios Kyrtzidis6310fdd2012-06-04 21:23:26 +0000156 return false;
Ted Kremenekf7639e12012-03-06 20:06:33 +0000157
158 SourceRange MsgRange = Msg->getSourceRange();
159 SourceRange RecRange = Rec->getSourceRange();
160 SourceRange Arg0Range = Msg->getArg(0)->getSourceRange();
161 SourceRange Arg1Range = Msg->getArg(1)->getSourceRange();
162
163 commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
164 Arg0Range.getBegin()),
165 CharSourceRange::getTokenRange(RecRange));
166 commit.replaceWithInner(CharSourceRange::getCharRange(Arg0Range.getBegin(),
167 Arg1Range.getBegin()),
168 CharSourceRange::getTokenRange(Arg0Range));
169 commit.replaceWithInner(SourceRange(Arg1Range.getBegin(), MsgRange.getEnd()),
170 Arg1Range);
171 commit.insertWrap("[", CharSourceRange::getCharRange(Arg0Range.getBegin(),
172 Arg1Range.getBegin()),
173 "] = ");
174 maybePutParensOnReceiver(Rec, commit);
175 return true;
176}
177
Argyrios Kyrtzidis6310fdd2012-06-04 21:23:26 +0000178static bool rewriteToDictionarySubscriptSet(const ObjCInterfaceDecl *IFace,
179 const ObjCMessageExpr *Msg,
180 const NSAPI &NS,
Ted Kremenekf7639e12012-03-06 20:06:33 +0000181 Commit &commit) {
182 if (Msg->getNumArgs() != 2)
183 return false;
184 const Expr *Rec = Msg->getInstanceReceiver();
185 if (!Rec)
186 return false;
Argyrios Kyrtzidisa3fcbeb2012-06-19 02:22:02 +0000187 if (!IFace->lookupInstanceMethod(NS.getSetObjectForKeyedSubscriptSelector()))
Argyrios Kyrtzidis6310fdd2012-06-04 21:23:26 +0000188 return false;
Ted Kremenekf7639e12012-03-06 20:06:33 +0000189
190 SourceRange MsgRange = Msg->getSourceRange();
191 SourceRange RecRange = Rec->getSourceRange();
192 SourceRange Arg0Range = Msg->getArg(0)->getSourceRange();
193 SourceRange Arg1Range = Msg->getArg(1)->getSourceRange();
194
195 SourceLocation LocBeforeVal = Arg0Range.getBegin();
196 commit.insertBefore(LocBeforeVal, "] = ");
197 commit.insertFromRange(LocBeforeVal, Arg1Range, /*afterToken=*/false,
198 /*beforePreviousInsertions=*/true);
199 commit.insertBefore(LocBeforeVal, "[");
200 commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
201 Arg0Range.getBegin()),
202 CharSourceRange::getTokenRange(RecRange));
203 commit.replaceWithInner(SourceRange(Arg0Range.getBegin(), MsgRange.getEnd()),
204 Arg0Range);
205 maybePutParensOnReceiver(Rec, commit);
206 return true;
207}
208
209bool edit::rewriteToObjCSubscriptSyntax(const ObjCMessageExpr *Msg,
Argyrios Kyrtzidis6310fdd2012-06-04 21:23:26 +0000210 const NSAPI &NS, Commit &commit) {
Ted Kremenekf7639e12012-03-06 20:06:33 +0000211 if (!Msg || Msg->isImplicit() ||
212 Msg->getReceiverKind() != ObjCMessageExpr::Instance)
213 return false;
214 const ObjCMethodDecl *Method = Msg->getMethodDecl();
215 if (!Method)
216 return false;
217
218 const ObjCInterfaceDecl *
219 IFace = NS.getASTContext().getObjContainingInterface(
220 const_cast<ObjCMethodDecl *>(Method));
221 if (!IFace)
222 return false;
223 IdentifierInfo *II = IFace->getIdentifier();
224 Selector Sel = Msg->getSelector();
225
Argyrios Kyrtzidis6310fdd2012-06-04 21:23:26 +0000226 if (II == NS.getNSClassId(NSAPI::ClassId_NSArray) &&
227 Sel == NS.getNSArraySelector(NSAPI::NSArr_objectAtIndex))
228 return rewriteToArraySubscriptGet(IFace, Msg, NS, commit);
229
230 if (II == NS.getNSClassId(NSAPI::ClassId_NSDictionary) &&
231 Sel == NS.getNSDictionarySelector(NSAPI::NSDict_objectForKey))
232 return rewriteToDictionarySubscriptGet(IFace, Msg, NS, commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000233
234 if (Msg->getNumArgs() != 2)
235 return false;
236
237 if (II == NS.getNSClassId(NSAPI::ClassId_NSMutableArray) &&
238 Sel == NS.getNSArraySelector(NSAPI::NSMutableArr_replaceObjectAtIndex))
Argyrios Kyrtzidis6310fdd2012-06-04 21:23:26 +0000239 return rewriteToArraySubscriptSet(IFace, Msg, NS, commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000240
241 if (II == NS.getNSClassId(NSAPI::ClassId_NSMutableDictionary) &&
242 Sel == NS.getNSDictionarySelector(NSAPI::NSMutableDict_setObjectForKey))
Argyrios Kyrtzidis6310fdd2012-06-04 21:23:26 +0000243 return rewriteToDictionarySubscriptSet(IFace, Msg, NS, commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000244
245 return false;
246}
247
248//===----------------------------------------------------------------------===//
249// rewriteToObjCLiteralSyntax.
250//===----------------------------------------------------------------------===//
251
252static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
253 const NSAPI &NS, Commit &commit);
254static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
255 const NSAPI &NS, Commit &commit);
256static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
257 const NSAPI &NS, Commit &commit);
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000258static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
259 const NSAPI &NS, Commit &commit);
Argyrios Kyrtzidis7bd957c2012-05-15 22:22:10 +0000260static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
261 const NSAPI &NS, Commit &commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000262
263bool edit::rewriteToObjCLiteralSyntax(const ObjCMessageExpr *Msg,
264 const NSAPI &NS, Commit &commit) {
265 IdentifierInfo *II = 0;
Argyrios Kyrtzidis63aebfb2012-06-06 22:23:12 +0000266 if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
Ted Kremenekf7639e12012-03-06 20:06:33 +0000267 return false;
268
269 if (II == NS.getNSClassId(NSAPI::ClassId_NSArray))
270 return rewriteToArrayLiteral(Msg, NS, commit);
271 if (II == NS.getNSClassId(NSAPI::ClassId_NSDictionary))
272 return rewriteToDictionaryLiteral(Msg, NS, commit);
273 if (II == NS.getNSClassId(NSAPI::ClassId_NSNumber))
274 return rewriteToNumberLiteral(Msg, NS, commit);
Argyrios Kyrtzidis7bd957c2012-05-15 22:22:10 +0000275 if (II == NS.getNSClassId(NSAPI::ClassId_NSString))
276 return rewriteToStringBoxedExpression(Msg, NS, commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000277
278 return false;
279}
280
281//===----------------------------------------------------------------------===//
282// rewriteToArrayLiteral.
283//===----------------------------------------------------------------------===//
284
Argyrios Kyrtzidisc1dfed62012-05-14 22:01:53 +0000285/// \brief Adds an explicit cast to 'id' if the type is not objc object.
286static void objectifyExpr(const Expr *E, Commit &commit);
287
Ted Kremenekf7639e12012-03-06 20:06:33 +0000288static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
289 const NSAPI &NS, Commit &commit) {
290 Selector Sel = Msg->getSelector();
291 SourceRange MsgRange = Msg->getSourceRange();
292
293 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array)) {
294 if (Msg->getNumArgs() != 0)
295 return false;
296 commit.replace(MsgRange, "@[]");
297 return true;
298 }
299
300 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) {
301 if (Msg->getNumArgs() != 1)
302 return false;
Argyrios Kyrtzidisc1dfed62012-05-14 22:01:53 +0000303 objectifyExpr(Msg->getArg(0), commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000304 SourceRange ArgRange = Msg->getArg(0)->getSourceRange();
305 commit.replaceWithInner(MsgRange, ArgRange);
306 commit.insertWrap("@[", ArgRange, "]");
307 return true;
308 }
309
Argyrios Kyrtzidis63aebfb2012-06-06 22:23:12 +0000310 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) ||
311 Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) {
Ted Kremenekf7639e12012-03-06 20:06:33 +0000312 if (Msg->getNumArgs() == 0)
313 return false;
314 const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1);
315 if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
316 return false;
317
Argyrios Kyrtzidisc1dfed62012-05-14 22:01:53 +0000318 for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i)
319 objectifyExpr(Msg->getArg(i), commit);
320
Ted Kremenekf7639e12012-03-06 20:06:33 +0000321 if (Msg->getNumArgs() == 1) {
322 commit.replace(MsgRange, "@[]");
323 return true;
324 }
325 SourceRange ArgRange(Msg->getArg(0)->getLocStart(),
326 Msg->getArg(Msg->getNumArgs()-2)->getLocEnd());
327 commit.replaceWithInner(MsgRange, ArgRange);
328 commit.insertWrap("@[", ArgRange, "]");
329 return true;
330 }
331
332 return false;
333}
334
335//===----------------------------------------------------------------------===//
336// rewriteToDictionaryLiteral.
337//===----------------------------------------------------------------------===//
338
339static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
340 const NSAPI &NS, Commit &commit) {
341 Selector Sel = Msg->getSelector();
342 SourceRange MsgRange = Msg->getSourceRange();
343
344 if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_dictionary)) {
345 if (Msg->getNumArgs() != 0)
346 return false;
347 commit.replace(MsgRange, "@{}");
348 return true;
349 }
350
351 if (Sel == NS.getNSDictionarySelector(
352 NSAPI::NSDict_dictionaryWithObjectForKey)) {
353 if (Msg->getNumArgs() != 2)
354 return false;
Argyrios Kyrtzidisc1dfed62012-05-14 22:01:53 +0000355
356 objectifyExpr(Msg->getArg(0), commit);
357 objectifyExpr(Msg->getArg(1), commit);
358
Ted Kremenekf7639e12012-03-06 20:06:33 +0000359 SourceRange ValRange = Msg->getArg(0)->getSourceRange();
360 SourceRange KeyRange = Msg->getArg(1)->getSourceRange();
361 // Insert key before the value.
362 commit.insertBefore(ValRange.getBegin(), ": ");
363 commit.insertFromRange(ValRange.getBegin(),
364 CharSourceRange::getTokenRange(KeyRange),
365 /*afterToken=*/false, /*beforePreviousInsertions=*/true);
366 commit.insertBefore(ValRange.getBegin(), "@{");
367 commit.insertAfterToken(ValRange.getEnd(), "}");
368 commit.replaceWithInner(MsgRange, ValRange);
369 return true;
370 }
371
372 if (Sel == NS.getNSDictionarySelector(
Argyrios Kyrtzidis63aebfb2012-06-06 22:23:12 +0000373 NSAPI::NSDict_dictionaryWithObjectsAndKeys) ||
374 Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsAndKeys)) {
Ted Kremenekf7639e12012-03-06 20:06:33 +0000375 if (Msg->getNumArgs() % 2 != 1)
376 return false;
377 unsigned SentinelIdx = Msg->getNumArgs() - 1;
378 const Expr *SentinelExpr = Msg->getArg(SentinelIdx);
379 if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
380 return false;
381
382 if (Msg->getNumArgs() == 1) {
383 commit.replace(MsgRange, "@{}");
384 return true;
385 }
386
387 for (unsigned i = 0; i < SentinelIdx; i += 2) {
Argyrios Kyrtzidisc1dfed62012-05-14 22:01:53 +0000388 objectifyExpr(Msg->getArg(i), commit);
389 objectifyExpr(Msg->getArg(i+1), commit);
390
Ted Kremenekf7639e12012-03-06 20:06:33 +0000391 SourceRange ValRange = Msg->getArg(i)->getSourceRange();
392 SourceRange KeyRange = Msg->getArg(i+1)->getSourceRange();
393 // Insert value after key.
394 commit.insertAfterToken(KeyRange.getEnd(), ": ");
395 commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true);
396 commit.remove(CharSourceRange::getCharRange(ValRange.getBegin(),
397 KeyRange.getBegin()));
398 }
399 // Range of arguments up until and including the last key.
400 // The sentinel and first value are cut off, the value will move after the
401 // key.
402 SourceRange ArgRange(Msg->getArg(1)->getLocStart(),
403 Msg->getArg(SentinelIdx-1)->getLocEnd());
404 commit.insertWrap("@{", ArgRange, "}");
405 commit.replaceWithInner(MsgRange, ArgRange);
406 return true;
407 }
408
409 return false;
410}
411
412//===----------------------------------------------------------------------===//
413// rewriteToNumberLiteral.
414//===----------------------------------------------------------------------===//
415
416static bool rewriteToCharLiteral(const ObjCMessageExpr *Msg,
417 const CharacterLiteral *Arg,
418 const NSAPI &NS, Commit &commit) {
419 if (Arg->getKind() != CharacterLiteral::Ascii)
420 return false;
421 if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithChar,
422 Msg->getSelector())) {
423 SourceRange ArgRange = Arg->getSourceRange();
424 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
425 commit.insert(ArgRange.getBegin(), "@");
426 return true;
427 }
428
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000429 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000430}
431
432static bool rewriteToBoolLiteral(const ObjCMessageExpr *Msg,
433 const Expr *Arg,
434 const NSAPI &NS, Commit &commit) {
435 if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithBool,
436 Msg->getSelector())) {
437 SourceRange ArgRange = Arg->getSourceRange();
438 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
439 commit.insert(ArgRange.getBegin(), "@");
440 return true;
441 }
442
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000443 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000444}
445
446namespace {
447
448struct LiteralInfo {
449 bool Hex, Octal;
450 StringRef U, F, L, LL;
451 CharSourceRange WithoutSuffRange;
452};
453
454}
455
456static bool getLiteralInfo(SourceRange literalRange,
457 bool isFloat, bool isIntZero,
458 ASTContext &Ctx, LiteralInfo &Info) {
459 if (literalRange.getBegin().isMacroID() ||
460 literalRange.getEnd().isMacroID())
461 return false;
462 StringRef text = Lexer::getSourceText(
463 CharSourceRange::getTokenRange(literalRange),
David Blaikiebbafb8a2012-03-11 07:00:24 +0000464 Ctx.getSourceManager(), Ctx.getLangOpts());
Ted Kremenekf7639e12012-03-06 20:06:33 +0000465 if (text.empty())
466 return false;
467
468 llvm::Optional<bool> UpperU, UpperL;
469 bool UpperF = false;
470
471 struct Suff {
472 static bool has(StringRef suff, StringRef &text) {
473 if (text.endswith(suff)) {
474 text = text.substr(0, text.size()-suff.size());
475 return true;
476 }
477 return false;
478 }
479 };
480
481 while (1) {
482 if (Suff::has("u", text)) {
483 UpperU = false;
484 } else if (Suff::has("U", text)) {
485 UpperU = true;
486 } else if (Suff::has("ll", text)) {
487 UpperL = false;
488 } else if (Suff::has("LL", text)) {
489 UpperL = true;
490 } else if (Suff::has("l", text)) {
491 UpperL = false;
492 } else if (Suff::has("L", text)) {
493 UpperL = true;
494 } else if (isFloat && Suff::has("f", text)) {
495 UpperF = false;
496 } else if (isFloat && Suff::has("F", text)) {
497 UpperF = true;
498 } else
499 break;
500 }
501
502 if (!UpperU.hasValue() && !UpperL.hasValue())
503 UpperU = UpperL = true;
504 else if (UpperU.hasValue() && !UpperL.hasValue())
505 UpperL = UpperU;
506 else if (UpperL.hasValue() && !UpperU.hasValue())
507 UpperU = UpperL;
508
509 Info.U = *UpperU ? "U" : "u";
510 Info.L = *UpperL ? "L" : "l";
511 Info.LL = *UpperL ? "LL" : "ll";
512 Info.F = UpperF ? "F" : "f";
513
514 Info.Hex = Info.Octal = false;
515 if (text.startswith("0x"))
516 Info.Hex = true;
517 else if (!isFloat && !isIntZero && text.startswith("0"))
518 Info.Octal = true;
519
520 SourceLocation B = literalRange.getBegin();
521 Info.WithoutSuffRange =
522 CharSourceRange::getCharRange(B, B.getLocWithOffset(text.size()));
523 return true;
524}
525
526static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
527 const NSAPI &NS, Commit &commit) {
528 if (Msg->getNumArgs() != 1)
529 return false;
530
531 const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts();
532 if (const CharacterLiteral *CharE = dyn_cast<CharacterLiteral>(Arg))
533 return rewriteToCharLiteral(Msg, CharE, NS, commit);
534 if (const ObjCBoolLiteralExpr *BE = dyn_cast<ObjCBoolLiteralExpr>(Arg))
535 return rewriteToBoolLiteral(Msg, BE, NS, commit);
536 if (const CXXBoolLiteralExpr *BE = dyn_cast<CXXBoolLiteralExpr>(Arg))
537 return rewriteToBoolLiteral(Msg, BE, NS, commit);
538
539 const Expr *literalE = Arg;
540 if (const UnaryOperator *UOE = dyn_cast<UnaryOperator>(literalE)) {
541 if (UOE->getOpcode() == UO_Plus || UOE->getOpcode() == UO_Minus)
542 literalE = UOE->getSubExpr();
543 }
544
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000545 // Only integer and floating literals, otherwise try to rewrite to boxed
546 // expression.
Ted Kremenekf7639e12012-03-06 20:06:33 +0000547 if (!isa<IntegerLiteral>(literalE) && !isa<FloatingLiteral>(literalE))
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000548 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000549
550 ASTContext &Ctx = NS.getASTContext();
551 Selector Sel = Msg->getSelector();
552 llvm::Optional<NSAPI::NSNumberLiteralMethodKind>
553 MKOpt = NS.getNSNumberLiteralMethodKind(Sel);
554 if (!MKOpt)
555 return false;
556 NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;
557
Benjamin Kramerece209a2012-03-13 17:05:43 +0000558 bool CallIsUnsigned = false, CallIsLong = false, CallIsLongLong = false;
Ted Kremenekf7639e12012-03-06 20:06:33 +0000559 bool CallIsFloating = false, CallIsDouble = false;
560
561 switch (MK) {
562 // We cannot have these calls with int/float literals.
563 case NSAPI::NSNumberWithChar:
564 case NSAPI::NSNumberWithUnsignedChar:
565 case NSAPI::NSNumberWithShort:
566 case NSAPI::NSNumberWithUnsignedShort:
567 case NSAPI::NSNumberWithBool:
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000568 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000569
570 case NSAPI::NSNumberWithUnsignedInt:
571 case NSAPI::NSNumberWithUnsignedInteger:
572 CallIsUnsigned = true;
573 case NSAPI::NSNumberWithInt:
574 case NSAPI::NSNumberWithInteger:
Ted Kremenekf7639e12012-03-06 20:06:33 +0000575 break;
576
577 case NSAPI::NSNumberWithUnsignedLong:
578 CallIsUnsigned = true;
579 case NSAPI::NSNumberWithLong:
Benjamin Kramerece209a2012-03-13 17:05:43 +0000580 CallIsLong = true;
Ted Kremenekf7639e12012-03-06 20:06:33 +0000581 break;
582
583 case NSAPI::NSNumberWithUnsignedLongLong:
584 CallIsUnsigned = true;
585 case NSAPI::NSNumberWithLongLong:
Benjamin Kramerece209a2012-03-13 17:05:43 +0000586 CallIsLongLong = true;
Ted Kremenekf7639e12012-03-06 20:06:33 +0000587 break;
588
589 case NSAPI::NSNumberWithDouble:
590 CallIsDouble = true;
591 case NSAPI::NSNumberWithFloat:
592 CallIsFloating = true;
593 break;
594 }
595
596 SourceRange ArgRange = Arg->getSourceRange();
597 QualType ArgTy = Arg->getType();
598 QualType CallTy = Msg->getArg(0)->getType();
599
600 // Check for the easy case, the literal maps directly to the call.
601 if (Ctx.hasSameType(ArgTy, CallTy)) {
602 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
603 commit.insert(ArgRange.getBegin(), "@");
604 return true;
605 }
606
607 // We will need to modify the literal suffix to get the same type as the call.
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000608 // Try with boxed expression if it came from a macro.
Ted Kremenekf7639e12012-03-06 20:06:33 +0000609 if (ArgRange.getBegin().isMacroID())
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000610 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000611
612 bool LitIsFloat = ArgTy->isFloatingType();
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000613 // For a float passed to integer call, don't try rewriting to objc literal.
614 // It is difficult and a very uncommon case anyway.
615 // But try with boxed expression.
Ted Kremenekf7639e12012-03-06 20:06:33 +0000616 if (LitIsFloat && !CallIsFloating)
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000617 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000618
619 // Try to modify the literal make it the same type as the method call.
620 // -Modify the suffix, and/or
621 // -Change integer to float
622
623 LiteralInfo LitInfo;
624 bool isIntZero = false;
625 if (const IntegerLiteral *IntE = dyn_cast<IntegerLiteral>(literalE))
626 isIntZero = !IntE->getValue().getBoolValue();
627 if (!getLiteralInfo(ArgRange, LitIsFloat, isIntZero, Ctx, LitInfo))
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000628 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000629
630 // Not easy to do int -> float with hex/octal and uncommon anyway.
631 if (!LitIsFloat && CallIsFloating && (LitInfo.Hex || LitInfo.Octal))
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000632 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000633
634 SourceLocation LitB = LitInfo.WithoutSuffRange.getBegin();
635 SourceLocation LitE = LitInfo.WithoutSuffRange.getEnd();
636
637 commit.replaceWithInner(CharSourceRange::getTokenRange(Msg->getSourceRange()),
638 LitInfo.WithoutSuffRange);
639 commit.insert(LitB, "@");
640
641 if (!LitIsFloat && CallIsFloating)
642 commit.insert(LitE, ".0");
643
644 if (CallIsFloating) {
645 if (!CallIsDouble)
646 commit.insert(LitE, LitInfo.F);
647 } else {
648 if (CallIsUnsigned)
649 commit.insert(LitE, LitInfo.U);
650
651 if (CallIsLong)
652 commit.insert(LitE, LitInfo.L);
653 else if (CallIsLongLong)
654 commit.insert(LitE, LitInfo.LL);
655 }
656 return true;
657}
Argyrios Kyrtzidisc1dfed62012-05-14 22:01:53 +0000658
Argyrios Kyrtzidis0bbe94f2012-05-14 23:33:49 +0000659// FIXME: Make determination of operator precedence more general and
660// make it broadly available.
661static bool subscriptOperatorNeedsParens(const Expr *FullExpr) {
662 const Expr* Expr = FullExpr->IgnoreImpCasts();
663 if (isa<ArraySubscriptExpr>(Expr) ||
664 isa<CallExpr>(Expr) ||
665 isa<DeclRefExpr>(Expr) ||
666 isa<CXXNamedCastExpr>(Expr) ||
667 isa<CXXConstructExpr>(Expr) ||
668 isa<CXXThisExpr>(Expr) ||
669 isa<CXXTypeidExpr>(Expr) ||
670 isa<CXXUnresolvedConstructExpr>(Expr) ||
671 isa<ObjCMessageExpr>(Expr) ||
672 isa<ObjCPropertyRefExpr>(Expr) ||
673 isa<ObjCProtocolExpr>(Expr) ||
674 isa<MemberExpr>(Expr) ||
Argyrios Kyrtzidis94442982012-05-22 00:47:53 +0000675 isa<ObjCIvarRefExpr>(Expr) ||
Argyrios Kyrtzidis0bbe94f2012-05-14 23:33:49 +0000676 isa<ParenExpr>(FullExpr) ||
677 isa<ParenListExpr>(Expr) ||
678 isa<SizeOfPackExpr>(Expr))
679 return false;
680
681 return true;
682}
Argyrios Kyrtzidisc1dfed62012-05-14 22:01:53 +0000683static bool castOperatorNeedsParens(const Expr *FullExpr) {
684 const Expr* Expr = FullExpr->IgnoreImpCasts();
685 if (isa<ArraySubscriptExpr>(Expr) ||
686 isa<CallExpr>(Expr) ||
687 isa<DeclRefExpr>(Expr) ||
688 isa<CastExpr>(Expr) ||
689 isa<CXXNewExpr>(Expr) ||
690 isa<CXXConstructExpr>(Expr) ||
691 isa<CXXDeleteExpr>(Expr) ||
692 isa<CXXNoexceptExpr>(Expr) ||
693 isa<CXXPseudoDestructorExpr>(Expr) ||
694 isa<CXXScalarValueInitExpr>(Expr) ||
695 isa<CXXThisExpr>(Expr) ||
696 isa<CXXTypeidExpr>(Expr) ||
697 isa<CXXUnresolvedConstructExpr>(Expr) ||
698 isa<ObjCMessageExpr>(Expr) ||
699 isa<ObjCPropertyRefExpr>(Expr) ||
700 isa<ObjCProtocolExpr>(Expr) ||
701 isa<MemberExpr>(Expr) ||
Argyrios Kyrtzidis94442982012-05-22 00:47:53 +0000702 isa<ObjCIvarRefExpr>(Expr) ||
Argyrios Kyrtzidisc1dfed62012-05-14 22:01:53 +0000703 isa<ParenExpr>(FullExpr) ||
704 isa<ParenListExpr>(Expr) ||
705 isa<SizeOfPackExpr>(Expr) ||
706 isa<UnaryOperator>(Expr))
707 return false;
708
709 return true;
710}
711
712static void objectifyExpr(const Expr *E, Commit &commit) {
713 if (!E) return;
714
715 QualType T = E->getType();
716 if (T->isObjCObjectPointerType()) {
717 if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) {
718 if (ICE->getCastKind() != CK_CPointerToObjCPointerCast)
719 return;
720 } else {
721 return;
722 }
723 } else if (!T->isPointerType()) {
724 return;
725 }
726
727 SourceRange Range = E->getSourceRange();
728 if (castOperatorNeedsParens(E))
729 commit.insertWrap("(", Range, ")");
730 commit.insertBefore(Range.getBegin(), "(id)");
731}
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000732
733//===----------------------------------------------------------------------===//
734// rewriteToNumericBoxedExpression.
735//===----------------------------------------------------------------------===//
736
Argyrios Kyrtzidiscebe7222012-05-15 22:59:54 +0000737static bool isEnumConstant(const Expr *E) {
738 if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts()))
739 if (const ValueDecl *VD = DRE->getDecl())
740 return isa<EnumConstantDecl>(VD);
741
742 return false;
743}
744
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000745static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
746 const NSAPI &NS, Commit &commit) {
747 if (Msg->getNumArgs() != 1)
748 return false;
749
750 const Expr *Arg = Msg->getArg(0);
751 if (Arg->isTypeDependent())
752 return false;
753
754 ASTContext &Ctx = NS.getASTContext();
755 Selector Sel = Msg->getSelector();
756 llvm::Optional<NSAPI::NSNumberLiteralMethodKind>
757 MKOpt = NS.getNSNumberLiteralMethodKind(Sel);
758 if (!MKOpt)
759 return false;
760 NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;
761
762 const Expr *OrigArg = Arg->IgnoreImpCasts();
763 QualType FinalTy = Arg->getType();
764 QualType OrigTy = OrigArg->getType();
765 uint64_t FinalTySize = Ctx.getTypeSize(FinalTy);
766 uint64_t OrigTySize = Ctx.getTypeSize(OrigTy);
767
768 bool isTruncated = FinalTySize < OrigTySize;
769 bool needsCast = false;
770
771 if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) {
772 switch (ICE->getCastKind()) {
773 case CK_LValueToRValue:
774 case CK_NoOp:
775 case CK_UserDefinedConversion:
776 break;
777
778 case CK_IntegralCast: {
779 if (MK == NSAPI::NSNumberWithBool && OrigTy->isBooleanType())
780 break;
781 // Be more liberal with Integer/UnsignedInteger which are very commonly
782 // used.
783 if ((MK == NSAPI::NSNumberWithInteger ||
784 MK == NSAPI::NSNumberWithUnsignedInteger) &&
785 !isTruncated) {
Argyrios Kyrtzidiscebe7222012-05-15 22:59:54 +0000786 if (OrigTy->getAs<EnumType>() || isEnumConstant(OrigArg))
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000787 break;
788 if ((MK==NSAPI::NSNumberWithInteger) == OrigTy->isSignedIntegerType() &&
789 OrigTySize >= Ctx.getTypeSize(Ctx.IntTy))
790 break;
791 }
792
793 needsCast = true;
794 break;
795 }
796
797 case CK_PointerToBoolean:
798 case CK_IntegralToBoolean:
799 case CK_IntegralToFloating:
800 case CK_FloatingToIntegral:
801 case CK_FloatingToBoolean:
802 case CK_FloatingCast:
803 case CK_FloatingComplexToReal:
804 case CK_FloatingComplexToBoolean:
805 case CK_IntegralComplexToReal:
806 case CK_IntegralComplexToBoolean:
807 case CK_AtomicToNonAtomic:
808 needsCast = true;
809 break;
810
811 case CK_Dependent:
812 case CK_BitCast:
813 case CK_LValueBitCast:
814 case CK_BaseToDerived:
815 case CK_DerivedToBase:
816 case CK_UncheckedDerivedToBase:
817 case CK_Dynamic:
818 case CK_ToUnion:
819 case CK_ArrayToPointerDecay:
820 case CK_FunctionToPointerDecay:
821 case CK_NullToPointer:
822 case CK_NullToMemberPointer:
823 case CK_BaseToDerivedMemberPointer:
824 case CK_DerivedToBaseMemberPointer:
825 case CK_MemberPointerToBoolean:
826 case CK_ReinterpretMemberPointer:
827 case CK_ConstructorConversion:
828 case CK_IntegralToPointer:
829 case CK_PointerToIntegral:
830 case CK_ToVoid:
831 case CK_VectorSplat:
832 case CK_CPointerToObjCPointerCast:
833 case CK_BlockPointerToObjCPointerCast:
834 case CK_AnyPointerToBlockPointerCast:
835 case CK_ObjCObjectLValueCast:
836 case CK_FloatingRealToComplex:
837 case CK_FloatingComplexCast:
838 case CK_FloatingComplexToIntegralComplex:
839 case CK_IntegralRealToComplex:
840 case CK_IntegralComplexCast:
841 case CK_IntegralComplexToFloatingComplex:
842 case CK_ARCProduceObject:
843 case CK_ARCConsumeObject:
844 case CK_ARCReclaimReturnedObject:
845 case CK_ARCExtendBlockObject:
846 case CK_NonAtomicToAtomic:
847 case CK_CopyAndAutoreleaseBlockObject:
848 return false;
849 }
850 }
851
Argyrios Kyrtzidisb4822602012-05-24 16:48:23 +0000852 if (needsCast) {
853 DiagnosticsEngine &Diags = Ctx.getDiagnostics();
854 // FIXME: Use a custom category name to distinguish migration diagnostics.
855 unsigned diagID = Diags.getCustomDiagID(DiagnosticsEngine::Warning,
Argyrios Kyrtzidis927a4372012-06-20 01:28:32 +0000856 "converting to boxing syntax requires casting %0 to %1");
857 Diags.Report(Msg->getExprLoc(), diagID) << OrigTy << FinalTy
858 << Msg->getSourceRange();
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000859 return false;
Argyrios Kyrtzidisb4822602012-05-24 16:48:23 +0000860 }
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000861
862 SourceRange ArgRange = OrigArg->getSourceRange();
Argyrios Kyrtzidis7bd957c2012-05-15 22:22:10 +0000863 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000864
865 if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
866 commit.insertBefore(ArgRange.getBegin(), "@");
867 else
868 commit.insertWrap("@(", ArgRange, ")");
869
870 return true;
871}
Argyrios Kyrtzidis7bd957c2012-05-15 22:22:10 +0000872
873//===----------------------------------------------------------------------===//
874// rewriteToStringBoxedExpression.
875//===----------------------------------------------------------------------===//
876
877static bool doRewriteToUTF8StringBoxedExpressionHelper(
878 const ObjCMessageExpr *Msg,
879 const NSAPI &NS, Commit &commit) {
880 const Expr *Arg = Msg->getArg(0);
881 if (Arg->isTypeDependent())
882 return false;
883
Argyrios Kyrtzidis81541472012-05-16 00:21:21 +0000884 ASTContext &Ctx = NS.getASTContext();
885
Argyrios Kyrtzidis7bd957c2012-05-15 22:22:10 +0000886 const Expr *OrigArg = Arg->IgnoreImpCasts();
887 QualType OrigTy = OrigArg->getType();
Argyrios Kyrtzidis81541472012-05-16 00:21:21 +0000888 if (OrigTy->isArrayType())
889 OrigTy = Ctx.getArrayDecayedType(OrigTy);
Argyrios Kyrtzidis7bd957c2012-05-15 22:22:10 +0000890
891 if (const StringLiteral *
892 StrE = dyn_cast<StringLiteral>(OrigArg->IgnoreParens())) {
893 commit.replaceWithInner(Msg->getSourceRange(), StrE->getSourceRange());
894 commit.insert(StrE->getLocStart(), "@");
895 return true;
896 }
897
Argyrios Kyrtzidis7bd957c2012-05-15 22:22:10 +0000898 if (const PointerType *PT = OrigTy->getAs<PointerType>()) {
899 QualType PointeeType = PT->getPointeeType();
900 if (Ctx.hasSameUnqualifiedType(PointeeType, Ctx.CharTy)) {
901 SourceRange ArgRange = OrigArg->getSourceRange();
902 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
903
904 if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
905 commit.insertBefore(ArgRange.getBegin(), "@");
906 else
907 commit.insertWrap("@(", ArgRange, ")");
908
909 return true;
910 }
911 }
912
913 return false;
914}
915
916static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
917 const NSAPI &NS, Commit &commit) {
918 Selector Sel = Msg->getSelector();
919
920 if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithUTF8String) ||
921 Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCString)) {
922 if (Msg->getNumArgs() != 1)
923 return false;
924 return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);
925 }
926
927 if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCStringEncoding)) {
928 if (Msg->getNumArgs() != 2)
929 return false;
930
931 const Expr *encodingArg = Msg->getArg(1);
932 if (NS.isNSUTF8StringEncodingConstant(encodingArg) ||
933 NS.isNSASCIIStringEncodingConstant(encodingArg))
934 return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);
935 }
936
937 return false;
938}