blob: 215aacdad10833a9f42843b68de527877efec0e9 [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"
Chandler Carruth3a022472012-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"
Argyrios Kyrtzidis6b4f3412013-01-16 23:54:48 +000019#include "clang/AST/ParentMap.h"
Ted Kremenekf7639e12012-03-06 20:06:33 +000020#include "clang/Edit/Commit.h"
21#include "clang/Lex/Lexer.h"
Ted Kremenekf7639e12012-03-06 20:06:33 +000022
23using namespace clang;
24using namespace edit;
25
26static bool checkForLiteralCreation(const ObjCMessageExpr *Msg,
Argyrios Kyrtzidis63aebfb2012-06-06 22:23:12 +000027 IdentifierInfo *&ClassId,
28 const LangOptions &LangOpts) {
Ted Kremenekf7639e12012-03-06 20:06:33 +000029 if (!Msg || Msg->isImplicit() || !Msg->getMethodDecl())
30 return false;
31
32 const ObjCInterfaceDecl *Receiver = Msg->getReceiverInterface();
33 if (!Receiver)
34 return false;
35 ClassId = Receiver->getIdentifier();
36
37 if (Msg->getReceiverKind() == ObjCMessageExpr::Class)
38 return true;
39
Argyrios Kyrtzidis63aebfb2012-06-06 22:23:12 +000040 // When in ARC mode we also convert "[[.. alloc] init]" messages to literals,
41 // since the change from +1 to +0 will be handled fine by ARC.
42 if (LangOpts.ObjCAutoRefCount) {
43 if (Msg->getReceiverKind() == ObjCMessageExpr::Instance) {
44 if (const ObjCMessageExpr *Rec = dyn_cast<ObjCMessageExpr>(
45 Msg->getInstanceReceiver()->IgnoreParenImpCasts())) {
46 if (Rec->getMethodFamily() == OMF_alloc)
47 return true;
48 }
49 }
50 }
51
Ted Kremenekf7639e12012-03-06 20:06:33 +000052 return false;
53}
54
55//===----------------------------------------------------------------------===//
56// rewriteObjCRedundantCallWithLiteral.
57//===----------------------------------------------------------------------===//
58
59bool edit::rewriteObjCRedundantCallWithLiteral(const ObjCMessageExpr *Msg,
60 const NSAPI &NS, Commit &commit) {
61 IdentifierInfo *II = 0;
Argyrios Kyrtzidis63aebfb2012-06-06 22:23:12 +000062 if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
Ted Kremenekf7639e12012-03-06 20:06:33 +000063 return false;
64 if (Msg->getNumArgs() != 1)
65 return false;
66
67 const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts();
68 Selector Sel = Msg->getSelector();
69
70 if ((isa<ObjCStringLiteral>(Arg) &&
71 NS.getNSClassId(NSAPI::ClassId_NSString) == II &&
Argyrios Kyrtzidis63aebfb2012-06-06 22:23:12 +000072 (NS.getNSStringSelector(NSAPI::NSStr_stringWithString) == Sel ||
73 NS.getNSStringSelector(NSAPI::NSStr_initWithString) == Sel)) ||
Ted Kremenekf7639e12012-03-06 20:06:33 +000074
75 (isa<ObjCArrayLiteral>(Arg) &&
76 NS.getNSClassId(NSAPI::ClassId_NSArray) == II &&
Argyrios Kyrtzidis63aebfb2012-06-06 22:23:12 +000077 (NS.getNSArraySelector(NSAPI::NSArr_arrayWithArray) == Sel ||
78 NS.getNSArraySelector(NSAPI::NSArr_initWithArray) == Sel)) ||
Ted Kremenekf7639e12012-03-06 20:06:33 +000079
80 (isa<ObjCDictionaryLiteral>(Arg) &&
81 NS.getNSClassId(NSAPI::ClassId_NSDictionary) == II &&
Argyrios Kyrtzidis63aebfb2012-06-06 22:23:12 +000082 (NS.getNSDictionarySelector(
83 NSAPI::NSDict_dictionaryWithDictionary) == Sel ||
84 NS.getNSDictionarySelector(NSAPI::NSDict_initWithDictionary) == Sel))) {
Ted Kremenekf7639e12012-03-06 20:06:33 +000085
86 commit.replaceWithInner(Msg->getSourceRange(),
87 Msg->getArg(0)->getSourceRange());
88 return true;
89 }
90
91 return false;
92}
93
94//===----------------------------------------------------------------------===//
95// rewriteToObjCSubscriptSyntax.
96//===----------------------------------------------------------------------===//
97
Argyrios Kyrtzidis89b928e2012-07-06 00:07:09 +000098/// \brief Check for classes that accept 'objectForKey:' (or the other selectors
99/// that the migrator handles) but return their instances as 'id', resulting
100/// in the compiler resolving 'objectForKey:' as the method from NSDictionary.
101///
102/// When checking if we can convert to subscripting syntax, check whether
103/// the receiver is a result of a class method from a hardcoded list of
104/// such classes. In such a case return the specific class as the interface
105/// of the receiver.
106///
107/// FIXME: Remove this when these classes start using 'instancetype'.
108static const ObjCInterfaceDecl *
109maybeAdjustInterfaceForSubscriptingCheck(const ObjCInterfaceDecl *IFace,
110 const Expr *Receiver,
111 ASTContext &Ctx) {
112 assert(IFace && Receiver);
113
114 // If the receiver has type 'id'...
115 if (!Ctx.isObjCIdType(Receiver->getType().getUnqualifiedType()))
116 return IFace;
117
118 const ObjCMessageExpr *
119 InnerMsg = dyn_cast<ObjCMessageExpr>(Receiver->IgnoreParenCasts());
120 if (!InnerMsg)
121 return IFace;
122
123 QualType ClassRec;
124 switch (InnerMsg->getReceiverKind()) {
125 case ObjCMessageExpr::Instance:
126 case ObjCMessageExpr::SuperInstance:
127 return IFace;
128
129 case ObjCMessageExpr::Class:
130 ClassRec = InnerMsg->getClassReceiver();
131 break;
132 case ObjCMessageExpr::SuperClass:
133 ClassRec = InnerMsg->getSuperType();
134 break;
135 }
136
137 if (ClassRec.isNull())
138 return IFace;
139
140 // ...and it is the result of a class message...
141
142 const ObjCObjectType *ObjTy = ClassRec->getAs<ObjCObjectType>();
143 if (!ObjTy)
144 return IFace;
145 const ObjCInterfaceDecl *OID = ObjTy->getInterface();
146
147 // ...and the receiving class is NSMapTable or NSLocale, return that
148 // class as the receiving interface.
149 if (OID->getName() == "NSMapTable" ||
150 OID->getName() == "NSLocale")
151 return OID;
152
153 return IFace;
154}
155
156static bool canRewriteToSubscriptSyntax(const ObjCInterfaceDecl *&IFace,
157 const ObjCMessageExpr *Msg,
158 ASTContext &Ctx,
Argyrios Kyrtzidis13b92922012-07-05 21:49:51 +0000159 Selector subscriptSel) {
Argyrios Kyrtzidis89b928e2012-07-06 00:07:09 +0000160 const Expr *Rec = Msg->getInstanceReceiver();
161 if (!Rec)
162 return false;
163 IFace = maybeAdjustInterfaceForSubscriptingCheck(IFace, Rec, Ctx);
164
Argyrios Kyrtzidis13b92922012-07-05 21:49:51 +0000165 if (const ObjCMethodDecl *MD = IFace->lookupInstanceMethod(subscriptSel)) {
166 if (!MD->isUnavailable())
167 return true;
168 }
169 return false;
170}
171
Argyrios Kyrtzidis0bbe94f2012-05-14 23:33:49 +0000172static bool subscriptOperatorNeedsParens(const Expr *FullExpr);
173
Ted Kremenekf7639e12012-03-06 20:06:33 +0000174static void maybePutParensOnReceiver(const Expr *Receiver, Commit &commit) {
Argyrios Kyrtzidis0bbe94f2012-05-14 23:33:49 +0000175 if (subscriptOperatorNeedsParens(Receiver)) {
Ted Kremenekf7639e12012-03-06 20:06:33 +0000176 SourceRange RecRange = Receiver->getSourceRange();
177 commit.insertWrap("(", RecRange, ")");
178 }
179}
180
Argyrios Kyrtzidis6310fdd2012-06-04 21:23:26 +0000181static bool rewriteToSubscriptGetCommon(const ObjCMessageExpr *Msg,
182 Commit &commit) {
Ted Kremenekf7639e12012-03-06 20:06:33 +0000183 if (Msg->getNumArgs() != 1)
184 return false;
185 const Expr *Rec = Msg->getInstanceReceiver();
186 if (!Rec)
187 return false;
188
189 SourceRange MsgRange = Msg->getSourceRange();
190 SourceRange RecRange = Rec->getSourceRange();
191 SourceRange ArgRange = Msg->getArg(0)->getSourceRange();
192
193 commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
194 ArgRange.getBegin()),
195 CharSourceRange::getTokenRange(RecRange));
196 commit.replaceWithInner(SourceRange(ArgRange.getBegin(), MsgRange.getEnd()),
197 ArgRange);
198 commit.insertWrap("[", ArgRange, "]");
199 maybePutParensOnReceiver(Rec, commit);
200 return true;
201}
202
Argyrios Kyrtzidis6310fdd2012-06-04 21:23:26 +0000203static bool rewriteToArraySubscriptGet(const ObjCInterfaceDecl *IFace,
204 const ObjCMessageExpr *Msg,
205 const NSAPI &NS,
206 Commit &commit) {
Argyrios Kyrtzidis89b928e2012-07-06 00:07:09 +0000207 if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
Argyrios Kyrtzidis13b92922012-07-05 21:49:51 +0000208 NS.getObjectAtIndexedSubscriptSelector()))
Argyrios Kyrtzidis6310fdd2012-06-04 21:23:26 +0000209 return false;
210 return rewriteToSubscriptGetCommon(Msg, commit);
211}
212
213static bool rewriteToDictionarySubscriptGet(const ObjCInterfaceDecl *IFace,
214 const ObjCMessageExpr *Msg,
215 const NSAPI &NS,
216 Commit &commit) {
Argyrios Kyrtzidis89b928e2012-07-06 00:07:09 +0000217 if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
Argyrios Kyrtzidis13b92922012-07-05 21:49:51 +0000218 NS.getObjectForKeyedSubscriptSelector()))
Argyrios Kyrtzidis6310fdd2012-06-04 21:23:26 +0000219 return false;
220 return rewriteToSubscriptGetCommon(Msg, commit);
221}
222
223static bool rewriteToArraySubscriptSet(const ObjCInterfaceDecl *IFace,
224 const ObjCMessageExpr *Msg,
225 const NSAPI &NS,
Ted Kremenekf7639e12012-03-06 20:06:33 +0000226 Commit &commit) {
Argyrios Kyrtzidis89b928e2012-07-06 00:07:09 +0000227 if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
Argyrios Kyrtzidis13b92922012-07-05 21:49:51 +0000228 NS.getSetObjectAtIndexedSubscriptSelector()))
229 return false;
230
Ted Kremenekf7639e12012-03-06 20:06:33 +0000231 if (Msg->getNumArgs() != 2)
232 return false;
233 const Expr *Rec = Msg->getInstanceReceiver();
234 if (!Rec)
235 return false;
236
237 SourceRange MsgRange = Msg->getSourceRange();
238 SourceRange RecRange = Rec->getSourceRange();
239 SourceRange Arg0Range = Msg->getArg(0)->getSourceRange();
240 SourceRange Arg1Range = Msg->getArg(1)->getSourceRange();
241
242 commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
243 Arg0Range.getBegin()),
244 CharSourceRange::getTokenRange(RecRange));
245 commit.replaceWithInner(CharSourceRange::getCharRange(Arg0Range.getBegin(),
246 Arg1Range.getBegin()),
247 CharSourceRange::getTokenRange(Arg0Range));
248 commit.replaceWithInner(SourceRange(Arg1Range.getBegin(), MsgRange.getEnd()),
249 Arg1Range);
250 commit.insertWrap("[", CharSourceRange::getCharRange(Arg0Range.getBegin(),
251 Arg1Range.getBegin()),
252 "] = ");
253 maybePutParensOnReceiver(Rec, commit);
254 return true;
255}
256
Argyrios Kyrtzidis6310fdd2012-06-04 21:23:26 +0000257static bool rewriteToDictionarySubscriptSet(const ObjCInterfaceDecl *IFace,
258 const ObjCMessageExpr *Msg,
259 const NSAPI &NS,
Ted Kremenekf7639e12012-03-06 20:06:33 +0000260 Commit &commit) {
Argyrios Kyrtzidis89b928e2012-07-06 00:07:09 +0000261 if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
Argyrios Kyrtzidis13b92922012-07-05 21:49:51 +0000262 NS.getSetObjectForKeyedSubscriptSelector()))
263 return false;
264
Ted Kremenekf7639e12012-03-06 20:06:33 +0000265 if (Msg->getNumArgs() != 2)
266 return false;
267 const Expr *Rec = Msg->getInstanceReceiver();
268 if (!Rec)
269 return false;
270
271 SourceRange MsgRange = Msg->getSourceRange();
272 SourceRange RecRange = Rec->getSourceRange();
273 SourceRange Arg0Range = Msg->getArg(0)->getSourceRange();
274 SourceRange Arg1Range = Msg->getArg(1)->getSourceRange();
275
276 SourceLocation LocBeforeVal = Arg0Range.getBegin();
277 commit.insertBefore(LocBeforeVal, "] = ");
278 commit.insertFromRange(LocBeforeVal, Arg1Range, /*afterToken=*/false,
279 /*beforePreviousInsertions=*/true);
280 commit.insertBefore(LocBeforeVal, "[");
281 commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
282 Arg0Range.getBegin()),
283 CharSourceRange::getTokenRange(RecRange));
284 commit.replaceWithInner(SourceRange(Arg0Range.getBegin(), MsgRange.getEnd()),
285 Arg0Range);
286 maybePutParensOnReceiver(Rec, commit);
287 return true;
288}
289
290bool edit::rewriteToObjCSubscriptSyntax(const ObjCMessageExpr *Msg,
Argyrios Kyrtzidis6310fdd2012-06-04 21:23:26 +0000291 const NSAPI &NS, Commit &commit) {
Ted Kremenekf7639e12012-03-06 20:06:33 +0000292 if (!Msg || Msg->isImplicit() ||
293 Msg->getReceiverKind() != ObjCMessageExpr::Instance)
294 return false;
295 const ObjCMethodDecl *Method = Msg->getMethodDecl();
296 if (!Method)
297 return false;
298
299 const ObjCInterfaceDecl *
300 IFace = NS.getASTContext().getObjContainingInterface(
301 const_cast<ObjCMethodDecl *>(Method));
302 if (!IFace)
303 return false;
Ted Kremenekf7639e12012-03-06 20:06:33 +0000304 Selector Sel = Msg->getSelector();
305
Argyrios Kyrtzidis13b92922012-07-05 21:49:51 +0000306 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_objectAtIndex))
Argyrios Kyrtzidis6310fdd2012-06-04 21:23:26 +0000307 return rewriteToArraySubscriptGet(IFace, Msg, NS, commit);
308
Argyrios Kyrtzidis13b92922012-07-05 21:49:51 +0000309 if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_objectForKey))
Argyrios Kyrtzidis6310fdd2012-06-04 21:23:26 +0000310 return rewriteToDictionarySubscriptGet(IFace, Msg, NS, commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000311
312 if (Msg->getNumArgs() != 2)
313 return false;
314
Argyrios Kyrtzidis13b92922012-07-05 21:49:51 +0000315 if (Sel == NS.getNSArraySelector(NSAPI::NSMutableArr_replaceObjectAtIndex))
Argyrios Kyrtzidis6310fdd2012-06-04 21:23:26 +0000316 return rewriteToArraySubscriptSet(IFace, Msg, NS, commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000317
Argyrios Kyrtzidis13b92922012-07-05 21:49:51 +0000318 if (Sel == NS.getNSDictionarySelector(NSAPI::NSMutableDict_setObjectForKey))
Argyrios Kyrtzidis6310fdd2012-06-04 21:23:26 +0000319 return rewriteToDictionarySubscriptSet(IFace, Msg, NS, commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000320
321 return false;
322}
323
324//===----------------------------------------------------------------------===//
325// rewriteToObjCLiteralSyntax.
326//===----------------------------------------------------------------------===//
327
328static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
Argyrios Kyrtzidis6b4f3412013-01-16 23:54:48 +0000329 const NSAPI &NS, Commit &commit,
330 const ParentMap *PMap);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000331static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
332 const NSAPI &NS, Commit &commit);
333static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
334 const NSAPI &NS, Commit &commit);
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000335static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
336 const NSAPI &NS, Commit &commit);
Argyrios Kyrtzidis7bd957c2012-05-15 22:22:10 +0000337static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
338 const NSAPI &NS, Commit &commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000339
340bool edit::rewriteToObjCLiteralSyntax(const ObjCMessageExpr *Msg,
Argyrios Kyrtzidis6b4f3412013-01-16 23:54:48 +0000341 const NSAPI &NS, Commit &commit,
342 const ParentMap *PMap) {
Ted Kremenekf7639e12012-03-06 20:06:33 +0000343 IdentifierInfo *II = 0;
Argyrios Kyrtzidis63aebfb2012-06-06 22:23:12 +0000344 if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
Ted Kremenekf7639e12012-03-06 20:06:33 +0000345 return false;
346
347 if (II == NS.getNSClassId(NSAPI::ClassId_NSArray))
Argyrios Kyrtzidis6b4f3412013-01-16 23:54:48 +0000348 return rewriteToArrayLiteral(Msg, NS, commit, PMap);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000349 if (II == NS.getNSClassId(NSAPI::ClassId_NSDictionary))
350 return rewriteToDictionaryLiteral(Msg, NS, commit);
351 if (II == NS.getNSClassId(NSAPI::ClassId_NSNumber))
352 return rewriteToNumberLiteral(Msg, NS, commit);
Argyrios Kyrtzidis7bd957c2012-05-15 22:22:10 +0000353 if (II == NS.getNSClassId(NSAPI::ClassId_NSString))
354 return rewriteToStringBoxedExpression(Msg, NS, commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000355
356 return false;
357}
358
Argyrios Kyrtzidis6b4f3412013-01-16 23:54:48 +0000359/// \brief Returns true if the immediate message arguments of \c Msg should not
360/// be rewritten because it will interfere with the rewrite of the parent
361/// message expression. e.g.
362/// \code
363/// [NSDictionary dictionaryWithObjects:
364/// [NSArray arrayWithObjects:@"1", @"2", nil]
365/// forKeys:[NSArray arrayWithObjects:@"A", @"B", nil]];
366/// \endcode
367/// It will return true for this because we are going to rewrite this directly
368/// to a dictionary literal without any array literals.
369static bool shouldNotRewriteImmediateMessageArgs(const ObjCMessageExpr *Msg,
370 const NSAPI &NS);
371
Ted Kremenekf7639e12012-03-06 20:06:33 +0000372//===----------------------------------------------------------------------===//
373// rewriteToArrayLiteral.
374//===----------------------------------------------------------------------===//
375
Argyrios Kyrtzidisc1dfed62012-05-14 22:01:53 +0000376/// \brief Adds an explicit cast to 'id' if the type is not objc object.
377static void objectifyExpr(const Expr *E, Commit &commit);
378
Ted Kremenekf7639e12012-03-06 20:06:33 +0000379static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
Argyrios Kyrtzidis6b4f3412013-01-16 23:54:48 +0000380 const NSAPI &NS, Commit &commit,
381 const ParentMap *PMap) {
382 if (PMap) {
383 const ObjCMessageExpr *ParentMsg =
384 dyn_cast_or_null<ObjCMessageExpr>(PMap->getParentIgnoreParenCasts(Msg));
385 if (shouldNotRewriteImmediateMessageArgs(ParentMsg, NS))
386 return false;
387 }
388
Ted Kremenekf7639e12012-03-06 20:06:33 +0000389 Selector Sel = Msg->getSelector();
390 SourceRange MsgRange = Msg->getSourceRange();
391
392 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array)) {
393 if (Msg->getNumArgs() != 0)
394 return false;
395 commit.replace(MsgRange, "@[]");
396 return true;
397 }
398
399 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) {
400 if (Msg->getNumArgs() != 1)
401 return false;
Argyrios Kyrtzidisc1dfed62012-05-14 22:01:53 +0000402 objectifyExpr(Msg->getArg(0), commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000403 SourceRange ArgRange = Msg->getArg(0)->getSourceRange();
404 commit.replaceWithInner(MsgRange, ArgRange);
405 commit.insertWrap("@[", ArgRange, "]");
406 return true;
407 }
408
Argyrios Kyrtzidis63aebfb2012-06-06 22:23:12 +0000409 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) ||
410 Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) {
Ted Kremenekf7639e12012-03-06 20:06:33 +0000411 if (Msg->getNumArgs() == 0)
412 return false;
413 const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1);
414 if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
415 return false;
416
Argyrios Kyrtzidisc1dfed62012-05-14 22:01:53 +0000417 for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i)
418 objectifyExpr(Msg->getArg(i), commit);
419
Ted Kremenekf7639e12012-03-06 20:06:33 +0000420 if (Msg->getNumArgs() == 1) {
421 commit.replace(MsgRange, "@[]");
422 return true;
423 }
424 SourceRange ArgRange(Msg->getArg(0)->getLocStart(),
425 Msg->getArg(Msg->getNumArgs()-2)->getLocEnd());
426 commit.replaceWithInner(MsgRange, ArgRange);
427 commit.insertWrap("@[", ArgRange, "]");
428 return true;
429 }
430
431 return false;
432}
433
434//===----------------------------------------------------------------------===//
435// rewriteToDictionaryLiteral.
436//===----------------------------------------------------------------------===//
437
Argyrios Kyrtzidis6b4f3412013-01-16 23:54:48 +0000438/// \brief If \c Msg is an NSArray creation message or literal, this gets the
439/// objects that were used to create it.
440/// \returns true if it is an NSArray and we got objects, or false otherwise.
441static bool getNSArrayObjects(const Expr *E, const NSAPI &NS,
442 SmallVectorImpl<const Expr *> &Objs) {
443 if (!E)
444 return false;
445
446 E = E->IgnoreParenCasts();
447 if (!E)
448 return false;
449
450 if (const ObjCMessageExpr *Msg = dyn_cast<ObjCMessageExpr>(E)) {
451 IdentifierInfo *Cls = 0;
452 if (!checkForLiteralCreation(Msg, Cls, NS.getASTContext().getLangOpts()))
453 return false;
454
455 if (Cls != NS.getNSClassId(NSAPI::ClassId_NSArray))
456 return false;
457
458 Selector Sel = Msg->getSelector();
459 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array))
460 return true; // empty array.
461
462 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) {
463 if (Msg->getNumArgs() != 1)
464 return false;
465 Objs.push_back(Msg->getArg(0));
466 return true;
467 }
468
469 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) ||
470 Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) {
471 if (Msg->getNumArgs() == 0)
472 return false;
473 const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1);
474 if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
475 return false;
476
477 for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i)
478 Objs.push_back(Msg->getArg(i));
479 return true;
480 }
481
482 } else if (const ObjCArrayLiteral *ArrLit = dyn_cast<ObjCArrayLiteral>(E)) {
483 for (unsigned i = 0, e = ArrLit->getNumElements(); i != e; ++i)
484 Objs.push_back(ArrLit->getElement(i));
485 return true;
486 }
487
488 return false;
489}
490
Ted Kremenekf7639e12012-03-06 20:06:33 +0000491static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
492 const NSAPI &NS, Commit &commit) {
493 Selector Sel = Msg->getSelector();
494 SourceRange MsgRange = Msg->getSourceRange();
495
496 if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_dictionary)) {
497 if (Msg->getNumArgs() != 0)
498 return false;
499 commit.replace(MsgRange, "@{}");
500 return true;
501 }
502
503 if (Sel == NS.getNSDictionarySelector(
504 NSAPI::NSDict_dictionaryWithObjectForKey)) {
505 if (Msg->getNumArgs() != 2)
506 return false;
Argyrios Kyrtzidisc1dfed62012-05-14 22:01:53 +0000507
508 objectifyExpr(Msg->getArg(0), commit);
509 objectifyExpr(Msg->getArg(1), commit);
510
Ted Kremenekf7639e12012-03-06 20:06:33 +0000511 SourceRange ValRange = Msg->getArg(0)->getSourceRange();
512 SourceRange KeyRange = Msg->getArg(1)->getSourceRange();
513 // Insert key before the value.
514 commit.insertBefore(ValRange.getBegin(), ": ");
515 commit.insertFromRange(ValRange.getBegin(),
516 CharSourceRange::getTokenRange(KeyRange),
517 /*afterToken=*/false, /*beforePreviousInsertions=*/true);
518 commit.insertBefore(ValRange.getBegin(), "@{");
519 commit.insertAfterToken(ValRange.getEnd(), "}");
520 commit.replaceWithInner(MsgRange, ValRange);
521 return true;
522 }
523
524 if (Sel == NS.getNSDictionarySelector(
Argyrios Kyrtzidis63aebfb2012-06-06 22:23:12 +0000525 NSAPI::NSDict_dictionaryWithObjectsAndKeys) ||
526 Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsAndKeys)) {
Ted Kremenekf7639e12012-03-06 20:06:33 +0000527 if (Msg->getNumArgs() % 2 != 1)
528 return false;
529 unsigned SentinelIdx = Msg->getNumArgs() - 1;
530 const Expr *SentinelExpr = Msg->getArg(SentinelIdx);
531 if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
532 return false;
533
534 if (Msg->getNumArgs() == 1) {
535 commit.replace(MsgRange, "@{}");
536 return true;
537 }
538
539 for (unsigned i = 0; i < SentinelIdx; i += 2) {
Argyrios Kyrtzidisc1dfed62012-05-14 22:01:53 +0000540 objectifyExpr(Msg->getArg(i), commit);
541 objectifyExpr(Msg->getArg(i+1), commit);
542
Ted Kremenekf7639e12012-03-06 20:06:33 +0000543 SourceRange ValRange = Msg->getArg(i)->getSourceRange();
544 SourceRange KeyRange = Msg->getArg(i+1)->getSourceRange();
545 // Insert value after key.
546 commit.insertAfterToken(KeyRange.getEnd(), ": ");
547 commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true);
548 commit.remove(CharSourceRange::getCharRange(ValRange.getBegin(),
549 KeyRange.getBegin()));
550 }
551 // Range of arguments up until and including the last key.
552 // The sentinel and first value are cut off, the value will move after the
553 // key.
554 SourceRange ArgRange(Msg->getArg(1)->getLocStart(),
555 Msg->getArg(SentinelIdx-1)->getLocEnd());
556 commit.insertWrap("@{", ArgRange, "}");
557 commit.replaceWithInner(MsgRange, ArgRange);
558 return true;
559 }
560
Argyrios Kyrtzidis6b4f3412013-01-16 23:54:48 +0000561 if (Sel == NS.getNSDictionarySelector(
562 NSAPI::NSDict_dictionaryWithObjectsForKeys) ||
563 Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsForKeys)) {
564 if (Msg->getNumArgs() != 2)
565 return false;
566
567 SmallVector<const Expr *, 8> Vals;
568 if (!getNSArrayObjects(Msg->getArg(0), NS, Vals))
569 return false;
570
571 SmallVector<const Expr *, 8> Keys;
572 if (!getNSArrayObjects(Msg->getArg(1), NS, Keys))
573 return false;
574
575 if (Vals.size() != Keys.size())
576 return false;
577
578 if (Vals.empty()) {
579 commit.replace(MsgRange, "@{}");
580 return true;
581 }
582
583 for (unsigned i = 0, n = Vals.size(); i < n; ++i) {
584 objectifyExpr(Vals[i], commit);
585 objectifyExpr(Keys[i], commit);
586
587 SourceRange ValRange = Vals[i]->getSourceRange();
588 SourceRange KeyRange = Keys[i]->getSourceRange();
589 // Insert value after key.
590 commit.insertAfterToken(KeyRange.getEnd(), ": ");
591 commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true);
592 }
593 // Range of arguments up until and including the last key.
594 // The first value is cut off, the value will move after the key.
595 SourceRange ArgRange(Keys.front()->getLocStart(),
596 Keys.back()->getLocEnd());
597 commit.insertWrap("@{", ArgRange, "}");
598 commit.replaceWithInner(MsgRange, ArgRange);
599 return true;
600 }
601
602 return false;
603}
604
605static bool shouldNotRewriteImmediateMessageArgs(const ObjCMessageExpr *Msg,
606 const NSAPI &NS) {
607 if (!Msg)
608 return false;
609
610 IdentifierInfo *II = 0;
611 if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
612 return false;
613
614 if (II != NS.getNSClassId(NSAPI::ClassId_NSDictionary))
615 return false;
616
617 Selector Sel = Msg->getSelector();
618 if (Sel == NS.getNSDictionarySelector(
619 NSAPI::NSDict_dictionaryWithObjectsForKeys) ||
620 Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsForKeys)) {
621 if (Msg->getNumArgs() != 2)
622 return false;
623
624 SmallVector<const Expr *, 8> Vals;
625 if (!getNSArrayObjects(Msg->getArg(0), NS, Vals))
626 return false;
627
628 SmallVector<const Expr *, 8> Keys;
629 if (!getNSArrayObjects(Msg->getArg(1), NS, Keys))
630 return false;
631
632 if (Vals.size() != Keys.size())
633 return false;
634
635 return true;
636 }
637
Ted Kremenekf7639e12012-03-06 20:06:33 +0000638 return false;
639}
640
641//===----------------------------------------------------------------------===//
642// rewriteToNumberLiteral.
643//===----------------------------------------------------------------------===//
644
645static bool rewriteToCharLiteral(const ObjCMessageExpr *Msg,
646 const CharacterLiteral *Arg,
647 const NSAPI &NS, Commit &commit) {
648 if (Arg->getKind() != CharacterLiteral::Ascii)
649 return false;
650 if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithChar,
651 Msg->getSelector())) {
652 SourceRange ArgRange = Arg->getSourceRange();
653 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
654 commit.insert(ArgRange.getBegin(), "@");
655 return true;
656 }
657
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000658 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000659}
660
661static bool rewriteToBoolLiteral(const ObjCMessageExpr *Msg,
662 const Expr *Arg,
663 const NSAPI &NS, Commit &commit) {
664 if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithBool,
665 Msg->getSelector())) {
666 SourceRange ArgRange = Arg->getSourceRange();
667 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
668 commit.insert(ArgRange.getBegin(), "@");
669 return true;
670 }
671
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000672 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000673}
674
675namespace {
676
677struct LiteralInfo {
678 bool Hex, Octal;
679 StringRef U, F, L, LL;
680 CharSourceRange WithoutSuffRange;
681};
682
683}
684
685static bool getLiteralInfo(SourceRange literalRange,
686 bool isFloat, bool isIntZero,
687 ASTContext &Ctx, LiteralInfo &Info) {
688 if (literalRange.getBegin().isMacroID() ||
689 literalRange.getEnd().isMacroID())
690 return false;
691 StringRef text = Lexer::getSourceText(
692 CharSourceRange::getTokenRange(literalRange),
David Blaikiebbafb8a2012-03-11 07:00:24 +0000693 Ctx.getSourceManager(), Ctx.getLangOpts());
Ted Kremenekf7639e12012-03-06 20:06:33 +0000694 if (text.empty())
695 return false;
696
697 llvm::Optional<bool> UpperU, UpperL;
698 bool UpperF = false;
699
700 struct Suff {
701 static bool has(StringRef suff, StringRef &text) {
702 if (text.endswith(suff)) {
703 text = text.substr(0, text.size()-suff.size());
704 return true;
705 }
706 return false;
707 }
708 };
709
710 while (1) {
711 if (Suff::has("u", text)) {
712 UpperU = false;
713 } else if (Suff::has("U", text)) {
714 UpperU = true;
715 } else if (Suff::has("ll", text)) {
716 UpperL = false;
717 } else if (Suff::has("LL", text)) {
718 UpperL = true;
719 } else if (Suff::has("l", text)) {
720 UpperL = false;
721 } else if (Suff::has("L", text)) {
722 UpperL = true;
723 } else if (isFloat && Suff::has("f", text)) {
724 UpperF = false;
725 } else if (isFloat && Suff::has("F", text)) {
726 UpperF = true;
727 } else
728 break;
729 }
730
731 if (!UpperU.hasValue() && !UpperL.hasValue())
732 UpperU = UpperL = true;
733 else if (UpperU.hasValue() && !UpperL.hasValue())
734 UpperL = UpperU;
735 else if (UpperL.hasValue() && !UpperU.hasValue())
736 UpperU = UpperL;
737
738 Info.U = *UpperU ? "U" : "u";
739 Info.L = *UpperL ? "L" : "l";
740 Info.LL = *UpperL ? "LL" : "ll";
741 Info.F = UpperF ? "F" : "f";
742
743 Info.Hex = Info.Octal = false;
744 if (text.startswith("0x"))
745 Info.Hex = true;
746 else if (!isFloat && !isIntZero && text.startswith("0"))
747 Info.Octal = true;
748
749 SourceLocation B = literalRange.getBegin();
750 Info.WithoutSuffRange =
751 CharSourceRange::getCharRange(B, B.getLocWithOffset(text.size()));
752 return true;
753}
754
755static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
756 const NSAPI &NS, Commit &commit) {
757 if (Msg->getNumArgs() != 1)
758 return false;
759
760 const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts();
761 if (const CharacterLiteral *CharE = dyn_cast<CharacterLiteral>(Arg))
762 return rewriteToCharLiteral(Msg, CharE, NS, commit);
763 if (const ObjCBoolLiteralExpr *BE = dyn_cast<ObjCBoolLiteralExpr>(Arg))
764 return rewriteToBoolLiteral(Msg, BE, NS, commit);
765 if (const CXXBoolLiteralExpr *BE = dyn_cast<CXXBoolLiteralExpr>(Arg))
766 return rewriteToBoolLiteral(Msg, BE, NS, commit);
767
768 const Expr *literalE = Arg;
769 if (const UnaryOperator *UOE = dyn_cast<UnaryOperator>(literalE)) {
770 if (UOE->getOpcode() == UO_Plus || UOE->getOpcode() == UO_Minus)
771 literalE = UOE->getSubExpr();
772 }
773
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000774 // Only integer and floating literals, otherwise try to rewrite to boxed
775 // expression.
Ted Kremenekf7639e12012-03-06 20:06:33 +0000776 if (!isa<IntegerLiteral>(literalE) && !isa<FloatingLiteral>(literalE))
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000777 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000778
779 ASTContext &Ctx = NS.getASTContext();
780 Selector Sel = Msg->getSelector();
781 llvm::Optional<NSAPI::NSNumberLiteralMethodKind>
782 MKOpt = NS.getNSNumberLiteralMethodKind(Sel);
783 if (!MKOpt)
784 return false;
785 NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;
786
Benjamin Kramerece209a2012-03-13 17:05:43 +0000787 bool CallIsUnsigned = false, CallIsLong = false, CallIsLongLong = false;
Ted Kremenekf7639e12012-03-06 20:06:33 +0000788 bool CallIsFloating = false, CallIsDouble = false;
789
790 switch (MK) {
791 // We cannot have these calls with int/float literals.
792 case NSAPI::NSNumberWithChar:
793 case NSAPI::NSNumberWithUnsignedChar:
794 case NSAPI::NSNumberWithShort:
795 case NSAPI::NSNumberWithUnsignedShort:
796 case NSAPI::NSNumberWithBool:
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000797 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000798
799 case NSAPI::NSNumberWithUnsignedInt:
800 case NSAPI::NSNumberWithUnsignedInteger:
801 CallIsUnsigned = true;
802 case NSAPI::NSNumberWithInt:
803 case NSAPI::NSNumberWithInteger:
Ted Kremenekf7639e12012-03-06 20:06:33 +0000804 break;
805
806 case NSAPI::NSNumberWithUnsignedLong:
807 CallIsUnsigned = true;
808 case NSAPI::NSNumberWithLong:
Benjamin Kramerece209a2012-03-13 17:05:43 +0000809 CallIsLong = true;
Ted Kremenekf7639e12012-03-06 20:06:33 +0000810 break;
811
812 case NSAPI::NSNumberWithUnsignedLongLong:
813 CallIsUnsigned = true;
814 case NSAPI::NSNumberWithLongLong:
Benjamin Kramerece209a2012-03-13 17:05:43 +0000815 CallIsLongLong = true;
Ted Kremenekf7639e12012-03-06 20:06:33 +0000816 break;
817
818 case NSAPI::NSNumberWithDouble:
819 CallIsDouble = true;
820 case NSAPI::NSNumberWithFloat:
821 CallIsFloating = true;
822 break;
823 }
824
825 SourceRange ArgRange = Arg->getSourceRange();
826 QualType ArgTy = Arg->getType();
827 QualType CallTy = Msg->getArg(0)->getType();
828
829 // Check for the easy case, the literal maps directly to the call.
830 if (Ctx.hasSameType(ArgTy, CallTy)) {
831 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
832 commit.insert(ArgRange.getBegin(), "@");
833 return true;
834 }
835
836 // We will need to modify the literal suffix to get the same type as the call.
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000837 // Try with boxed expression if it came from a macro.
Ted Kremenekf7639e12012-03-06 20:06:33 +0000838 if (ArgRange.getBegin().isMacroID())
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000839 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000840
841 bool LitIsFloat = ArgTy->isFloatingType();
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000842 // For a float passed to integer call, don't try rewriting to objc literal.
843 // It is difficult and a very uncommon case anyway.
844 // But try with boxed expression.
Ted Kremenekf7639e12012-03-06 20:06:33 +0000845 if (LitIsFloat && !CallIsFloating)
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000846 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000847
848 // Try to modify the literal make it the same type as the method call.
849 // -Modify the suffix, and/or
850 // -Change integer to float
851
852 LiteralInfo LitInfo;
853 bool isIntZero = false;
854 if (const IntegerLiteral *IntE = dyn_cast<IntegerLiteral>(literalE))
855 isIntZero = !IntE->getValue().getBoolValue();
856 if (!getLiteralInfo(ArgRange, LitIsFloat, isIntZero, Ctx, LitInfo))
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000857 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000858
859 // Not easy to do int -> float with hex/octal and uncommon anyway.
860 if (!LitIsFloat && CallIsFloating && (LitInfo.Hex || LitInfo.Octal))
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000861 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000862
863 SourceLocation LitB = LitInfo.WithoutSuffRange.getBegin();
864 SourceLocation LitE = LitInfo.WithoutSuffRange.getEnd();
865
866 commit.replaceWithInner(CharSourceRange::getTokenRange(Msg->getSourceRange()),
867 LitInfo.WithoutSuffRange);
868 commit.insert(LitB, "@");
869
870 if (!LitIsFloat && CallIsFloating)
871 commit.insert(LitE, ".0");
872
873 if (CallIsFloating) {
874 if (!CallIsDouble)
875 commit.insert(LitE, LitInfo.F);
876 } else {
877 if (CallIsUnsigned)
878 commit.insert(LitE, LitInfo.U);
879
880 if (CallIsLong)
881 commit.insert(LitE, LitInfo.L);
882 else if (CallIsLongLong)
883 commit.insert(LitE, LitInfo.LL);
884 }
885 return true;
886}
Argyrios Kyrtzidisc1dfed62012-05-14 22:01:53 +0000887
Argyrios Kyrtzidis0bbe94f2012-05-14 23:33:49 +0000888// FIXME: Make determination of operator precedence more general and
889// make it broadly available.
890static bool subscriptOperatorNeedsParens(const Expr *FullExpr) {
891 const Expr* Expr = FullExpr->IgnoreImpCasts();
892 if (isa<ArraySubscriptExpr>(Expr) ||
893 isa<CallExpr>(Expr) ||
894 isa<DeclRefExpr>(Expr) ||
895 isa<CXXNamedCastExpr>(Expr) ||
896 isa<CXXConstructExpr>(Expr) ||
897 isa<CXXThisExpr>(Expr) ||
898 isa<CXXTypeidExpr>(Expr) ||
899 isa<CXXUnresolvedConstructExpr>(Expr) ||
900 isa<ObjCMessageExpr>(Expr) ||
901 isa<ObjCPropertyRefExpr>(Expr) ||
902 isa<ObjCProtocolExpr>(Expr) ||
903 isa<MemberExpr>(Expr) ||
Argyrios Kyrtzidis94442982012-05-22 00:47:53 +0000904 isa<ObjCIvarRefExpr>(Expr) ||
Argyrios Kyrtzidis0bbe94f2012-05-14 23:33:49 +0000905 isa<ParenExpr>(FullExpr) ||
906 isa<ParenListExpr>(Expr) ||
907 isa<SizeOfPackExpr>(Expr))
908 return false;
909
910 return true;
911}
Argyrios Kyrtzidisc1dfed62012-05-14 22:01:53 +0000912static bool castOperatorNeedsParens(const Expr *FullExpr) {
913 const Expr* Expr = FullExpr->IgnoreImpCasts();
914 if (isa<ArraySubscriptExpr>(Expr) ||
915 isa<CallExpr>(Expr) ||
916 isa<DeclRefExpr>(Expr) ||
917 isa<CastExpr>(Expr) ||
918 isa<CXXNewExpr>(Expr) ||
919 isa<CXXConstructExpr>(Expr) ||
920 isa<CXXDeleteExpr>(Expr) ||
921 isa<CXXNoexceptExpr>(Expr) ||
922 isa<CXXPseudoDestructorExpr>(Expr) ||
923 isa<CXXScalarValueInitExpr>(Expr) ||
924 isa<CXXThisExpr>(Expr) ||
925 isa<CXXTypeidExpr>(Expr) ||
926 isa<CXXUnresolvedConstructExpr>(Expr) ||
927 isa<ObjCMessageExpr>(Expr) ||
928 isa<ObjCPropertyRefExpr>(Expr) ||
929 isa<ObjCProtocolExpr>(Expr) ||
930 isa<MemberExpr>(Expr) ||
Argyrios Kyrtzidis94442982012-05-22 00:47:53 +0000931 isa<ObjCIvarRefExpr>(Expr) ||
Argyrios Kyrtzidisc1dfed62012-05-14 22:01:53 +0000932 isa<ParenExpr>(FullExpr) ||
933 isa<ParenListExpr>(Expr) ||
934 isa<SizeOfPackExpr>(Expr) ||
935 isa<UnaryOperator>(Expr))
936 return false;
937
938 return true;
939}
940
941static void objectifyExpr(const Expr *E, Commit &commit) {
942 if (!E) return;
943
944 QualType T = E->getType();
945 if (T->isObjCObjectPointerType()) {
946 if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) {
947 if (ICE->getCastKind() != CK_CPointerToObjCPointerCast)
948 return;
949 } else {
950 return;
951 }
952 } else if (!T->isPointerType()) {
953 return;
954 }
955
956 SourceRange Range = E->getSourceRange();
957 if (castOperatorNeedsParens(E))
958 commit.insertWrap("(", Range, ")");
959 commit.insertBefore(Range.getBegin(), "(id)");
960}
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000961
962//===----------------------------------------------------------------------===//
963// rewriteToNumericBoxedExpression.
964//===----------------------------------------------------------------------===//
965
Argyrios Kyrtzidiscebe7222012-05-15 22:59:54 +0000966static bool isEnumConstant(const Expr *E) {
967 if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts()))
968 if (const ValueDecl *VD = DRE->getDecl())
969 return isa<EnumConstantDecl>(VD);
970
971 return false;
972}
973
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000974static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
975 const NSAPI &NS, Commit &commit) {
976 if (Msg->getNumArgs() != 1)
977 return false;
978
979 const Expr *Arg = Msg->getArg(0);
980 if (Arg->isTypeDependent())
981 return false;
982
983 ASTContext &Ctx = NS.getASTContext();
984 Selector Sel = Msg->getSelector();
985 llvm::Optional<NSAPI::NSNumberLiteralMethodKind>
986 MKOpt = NS.getNSNumberLiteralMethodKind(Sel);
987 if (!MKOpt)
988 return false;
989 NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;
990
991 const Expr *OrigArg = Arg->IgnoreImpCasts();
992 QualType FinalTy = Arg->getType();
993 QualType OrigTy = OrigArg->getType();
994 uint64_t FinalTySize = Ctx.getTypeSize(FinalTy);
995 uint64_t OrigTySize = Ctx.getTypeSize(OrigTy);
996
997 bool isTruncated = FinalTySize < OrigTySize;
998 bool needsCast = false;
999
1000 if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) {
1001 switch (ICE->getCastKind()) {
1002 case CK_LValueToRValue:
1003 case CK_NoOp:
1004 case CK_UserDefinedConversion:
1005 break;
1006
1007 case CK_IntegralCast: {
1008 if (MK == NSAPI::NSNumberWithBool && OrigTy->isBooleanType())
1009 break;
1010 // Be more liberal with Integer/UnsignedInteger which are very commonly
1011 // used.
1012 if ((MK == NSAPI::NSNumberWithInteger ||
1013 MK == NSAPI::NSNumberWithUnsignedInteger) &&
1014 !isTruncated) {
Argyrios Kyrtzidiscebe7222012-05-15 22:59:54 +00001015 if (OrigTy->getAs<EnumType>() || isEnumConstant(OrigArg))
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +00001016 break;
1017 if ((MK==NSAPI::NSNumberWithInteger) == OrigTy->isSignedIntegerType() &&
1018 OrigTySize >= Ctx.getTypeSize(Ctx.IntTy))
1019 break;
1020 }
1021
1022 needsCast = true;
1023 break;
1024 }
1025
1026 case CK_PointerToBoolean:
1027 case CK_IntegralToBoolean:
1028 case CK_IntegralToFloating:
1029 case CK_FloatingToIntegral:
1030 case CK_FloatingToBoolean:
1031 case CK_FloatingCast:
1032 case CK_FloatingComplexToReal:
1033 case CK_FloatingComplexToBoolean:
1034 case CK_IntegralComplexToReal:
1035 case CK_IntegralComplexToBoolean:
1036 case CK_AtomicToNonAtomic:
1037 needsCast = true;
1038 break;
1039
1040 case CK_Dependent:
1041 case CK_BitCast:
1042 case CK_LValueBitCast:
1043 case CK_BaseToDerived:
1044 case CK_DerivedToBase:
1045 case CK_UncheckedDerivedToBase:
1046 case CK_Dynamic:
1047 case CK_ToUnion:
1048 case CK_ArrayToPointerDecay:
1049 case CK_FunctionToPointerDecay:
1050 case CK_NullToPointer:
1051 case CK_NullToMemberPointer:
1052 case CK_BaseToDerivedMemberPointer:
1053 case CK_DerivedToBaseMemberPointer:
1054 case CK_MemberPointerToBoolean:
1055 case CK_ReinterpretMemberPointer:
1056 case CK_ConstructorConversion:
1057 case CK_IntegralToPointer:
1058 case CK_PointerToIntegral:
1059 case CK_ToVoid:
1060 case CK_VectorSplat:
1061 case CK_CPointerToObjCPointerCast:
1062 case CK_BlockPointerToObjCPointerCast:
1063 case CK_AnyPointerToBlockPointerCast:
1064 case CK_ObjCObjectLValueCast:
1065 case CK_FloatingRealToComplex:
1066 case CK_FloatingComplexCast:
1067 case CK_FloatingComplexToIntegralComplex:
1068 case CK_IntegralRealToComplex:
1069 case CK_IntegralComplexCast:
1070 case CK_IntegralComplexToFloatingComplex:
1071 case CK_ARCProduceObject:
1072 case CK_ARCConsumeObject:
1073 case CK_ARCReclaimReturnedObject:
1074 case CK_ARCExtendBlockObject:
1075 case CK_NonAtomicToAtomic:
1076 case CK_CopyAndAutoreleaseBlockObject:
Eli Friedman34866c72012-08-31 00:14:07 +00001077 case CK_BuiltinFnToFnPtr:
Guy Benyei1b4fb3e2013-01-20 12:31:11 +00001078 case CK_ZeroToOCLEvent:
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +00001079 return false;
1080 }
1081 }
1082
Argyrios Kyrtzidisb4822602012-05-24 16:48:23 +00001083 if (needsCast) {
1084 DiagnosticsEngine &Diags = Ctx.getDiagnostics();
1085 // FIXME: Use a custom category name to distinguish migration diagnostics.
1086 unsigned diagID = Diags.getCustomDiagID(DiagnosticsEngine::Warning,
Argyrios Kyrtzidis927a4372012-06-20 01:28:32 +00001087 "converting to boxing syntax requires casting %0 to %1");
1088 Diags.Report(Msg->getExprLoc(), diagID) << OrigTy << FinalTy
1089 << Msg->getSourceRange();
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +00001090 return false;
Argyrios Kyrtzidisb4822602012-05-24 16:48:23 +00001091 }
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +00001092
1093 SourceRange ArgRange = OrigArg->getSourceRange();
Argyrios Kyrtzidis7bd957c2012-05-15 22:22:10 +00001094 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +00001095
1096 if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
1097 commit.insertBefore(ArgRange.getBegin(), "@");
1098 else
1099 commit.insertWrap("@(", ArgRange, ")");
1100
1101 return true;
1102}
Argyrios Kyrtzidis7bd957c2012-05-15 22:22:10 +00001103
1104//===----------------------------------------------------------------------===//
1105// rewriteToStringBoxedExpression.
1106//===----------------------------------------------------------------------===//
1107
1108static bool doRewriteToUTF8StringBoxedExpressionHelper(
1109 const ObjCMessageExpr *Msg,
1110 const NSAPI &NS, Commit &commit) {
1111 const Expr *Arg = Msg->getArg(0);
1112 if (Arg->isTypeDependent())
1113 return false;
1114
Argyrios Kyrtzidis81541472012-05-16 00:21:21 +00001115 ASTContext &Ctx = NS.getASTContext();
1116
Argyrios Kyrtzidis7bd957c2012-05-15 22:22:10 +00001117 const Expr *OrigArg = Arg->IgnoreImpCasts();
1118 QualType OrigTy = OrigArg->getType();
Argyrios Kyrtzidis81541472012-05-16 00:21:21 +00001119 if (OrigTy->isArrayType())
1120 OrigTy = Ctx.getArrayDecayedType(OrigTy);
Argyrios Kyrtzidis7bd957c2012-05-15 22:22:10 +00001121
1122 if (const StringLiteral *
1123 StrE = dyn_cast<StringLiteral>(OrigArg->IgnoreParens())) {
1124 commit.replaceWithInner(Msg->getSourceRange(), StrE->getSourceRange());
1125 commit.insert(StrE->getLocStart(), "@");
1126 return true;
1127 }
1128
Argyrios Kyrtzidis7bd957c2012-05-15 22:22:10 +00001129 if (const PointerType *PT = OrigTy->getAs<PointerType>()) {
1130 QualType PointeeType = PT->getPointeeType();
1131 if (Ctx.hasSameUnqualifiedType(PointeeType, Ctx.CharTy)) {
1132 SourceRange ArgRange = OrigArg->getSourceRange();
1133 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
1134
1135 if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
1136 commit.insertBefore(ArgRange.getBegin(), "@");
1137 else
1138 commit.insertWrap("@(", ArgRange, ")");
1139
1140 return true;
1141 }
1142 }
1143
1144 return false;
1145}
1146
1147static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
1148 const NSAPI &NS, Commit &commit) {
1149 Selector Sel = Msg->getSelector();
1150
1151 if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithUTF8String) ||
1152 Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCString)) {
1153 if (Msg->getNumArgs() != 1)
1154 return false;
1155 return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);
1156 }
1157
1158 if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCStringEncoding)) {
1159 if (Msg->getNumArgs() != 2)
1160 return false;
1161
1162 const Expr *encodingArg = Msg->getArg(1);
1163 if (NS.isNSUTF8StringEncodingConstant(encodingArg) ||
1164 NS.isNSASCIIStringEncodingConstant(encodingArg))
1165 return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);
1166 }
1167
1168 return false;
1169}