blob: 68d122dfd26f0764453c024e8501aeb6fc18c8f1 [file] [log] [blame]
Ted Kremenek30660a82012-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"
Chandler Carruth55fc8732012-12-04 09:13:33 +000015#include "clang/AST/ASTContext.h"
16#include "clang/AST/ExprCXX.h"
17#include "clang/AST/ExprObjC.h"
18#include "clang/AST/NSAPI.h"
Ted Kremenek30660a82012-03-06 20:06:33 +000019#include "clang/Edit/Commit.h"
20#include "clang/Lex/Lexer.h"
Ted Kremenek30660a82012-03-06 20:06:33 +000021
22using namespace clang;
23using namespace edit;
24
25static bool checkForLiteralCreation(const ObjCMessageExpr *Msg,
Argyrios Kyrtzidis6b4db9b2012-06-06 22:23:12 +000026 IdentifierInfo *&ClassId,
27 const LangOptions &LangOpts) {
Ted Kremenek30660a82012-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 Kyrtzidis6b4db9b2012-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 Kremenek30660a82012-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 Kyrtzidis6b4db9b2012-06-06 22:23:12 +000061 if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
Ted Kremenek30660a82012-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 Kyrtzidis6b4db9b2012-06-06 22:23:12 +000071 (NS.getNSStringSelector(NSAPI::NSStr_stringWithString) == Sel ||
72 NS.getNSStringSelector(NSAPI::NSStr_initWithString) == Sel)) ||
Ted Kremenek30660a82012-03-06 20:06:33 +000073
74 (isa<ObjCArrayLiteral>(Arg) &&
75 NS.getNSClassId(NSAPI::ClassId_NSArray) == II &&
Argyrios Kyrtzidis6b4db9b2012-06-06 22:23:12 +000076 (NS.getNSArraySelector(NSAPI::NSArr_arrayWithArray) == Sel ||
77 NS.getNSArraySelector(NSAPI::NSArr_initWithArray) == Sel)) ||
Ted Kremenek30660a82012-03-06 20:06:33 +000078
79 (isa<ObjCDictionaryLiteral>(Arg) &&
80 NS.getNSClassId(NSAPI::ClassId_NSDictionary) == II &&
Argyrios Kyrtzidis6b4db9b2012-06-06 22:23:12 +000081 (NS.getNSDictionarySelector(
82 NSAPI::NSDict_dictionaryWithDictionary) == Sel ||
83 NS.getNSDictionarySelector(NSAPI::NSDict_initWithDictionary) == Sel))) {
Ted Kremenek30660a82012-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 Kyrtzidis18387032012-07-06 00:07:09 +000097/// \brief Check for classes that accept 'objectForKey:' (or the other selectors
98/// that the migrator handles) but return their instances as 'id', resulting
99/// in the compiler resolving 'objectForKey:' as the method from NSDictionary.
100///
101/// When checking if we can convert to subscripting syntax, check whether
102/// the receiver is a result of a class method from a hardcoded list of
103/// such classes. In such a case return the specific class as the interface
104/// of the receiver.
105///
106/// FIXME: Remove this when these classes start using 'instancetype'.
107static const ObjCInterfaceDecl *
108maybeAdjustInterfaceForSubscriptingCheck(const ObjCInterfaceDecl *IFace,
109 const Expr *Receiver,
110 ASTContext &Ctx) {
111 assert(IFace && Receiver);
112
113 // If the receiver has type 'id'...
114 if (!Ctx.isObjCIdType(Receiver->getType().getUnqualifiedType()))
115 return IFace;
116
117 const ObjCMessageExpr *
118 InnerMsg = dyn_cast<ObjCMessageExpr>(Receiver->IgnoreParenCasts());
119 if (!InnerMsg)
120 return IFace;
121
122 QualType ClassRec;
123 switch (InnerMsg->getReceiverKind()) {
124 case ObjCMessageExpr::Instance:
125 case ObjCMessageExpr::SuperInstance:
126 return IFace;
127
128 case ObjCMessageExpr::Class:
129 ClassRec = InnerMsg->getClassReceiver();
130 break;
131 case ObjCMessageExpr::SuperClass:
132 ClassRec = InnerMsg->getSuperType();
133 break;
134 }
135
136 if (ClassRec.isNull())
137 return IFace;
138
139 // ...and it is the result of a class message...
140
141 const ObjCObjectType *ObjTy = ClassRec->getAs<ObjCObjectType>();
142 if (!ObjTy)
143 return IFace;
144 const ObjCInterfaceDecl *OID = ObjTy->getInterface();
145
146 // ...and the receiving class is NSMapTable or NSLocale, return that
147 // class as the receiving interface.
148 if (OID->getName() == "NSMapTable" ||
149 OID->getName() == "NSLocale")
150 return OID;
151
152 return IFace;
153}
154
155static bool canRewriteToSubscriptSyntax(const ObjCInterfaceDecl *&IFace,
156 const ObjCMessageExpr *Msg,
157 ASTContext &Ctx,
Argyrios Kyrtzidisc2abbe02012-07-05 21:49:51 +0000158 Selector subscriptSel) {
Argyrios Kyrtzidis18387032012-07-06 00:07:09 +0000159 const Expr *Rec = Msg->getInstanceReceiver();
160 if (!Rec)
161 return false;
162 IFace = maybeAdjustInterfaceForSubscriptingCheck(IFace, Rec, Ctx);
163
Argyrios Kyrtzidisc2abbe02012-07-05 21:49:51 +0000164 if (const ObjCMethodDecl *MD = IFace->lookupInstanceMethod(subscriptSel)) {
165 if (!MD->isUnavailable())
166 return true;
167 }
168 return false;
169}
170
Argyrios Kyrtzidis20119a82012-05-14 23:33:49 +0000171static bool subscriptOperatorNeedsParens(const Expr *FullExpr);
172
Ted Kremenek30660a82012-03-06 20:06:33 +0000173static void maybePutParensOnReceiver(const Expr *Receiver, Commit &commit) {
Argyrios Kyrtzidis20119a82012-05-14 23:33:49 +0000174 if (subscriptOperatorNeedsParens(Receiver)) {
Ted Kremenek30660a82012-03-06 20:06:33 +0000175 SourceRange RecRange = Receiver->getSourceRange();
176 commit.insertWrap("(", RecRange, ")");
177 }
178}
179
Argyrios Kyrtzidiscacf7182012-06-04 21:23:26 +0000180static bool rewriteToSubscriptGetCommon(const ObjCMessageExpr *Msg,
181 Commit &commit) {
Ted Kremenek30660a82012-03-06 20:06:33 +0000182 if (Msg->getNumArgs() != 1)
183 return false;
184 const Expr *Rec = Msg->getInstanceReceiver();
185 if (!Rec)
186 return false;
187
188 SourceRange MsgRange = Msg->getSourceRange();
189 SourceRange RecRange = Rec->getSourceRange();
190 SourceRange ArgRange = Msg->getArg(0)->getSourceRange();
191
192 commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
193 ArgRange.getBegin()),
194 CharSourceRange::getTokenRange(RecRange));
195 commit.replaceWithInner(SourceRange(ArgRange.getBegin(), MsgRange.getEnd()),
196 ArgRange);
197 commit.insertWrap("[", ArgRange, "]");
198 maybePutParensOnReceiver(Rec, commit);
199 return true;
200}
201
Argyrios Kyrtzidiscacf7182012-06-04 21:23:26 +0000202static bool rewriteToArraySubscriptGet(const ObjCInterfaceDecl *IFace,
203 const ObjCMessageExpr *Msg,
204 const NSAPI &NS,
205 Commit &commit) {
Argyrios Kyrtzidis18387032012-07-06 00:07:09 +0000206 if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
Argyrios Kyrtzidisc2abbe02012-07-05 21:49:51 +0000207 NS.getObjectAtIndexedSubscriptSelector()))
Argyrios Kyrtzidiscacf7182012-06-04 21:23:26 +0000208 return false;
209 return rewriteToSubscriptGetCommon(Msg, commit);
210}
211
212static bool rewriteToDictionarySubscriptGet(const ObjCInterfaceDecl *IFace,
213 const ObjCMessageExpr *Msg,
214 const NSAPI &NS,
215 Commit &commit) {
Argyrios Kyrtzidis18387032012-07-06 00:07:09 +0000216 if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
Argyrios Kyrtzidisc2abbe02012-07-05 21:49:51 +0000217 NS.getObjectForKeyedSubscriptSelector()))
Argyrios Kyrtzidiscacf7182012-06-04 21:23:26 +0000218 return false;
219 return rewriteToSubscriptGetCommon(Msg, commit);
220}
221
222static bool rewriteToArraySubscriptSet(const ObjCInterfaceDecl *IFace,
223 const ObjCMessageExpr *Msg,
224 const NSAPI &NS,
Ted Kremenek30660a82012-03-06 20:06:33 +0000225 Commit &commit) {
Argyrios Kyrtzidis18387032012-07-06 00:07:09 +0000226 if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
Argyrios Kyrtzidisc2abbe02012-07-05 21:49:51 +0000227 NS.getSetObjectAtIndexedSubscriptSelector()))
228 return false;
229
Ted Kremenek30660a82012-03-06 20:06:33 +0000230 if (Msg->getNumArgs() != 2)
231 return false;
232 const Expr *Rec = Msg->getInstanceReceiver();
233 if (!Rec)
234 return false;
235
236 SourceRange MsgRange = Msg->getSourceRange();
237 SourceRange RecRange = Rec->getSourceRange();
238 SourceRange Arg0Range = Msg->getArg(0)->getSourceRange();
239 SourceRange Arg1Range = Msg->getArg(1)->getSourceRange();
240
241 commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
242 Arg0Range.getBegin()),
243 CharSourceRange::getTokenRange(RecRange));
244 commit.replaceWithInner(CharSourceRange::getCharRange(Arg0Range.getBegin(),
245 Arg1Range.getBegin()),
246 CharSourceRange::getTokenRange(Arg0Range));
247 commit.replaceWithInner(SourceRange(Arg1Range.getBegin(), MsgRange.getEnd()),
248 Arg1Range);
249 commit.insertWrap("[", CharSourceRange::getCharRange(Arg0Range.getBegin(),
250 Arg1Range.getBegin()),
251 "] = ");
252 maybePutParensOnReceiver(Rec, commit);
253 return true;
254}
255
Argyrios Kyrtzidiscacf7182012-06-04 21:23:26 +0000256static bool rewriteToDictionarySubscriptSet(const ObjCInterfaceDecl *IFace,
257 const ObjCMessageExpr *Msg,
258 const NSAPI &NS,
Ted Kremenek30660a82012-03-06 20:06:33 +0000259 Commit &commit) {
Argyrios Kyrtzidis18387032012-07-06 00:07:09 +0000260 if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
Argyrios Kyrtzidisc2abbe02012-07-05 21:49:51 +0000261 NS.getSetObjectForKeyedSubscriptSelector()))
262 return false;
263
Ted Kremenek30660a82012-03-06 20:06:33 +0000264 if (Msg->getNumArgs() != 2)
265 return false;
266 const Expr *Rec = Msg->getInstanceReceiver();
267 if (!Rec)
268 return false;
269
270 SourceRange MsgRange = Msg->getSourceRange();
271 SourceRange RecRange = Rec->getSourceRange();
272 SourceRange Arg0Range = Msg->getArg(0)->getSourceRange();
273 SourceRange Arg1Range = Msg->getArg(1)->getSourceRange();
274
275 SourceLocation LocBeforeVal = Arg0Range.getBegin();
276 commit.insertBefore(LocBeforeVal, "] = ");
277 commit.insertFromRange(LocBeforeVal, Arg1Range, /*afterToken=*/false,
278 /*beforePreviousInsertions=*/true);
279 commit.insertBefore(LocBeforeVal, "[");
280 commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
281 Arg0Range.getBegin()),
282 CharSourceRange::getTokenRange(RecRange));
283 commit.replaceWithInner(SourceRange(Arg0Range.getBegin(), MsgRange.getEnd()),
284 Arg0Range);
285 maybePutParensOnReceiver(Rec, commit);
286 return true;
287}
288
289bool edit::rewriteToObjCSubscriptSyntax(const ObjCMessageExpr *Msg,
Argyrios Kyrtzidiscacf7182012-06-04 21:23:26 +0000290 const NSAPI &NS, Commit &commit) {
Ted Kremenek30660a82012-03-06 20:06:33 +0000291 if (!Msg || Msg->isImplicit() ||
292 Msg->getReceiverKind() != ObjCMessageExpr::Instance)
293 return false;
294 const ObjCMethodDecl *Method = Msg->getMethodDecl();
295 if (!Method)
296 return false;
297
298 const ObjCInterfaceDecl *
299 IFace = NS.getASTContext().getObjContainingInterface(
300 const_cast<ObjCMethodDecl *>(Method));
301 if (!IFace)
302 return false;
Ted Kremenek30660a82012-03-06 20:06:33 +0000303 Selector Sel = Msg->getSelector();
304
Argyrios Kyrtzidisc2abbe02012-07-05 21:49:51 +0000305 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_objectAtIndex))
Argyrios Kyrtzidiscacf7182012-06-04 21:23:26 +0000306 return rewriteToArraySubscriptGet(IFace, Msg, NS, commit);
307
Argyrios Kyrtzidisc2abbe02012-07-05 21:49:51 +0000308 if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_objectForKey))
Argyrios Kyrtzidiscacf7182012-06-04 21:23:26 +0000309 return rewriteToDictionarySubscriptGet(IFace, Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000310
311 if (Msg->getNumArgs() != 2)
312 return false;
313
Argyrios Kyrtzidisc2abbe02012-07-05 21:49:51 +0000314 if (Sel == NS.getNSArraySelector(NSAPI::NSMutableArr_replaceObjectAtIndex))
Argyrios Kyrtzidiscacf7182012-06-04 21:23:26 +0000315 return rewriteToArraySubscriptSet(IFace, Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000316
Argyrios Kyrtzidisc2abbe02012-07-05 21:49:51 +0000317 if (Sel == NS.getNSDictionarySelector(NSAPI::NSMutableDict_setObjectForKey))
Argyrios Kyrtzidiscacf7182012-06-04 21:23:26 +0000318 return rewriteToDictionarySubscriptSet(IFace, Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000319
320 return false;
321}
322
323//===----------------------------------------------------------------------===//
324// rewriteToObjCLiteralSyntax.
325//===----------------------------------------------------------------------===//
326
327static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
328 const NSAPI &NS, Commit &commit);
329static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
330 const NSAPI &NS, Commit &commit);
331static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
332 const NSAPI &NS, Commit &commit);
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000333static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
334 const NSAPI &NS, Commit &commit);
Argyrios Kyrtzidis7fe103c2012-05-15 22:22:10 +0000335static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
336 const NSAPI &NS, Commit &commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000337
338bool edit::rewriteToObjCLiteralSyntax(const ObjCMessageExpr *Msg,
339 const NSAPI &NS, Commit &commit) {
340 IdentifierInfo *II = 0;
Argyrios Kyrtzidis6b4db9b2012-06-06 22:23:12 +0000341 if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
Ted Kremenek30660a82012-03-06 20:06:33 +0000342 return false;
343
344 if (II == NS.getNSClassId(NSAPI::ClassId_NSArray))
345 return rewriteToArrayLiteral(Msg, NS, commit);
346 if (II == NS.getNSClassId(NSAPI::ClassId_NSDictionary))
347 return rewriteToDictionaryLiteral(Msg, NS, commit);
348 if (II == NS.getNSClassId(NSAPI::ClassId_NSNumber))
349 return rewriteToNumberLiteral(Msg, NS, commit);
Argyrios Kyrtzidis7fe103c2012-05-15 22:22:10 +0000350 if (II == NS.getNSClassId(NSAPI::ClassId_NSString))
351 return rewriteToStringBoxedExpression(Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000352
353 return false;
354}
355
356//===----------------------------------------------------------------------===//
357// rewriteToArrayLiteral.
358//===----------------------------------------------------------------------===//
359
Argyrios Kyrtzidis055b3952012-05-14 22:01:53 +0000360/// \brief Adds an explicit cast to 'id' if the type is not objc object.
361static void objectifyExpr(const Expr *E, Commit &commit);
362
Ted Kremenek30660a82012-03-06 20:06:33 +0000363static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
364 const NSAPI &NS, Commit &commit) {
365 Selector Sel = Msg->getSelector();
366 SourceRange MsgRange = Msg->getSourceRange();
367
368 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array)) {
369 if (Msg->getNumArgs() != 0)
370 return false;
371 commit.replace(MsgRange, "@[]");
372 return true;
373 }
374
375 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) {
376 if (Msg->getNumArgs() != 1)
377 return false;
Argyrios Kyrtzidis055b3952012-05-14 22:01:53 +0000378 objectifyExpr(Msg->getArg(0), commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000379 SourceRange ArgRange = Msg->getArg(0)->getSourceRange();
380 commit.replaceWithInner(MsgRange, ArgRange);
381 commit.insertWrap("@[", ArgRange, "]");
382 return true;
383 }
384
Argyrios Kyrtzidis6b4db9b2012-06-06 22:23:12 +0000385 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) ||
386 Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) {
Ted Kremenek30660a82012-03-06 20:06:33 +0000387 if (Msg->getNumArgs() == 0)
388 return false;
389 const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1);
390 if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
391 return false;
392
Argyrios Kyrtzidis055b3952012-05-14 22:01:53 +0000393 for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i)
394 objectifyExpr(Msg->getArg(i), commit);
395
Ted Kremenek30660a82012-03-06 20:06:33 +0000396 if (Msg->getNumArgs() == 1) {
397 commit.replace(MsgRange, "@[]");
398 return true;
399 }
400 SourceRange ArgRange(Msg->getArg(0)->getLocStart(),
401 Msg->getArg(Msg->getNumArgs()-2)->getLocEnd());
402 commit.replaceWithInner(MsgRange, ArgRange);
403 commit.insertWrap("@[", ArgRange, "]");
404 return true;
405 }
406
407 return false;
408}
409
410//===----------------------------------------------------------------------===//
411// rewriteToDictionaryLiteral.
412//===----------------------------------------------------------------------===//
413
414static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
415 const NSAPI &NS, Commit &commit) {
416 Selector Sel = Msg->getSelector();
417 SourceRange MsgRange = Msg->getSourceRange();
418
419 if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_dictionary)) {
420 if (Msg->getNumArgs() != 0)
421 return false;
422 commit.replace(MsgRange, "@{}");
423 return true;
424 }
425
426 if (Sel == NS.getNSDictionarySelector(
427 NSAPI::NSDict_dictionaryWithObjectForKey)) {
428 if (Msg->getNumArgs() != 2)
429 return false;
Argyrios Kyrtzidis055b3952012-05-14 22:01:53 +0000430
431 objectifyExpr(Msg->getArg(0), commit);
432 objectifyExpr(Msg->getArg(1), commit);
433
Ted Kremenek30660a82012-03-06 20:06:33 +0000434 SourceRange ValRange = Msg->getArg(0)->getSourceRange();
435 SourceRange KeyRange = Msg->getArg(1)->getSourceRange();
436 // Insert key before the value.
437 commit.insertBefore(ValRange.getBegin(), ": ");
438 commit.insertFromRange(ValRange.getBegin(),
439 CharSourceRange::getTokenRange(KeyRange),
440 /*afterToken=*/false, /*beforePreviousInsertions=*/true);
441 commit.insertBefore(ValRange.getBegin(), "@{");
442 commit.insertAfterToken(ValRange.getEnd(), "}");
443 commit.replaceWithInner(MsgRange, ValRange);
444 return true;
445 }
446
447 if (Sel == NS.getNSDictionarySelector(
Argyrios Kyrtzidis6b4db9b2012-06-06 22:23:12 +0000448 NSAPI::NSDict_dictionaryWithObjectsAndKeys) ||
449 Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsAndKeys)) {
Ted Kremenek30660a82012-03-06 20:06:33 +0000450 if (Msg->getNumArgs() % 2 != 1)
451 return false;
452 unsigned SentinelIdx = Msg->getNumArgs() - 1;
453 const Expr *SentinelExpr = Msg->getArg(SentinelIdx);
454 if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
455 return false;
456
457 if (Msg->getNumArgs() == 1) {
458 commit.replace(MsgRange, "@{}");
459 return true;
460 }
461
462 for (unsigned i = 0; i < SentinelIdx; i += 2) {
Argyrios Kyrtzidis055b3952012-05-14 22:01:53 +0000463 objectifyExpr(Msg->getArg(i), commit);
464 objectifyExpr(Msg->getArg(i+1), commit);
465
Ted Kremenek30660a82012-03-06 20:06:33 +0000466 SourceRange ValRange = Msg->getArg(i)->getSourceRange();
467 SourceRange KeyRange = Msg->getArg(i+1)->getSourceRange();
468 // Insert value after key.
469 commit.insertAfterToken(KeyRange.getEnd(), ": ");
470 commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true);
471 commit.remove(CharSourceRange::getCharRange(ValRange.getBegin(),
472 KeyRange.getBegin()));
473 }
474 // Range of arguments up until and including the last key.
475 // The sentinel and first value are cut off, the value will move after the
476 // key.
477 SourceRange ArgRange(Msg->getArg(1)->getLocStart(),
478 Msg->getArg(SentinelIdx-1)->getLocEnd());
479 commit.insertWrap("@{", ArgRange, "}");
480 commit.replaceWithInner(MsgRange, ArgRange);
481 return true;
482 }
483
484 return false;
485}
486
487//===----------------------------------------------------------------------===//
488// rewriteToNumberLiteral.
489//===----------------------------------------------------------------------===//
490
491static bool rewriteToCharLiteral(const ObjCMessageExpr *Msg,
492 const CharacterLiteral *Arg,
493 const NSAPI &NS, Commit &commit) {
494 if (Arg->getKind() != CharacterLiteral::Ascii)
495 return false;
496 if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithChar,
497 Msg->getSelector())) {
498 SourceRange ArgRange = Arg->getSourceRange();
499 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
500 commit.insert(ArgRange.getBegin(), "@");
501 return true;
502 }
503
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000504 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000505}
506
507static bool rewriteToBoolLiteral(const ObjCMessageExpr *Msg,
508 const Expr *Arg,
509 const NSAPI &NS, Commit &commit) {
510 if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithBool,
511 Msg->getSelector())) {
512 SourceRange ArgRange = Arg->getSourceRange();
513 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
514 commit.insert(ArgRange.getBegin(), "@");
515 return true;
516 }
517
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000518 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000519}
520
521namespace {
522
523struct LiteralInfo {
524 bool Hex, Octal;
525 StringRef U, F, L, LL;
526 CharSourceRange WithoutSuffRange;
527};
528
529}
530
531static bool getLiteralInfo(SourceRange literalRange,
532 bool isFloat, bool isIntZero,
533 ASTContext &Ctx, LiteralInfo &Info) {
534 if (literalRange.getBegin().isMacroID() ||
535 literalRange.getEnd().isMacroID())
536 return false;
537 StringRef text = Lexer::getSourceText(
538 CharSourceRange::getTokenRange(literalRange),
David Blaikie4e4d0842012-03-11 07:00:24 +0000539 Ctx.getSourceManager(), Ctx.getLangOpts());
Ted Kremenek30660a82012-03-06 20:06:33 +0000540 if (text.empty())
541 return false;
542
543 llvm::Optional<bool> UpperU, UpperL;
544 bool UpperF = false;
545
546 struct Suff {
547 static bool has(StringRef suff, StringRef &text) {
548 if (text.endswith(suff)) {
549 text = text.substr(0, text.size()-suff.size());
550 return true;
551 }
552 return false;
553 }
554 };
555
556 while (1) {
557 if (Suff::has("u", text)) {
558 UpperU = false;
559 } else if (Suff::has("U", text)) {
560 UpperU = true;
561 } else if (Suff::has("ll", text)) {
562 UpperL = false;
563 } else if (Suff::has("LL", text)) {
564 UpperL = true;
565 } else if (Suff::has("l", text)) {
566 UpperL = false;
567 } else if (Suff::has("L", text)) {
568 UpperL = true;
569 } else if (isFloat && Suff::has("f", text)) {
570 UpperF = false;
571 } else if (isFloat && Suff::has("F", text)) {
572 UpperF = true;
573 } else
574 break;
575 }
576
577 if (!UpperU.hasValue() && !UpperL.hasValue())
578 UpperU = UpperL = true;
579 else if (UpperU.hasValue() && !UpperL.hasValue())
580 UpperL = UpperU;
581 else if (UpperL.hasValue() && !UpperU.hasValue())
582 UpperU = UpperL;
583
584 Info.U = *UpperU ? "U" : "u";
585 Info.L = *UpperL ? "L" : "l";
586 Info.LL = *UpperL ? "LL" : "ll";
587 Info.F = UpperF ? "F" : "f";
588
589 Info.Hex = Info.Octal = false;
590 if (text.startswith("0x"))
591 Info.Hex = true;
592 else if (!isFloat && !isIntZero && text.startswith("0"))
593 Info.Octal = true;
594
595 SourceLocation B = literalRange.getBegin();
596 Info.WithoutSuffRange =
597 CharSourceRange::getCharRange(B, B.getLocWithOffset(text.size()));
598 return true;
599}
600
601static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
602 const NSAPI &NS, Commit &commit) {
603 if (Msg->getNumArgs() != 1)
604 return false;
605
606 const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts();
607 if (const CharacterLiteral *CharE = dyn_cast<CharacterLiteral>(Arg))
608 return rewriteToCharLiteral(Msg, CharE, NS, commit);
609 if (const ObjCBoolLiteralExpr *BE = dyn_cast<ObjCBoolLiteralExpr>(Arg))
610 return rewriteToBoolLiteral(Msg, BE, NS, commit);
611 if (const CXXBoolLiteralExpr *BE = dyn_cast<CXXBoolLiteralExpr>(Arg))
612 return rewriteToBoolLiteral(Msg, BE, NS, commit);
613
614 const Expr *literalE = Arg;
615 if (const UnaryOperator *UOE = dyn_cast<UnaryOperator>(literalE)) {
616 if (UOE->getOpcode() == UO_Plus || UOE->getOpcode() == UO_Minus)
617 literalE = UOE->getSubExpr();
618 }
619
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000620 // Only integer and floating literals, otherwise try to rewrite to boxed
621 // expression.
Ted Kremenek30660a82012-03-06 20:06:33 +0000622 if (!isa<IntegerLiteral>(literalE) && !isa<FloatingLiteral>(literalE))
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000623 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000624
625 ASTContext &Ctx = NS.getASTContext();
626 Selector Sel = Msg->getSelector();
627 llvm::Optional<NSAPI::NSNumberLiteralMethodKind>
628 MKOpt = NS.getNSNumberLiteralMethodKind(Sel);
629 if (!MKOpt)
630 return false;
631 NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;
632
Benjamin Kramerbea6c0a2012-03-13 17:05:43 +0000633 bool CallIsUnsigned = false, CallIsLong = false, CallIsLongLong = false;
Ted Kremenek30660a82012-03-06 20:06:33 +0000634 bool CallIsFloating = false, CallIsDouble = false;
635
636 switch (MK) {
637 // We cannot have these calls with int/float literals.
638 case NSAPI::NSNumberWithChar:
639 case NSAPI::NSNumberWithUnsignedChar:
640 case NSAPI::NSNumberWithShort:
641 case NSAPI::NSNumberWithUnsignedShort:
642 case NSAPI::NSNumberWithBool:
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000643 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000644
645 case NSAPI::NSNumberWithUnsignedInt:
646 case NSAPI::NSNumberWithUnsignedInteger:
647 CallIsUnsigned = true;
648 case NSAPI::NSNumberWithInt:
649 case NSAPI::NSNumberWithInteger:
Ted Kremenek30660a82012-03-06 20:06:33 +0000650 break;
651
652 case NSAPI::NSNumberWithUnsignedLong:
653 CallIsUnsigned = true;
654 case NSAPI::NSNumberWithLong:
Benjamin Kramerbea6c0a2012-03-13 17:05:43 +0000655 CallIsLong = true;
Ted Kremenek30660a82012-03-06 20:06:33 +0000656 break;
657
658 case NSAPI::NSNumberWithUnsignedLongLong:
659 CallIsUnsigned = true;
660 case NSAPI::NSNumberWithLongLong:
Benjamin Kramerbea6c0a2012-03-13 17:05:43 +0000661 CallIsLongLong = true;
Ted Kremenek30660a82012-03-06 20:06:33 +0000662 break;
663
664 case NSAPI::NSNumberWithDouble:
665 CallIsDouble = true;
666 case NSAPI::NSNumberWithFloat:
667 CallIsFloating = true;
668 break;
669 }
670
671 SourceRange ArgRange = Arg->getSourceRange();
672 QualType ArgTy = Arg->getType();
673 QualType CallTy = Msg->getArg(0)->getType();
674
675 // Check for the easy case, the literal maps directly to the call.
676 if (Ctx.hasSameType(ArgTy, CallTy)) {
677 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
678 commit.insert(ArgRange.getBegin(), "@");
679 return true;
680 }
681
682 // We will need to modify the literal suffix to get the same type as the call.
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000683 // Try with boxed expression if it came from a macro.
Ted Kremenek30660a82012-03-06 20:06:33 +0000684 if (ArgRange.getBegin().isMacroID())
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000685 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000686
687 bool LitIsFloat = ArgTy->isFloatingType();
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000688 // For a float passed to integer call, don't try rewriting to objc literal.
689 // It is difficult and a very uncommon case anyway.
690 // But try with boxed expression.
Ted Kremenek30660a82012-03-06 20:06:33 +0000691 if (LitIsFloat && !CallIsFloating)
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000692 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000693
694 // Try to modify the literal make it the same type as the method call.
695 // -Modify the suffix, and/or
696 // -Change integer to float
697
698 LiteralInfo LitInfo;
699 bool isIntZero = false;
700 if (const IntegerLiteral *IntE = dyn_cast<IntegerLiteral>(literalE))
701 isIntZero = !IntE->getValue().getBoolValue();
702 if (!getLiteralInfo(ArgRange, LitIsFloat, isIntZero, Ctx, LitInfo))
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000703 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000704
705 // Not easy to do int -> float with hex/octal and uncommon anyway.
706 if (!LitIsFloat && CallIsFloating && (LitInfo.Hex || LitInfo.Octal))
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000707 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000708
709 SourceLocation LitB = LitInfo.WithoutSuffRange.getBegin();
710 SourceLocation LitE = LitInfo.WithoutSuffRange.getEnd();
711
712 commit.replaceWithInner(CharSourceRange::getTokenRange(Msg->getSourceRange()),
713 LitInfo.WithoutSuffRange);
714 commit.insert(LitB, "@");
715
716 if (!LitIsFloat && CallIsFloating)
717 commit.insert(LitE, ".0");
718
719 if (CallIsFloating) {
720 if (!CallIsDouble)
721 commit.insert(LitE, LitInfo.F);
722 } else {
723 if (CallIsUnsigned)
724 commit.insert(LitE, LitInfo.U);
725
726 if (CallIsLong)
727 commit.insert(LitE, LitInfo.L);
728 else if (CallIsLongLong)
729 commit.insert(LitE, LitInfo.LL);
730 }
731 return true;
732}
Argyrios Kyrtzidis055b3952012-05-14 22:01:53 +0000733
Argyrios Kyrtzidis20119a82012-05-14 23:33:49 +0000734// FIXME: Make determination of operator precedence more general and
735// make it broadly available.
736static bool subscriptOperatorNeedsParens(const Expr *FullExpr) {
737 const Expr* Expr = FullExpr->IgnoreImpCasts();
738 if (isa<ArraySubscriptExpr>(Expr) ||
739 isa<CallExpr>(Expr) ||
740 isa<DeclRefExpr>(Expr) ||
741 isa<CXXNamedCastExpr>(Expr) ||
742 isa<CXXConstructExpr>(Expr) ||
743 isa<CXXThisExpr>(Expr) ||
744 isa<CXXTypeidExpr>(Expr) ||
745 isa<CXXUnresolvedConstructExpr>(Expr) ||
746 isa<ObjCMessageExpr>(Expr) ||
747 isa<ObjCPropertyRefExpr>(Expr) ||
748 isa<ObjCProtocolExpr>(Expr) ||
749 isa<MemberExpr>(Expr) ||
Argyrios Kyrtzidis2bddd432012-05-22 00:47:53 +0000750 isa<ObjCIvarRefExpr>(Expr) ||
Argyrios Kyrtzidis20119a82012-05-14 23:33:49 +0000751 isa<ParenExpr>(FullExpr) ||
752 isa<ParenListExpr>(Expr) ||
753 isa<SizeOfPackExpr>(Expr))
754 return false;
755
756 return true;
757}
Argyrios Kyrtzidis055b3952012-05-14 22:01:53 +0000758static bool castOperatorNeedsParens(const Expr *FullExpr) {
759 const Expr* Expr = FullExpr->IgnoreImpCasts();
760 if (isa<ArraySubscriptExpr>(Expr) ||
761 isa<CallExpr>(Expr) ||
762 isa<DeclRefExpr>(Expr) ||
763 isa<CastExpr>(Expr) ||
764 isa<CXXNewExpr>(Expr) ||
765 isa<CXXConstructExpr>(Expr) ||
766 isa<CXXDeleteExpr>(Expr) ||
767 isa<CXXNoexceptExpr>(Expr) ||
768 isa<CXXPseudoDestructorExpr>(Expr) ||
769 isa<CXXScalarValueInitExpr>(Expr) ||
770 isa<CXXThisExpr>(Expr) ||
771 isa<CXXTypeidExpr>(Expr) ||
772 isa<CXXUnresolvedConstructExpr>(Expr) ||
773 isa<ObjCMessageExpr>(Expr) ||
774 isa<ObjCPropertyRefExpr>(Expr) ||
775 isa<ObjCProtocolExpr>(Expr) ||
776 isa<MemberExpr>(Expr) ||
Argyrios Kyrtzidis2bddd432012-05-22 00:47:53 +0000777 isa<ObjCIvarRefExpr>(Expr) ||
Argyrios Kyrtzidis055b3952012-05-14 22:01:53 +0000778 isa<ParenExpr>(FullExpr) ||
779 isa<ParenListExpr>(Expr) ||
780 isa<SizeOfPackExpr>(Expr) ||
781 isa<UnaryOperator>(Expr))
782 return false;
783
784 return true;
785}
786
787static void objectifyExpr(const Expr *E, Commit &commit) {
788 if (!E) return;
789
790 QualType T = E->getType();
791 if (T->isObjCObjectPointerType()) {
792 if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) {
793 if (ICE->getCastKind() != CK_CPointerToObjCPointerCast)
794 return;
795 } else {
796 return;
797 }
798 } else if (!T->isPointerType()) {
799 return;
800 }
801
802 SourceRange Range = E->getSourceRange();
803 if (castOperatorNeedsParens(E))
804 commit.insertWrap("(", Range, ")");
805 commit.insertBefore(Range.getBegin(), "(id)");
806}
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000807
808//===----------------------------------------------------------------------===//
809// rewriteToNumericBoxedExpression.
810//===----------------------------------------------------------------------===//
811
Argyrios Kyrtzidisc5d33e92012-05-15 22:59:54 +0000812static bool isEnumConstant(const Expr *E) {
813 if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts()))
814 if (const ValueDecl *VD = DRE->getDecl())
815 return isa<EnumConstantDecl>(VD);
816
817 return false;
818}
819
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000820static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
821 const NSAPI &NS, Commit &commit) {
822 if (Msg->getNumArgs() != 1)
823 return false;
824
825 const Expr *Arg = Msg->getArg(0);
826 if (Arg->isTypeDependent())
827 return false;
828
829 ASTContext &Ctx = NS.getASTContext();
830 Selector Sel = Msg->getSelector();
831 llvm::Optional<NSAPI::NSNumberLiteralMethodKind>
832 MKOpt = NS.getNSNumberLiteralMethodKind(Sel);
833 if (!MKOpt)
834 return false;
835 NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;
836
837 const Expr *OrigArg = Arg->IgnoreImpCasts();
838 QualType FinalTy = Arg->getType();
839 QualType OrigTy = OrigArg->getType();
840 uint64_t FinalTySize = Ctx.getTypeSize(FinalTy);
841 uint64_t OrigTySize = Ctx.getTypeSize(OrigTy);
842
843 bool isTruncated = FinalTySize < OrigTySize;
844 bool needsCast = false;
845
846 if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) {
847 switch (ICE->getCastKind()) {
848 case CK_LValueToRValue:
849 case CK_NoOp:
850 case CK_UserDefinedConversion:
851 break;
852
853 case CK_IntegralCast: {
854 if (MK == NSAPI::NSNumberWithBool && OrigTy->isBooleanType())
855 break;
856 // Be more liberal with Integer/UnsignedInteger which are very commonly
857 // used.
858 if ((MK == NSAPI::NSNumberWithInteger ||
859 MK == NSAPI::NSNumberWithUnsignedInteger) &&
860 !isTruncated) {
Argyrios Kyrtzidisc5d33e92012-05-15 22:59:54 +0000861 if (OrigTy->getAs<EnumType>() || isEnumConstant(OrigArg))
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000862 break;
863 if ((MK==NSAPI::NSNumberWithInteger) == OrigTy->isSignedIntegerType() &&
864 OrigTySize >= Ctx.getTypeSize(Ctx.IntTy))
865 break;
866 }
867
868 needsCast = true;
869 break;
870 }
871
872 case CK_PointerToBoolean:
873 case CK_IntegralToBoolean:
874 case CK_IntegralToFloating:
875 case CK_FloatingToIntegral:
876 case CK_FloatingToBoolean:
877 case CK_FloatingCast:
878 case CK_FloatingComplexToReal:
879 case CK_FloatingComplexToBoolean:
880 case CK_IntegralComplexToReal:
881 case CK_IntegralComplexToBoolean:
882 case CK_AtomicToNonAtomic:
883 needsCast = true;
884 break;
885
886 case CK_Dependent:
887 case CK_BitCast:
888 case CK_LValueBitCast:
889 case CK_BaseToDerived:
890 case CK_DerivedToBase:
891 case CK_UncheckedDerivedToBase:
892 case CK_Dynamic:
893 case CK_ToUnion:
894 case CK_ArrayToPointerDecay:
895 case CK_FunctionToPointerDecay:
896 case CK_NullToPointer:
897 case CK_NullToMemberPointer:
898 case CK_BaseToDerivedMemberPointer:
899 case CK_DerivedToBaseMemberPointer:
900 case CK_MemberPointerToBoolean:
901 case CK_ReinterpretMemberPointer:
902 case CK_ConstructorConversion:
903 case CK_IntegralToPointer:
904 case CK_PointerToIntegral:
905 case CK_ToVoid:
906 case CK_VectorSplat:
907 case CK_CPointerToObjCPointerCast:
908 case CK_BlockPointerToObjCPointerCast:
909 case CK_AnyPointerToBlockPointerCast:
910 case CK_ObjCObjectLValueCast:
911 case CK_FloatingRealToComplex:
912 case CK_FloatingComplexCast:
913 case CK_FloatingComplexToIntegralComplex:
914 case CK_IntegralRealToComplex:
915 case CK_IntegralComplexCast:
916 case CK_IntegralComplexToFloatingComplex:
917 case CK_ARCProduceObject:
918 case CK_ARCConsumeObject:
919 case CK_ARCReclaimReturnedObject:
920 case CK_ARCExtendBlockObject:
921 case CK_NonAtomicToAtomic:
922 case CK_CopyAndAutoreleaseBlockObject:
Eli Friedmana6c66ce2012-08-31 00:14:07 +0000923 case CK_BuiltinFnToFnPtr:
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000924 return false;
925 }
926 }
927
Argyrios Kyrtzidis013a2542012-05-24 16:48:23 +0000928 if (needsCast) {
929 DiagnosticsEngine &Diags = Ctx.getDiagnostics();
930 // FIXME: Use a custom category name to distinguish migration diagnostics.
931 unsigned diagID = Diags.getCustomDiagID(DiagnosticsEngine::Warning,
Argyrios Kyrtzidis8b9fcd72012-06-20 01:28:32 +0000932 "converting to boxing syntax requires casting %0 to %1");
933 Diags.Report(Msg->getExprLoc(), diagID) << OrigTy << FinalTy
934 << Msg->getSourceRange();
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000935 return false;
Argyrios Kyrtzidis013a2542012-05-24 16:48:23 +0000936 }
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000937
938 SourceRange ArgRange = OrigArg->getSourceRange();
Argyrios Kyrtzidis7fe103c2012-05-15 22:22:10 +0000939 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000940
941 if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
942 commit.insertBefore(ArgRange.getBegin(), "@");
943 else
944 commit.insertWrap("@(", ArgRange, ")");
945
946 return true;
947}
Argyrios Kyrtzidis7fe103c2012-05-15 22:22:10 +0000948
949//===----------------------------------------------------------------------===//
950// rewriteToStringBoxedExpression.
951//===----------------------------------------------------------------------===//
952
953static bool doRewriteToUTF8StringBoxedExpressionHelper(
954 const ObjCMessageExpr *Msg,
955 const NSAPI &NS, Commit &commit) {
956 const Expr *Arg = Msg->getArg(0);
957 if (Arg->isTypeDependent())
958 return false;
959
Argyrios Kyrtzidis07736592012-05-16 00:21:21 +0000960 ASTContext &Ctx = NS.getASTContext();
961
Argyrios Kyrtzidis7fe103c2012-05-15 22:22:10 +0000962 const Expr *OrigArg = Arg->IgnoreImpCasts();
963 QualType OrigTy = OrigArg->getType();
Argyrios Kyrtzidis07736592012-05-16 00:21:21 +0000964 if (OrigTy->isArrayType())
965 OrigTy = Ctx.getArrayDecayedType(OrigTy);
Argyrios Kyrtzidis7fe103c2012-05-15 22:22:10 +0000966
967 if (const StringLiteral *
968 StrE = dyn_cast<StringLiteral>(OrigArg->IgnoreParens())) {
969 commit.replaceWithInner(Msg->getSourceRange(), StrE->getSourceRange());
970 commit.insert(StrE->getLocStart(), "@");
971 return true;
972 }
973
Argyrios Kyrtzidis7fe103c2012-05-15 22:22:10 +0000974 if (const PointerType *PT = OrigTy->getAs<PointerType>()) {
975 QualType PointeeType = PT->getPointeeType();
976 if (Ctx.hasSameUnqualifiedType(PointeeType, Ctx.CharTy)) {
977 SourceRange ArgRange = OrigArg->getSourceRange();
978 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
979
980 if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
981 commit.insertBefore(ArgRange.getBegin(), "@");
982 else
983 commit.insertWrap("@(", ArgRange, ")");
984
985 return true;
986 }
987 }
988
989 return false;
990}
991
992static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
993 const NSAPI &NS, Commit &commit) {
994 Selector Sel = Msg->getSelector();
995
996 if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithUTF8String) ||
997 Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCString)) {
998 if (Msg->getNumArgs() != 1)
999 return false;
1000 return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);
1001 }
1002
1003 if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCStringEncoding)) {
1004 if (Msg->getNumArgs() != 2)
1005 return false;
1006
1007 const Expr *encodingArg = Msg->getArg(1);
1008 if (NS.isNSUTF8StringEncodingConstant(encodingArg) ||
1009 NS.isNSASCIIStringEncodingConstant(encodingArg))
1010 return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);
1011 }
1012
1013 return false;
1014}