blob: e257a0b6f1c0d42b7431e31f842fd68a0c8296ad [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"
Argyrios Kyrtzidisa44b9702013-01-16 23:54:48 +000019#include "clang/AST/ParentMap.h"
Ted Kremenek30660a82012-03-06 20:06:33 +000020#include "clang/Edit/Commit.h"
21#include "clang/Lex/Lexer.h"
Ted Kremenek30660a82012-03-06 20:06:33 +000022
23using namespace clang;
24using namespace edit;
25
26static bool checkForLiteralCreation(const ObjCMessageExpr *Msg,
Argyrios Kyrtzidis6b4db9b2012-06-06 22:23:12 +000027 IdentifierInfo *&ClassId,
28 const LangOptions &LangOpts) {
Ted Kremenek30660a82012-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 Kyrtzidis6b4db9b2012-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 Kremenek30660a82012-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 Kyrtzidis6b4db9b2012-06-06 22:23:12 +000062 if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
Ted Kremenek30660a82012-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 Kyrtzidis6b4db9b2012-06-06 22:23:12 +000072 (NS.getNSStringSelector(NSAPI::NSStr_stringWithString) == Sel ||
73 NS.getNSStringSelector(NSAPI::NSStr_initWithString) == Sel)) ||
Ted Kremenek30660a82012-03-06 20:06:33 +000074
75 (isa<ObjCArrayLiteral>(Arg) &&
76 NS.getNSClassId(NSAPI::ClassId_NSArray) == II &&
Argyrios Kyrtzidis6b4db9b2012-06-06 22:23:12 +000077 (NS.getNSArraySelector(NSAPI::NSArr_arrayWithArray) == Sel ||
78 NS.getNSArraySelector(NSAPI::NSArr_initWithArray) == Sel)) ||
Ted Kremenek30660a82012-03-06 20:06:33 +000079
80 (isa<ObjCDictionaryLiteral>(Arg) &&
81 NS.getNSClassId(NSAPI::ClassId_NSDictionary) == II &&
Argyrios Kyrtzidis6b4db9b2012-06-06 22:23:12 +000082 (NS.getNSDictionarySelector(
83 NSAPI::NSDict_dictionaryWithDictionary) == Sel ||
84 NS.getNSDictionarySelector(NSAPI::NSDict_initWithDictionary) == Sel))) {
Ted Kremenek30660a82012-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 Kyrtzidis18387032012-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 Kyrtzidisc2abbe02012-07-05 21:49:51 +0000159 Selector subscriptSel) {
Argyrios Kyrtzidis18387032012-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 Kyrtzidisc2abbe02012-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 Kyrtzidis20119a82012-05-14 23:33:49 +0000172static bool subscriptOperatorNeedsParens(const Expr *FullExpr);
173
Ted Kremenek30660a82012-03-06 20:06:33 +0000174static void maybePutParensOnReceiver(const Expr *Receiver, Commit &commit) {
Argyrios Kyrtzidis20119a82012-05-14 23:33:49 +0000175 if (subscriptOperatorNeedsParens(Receiver)) {
Ted Kremenek30660a82012-03-06 20:06:33 +0000176 SourceRange RecRange = Receiver->getSourceRange();
177 commit.insertWrap("(", RecRange, ")");
178 }
179}
180
Argyrios Kyrtzidiscacf7182012-06-04 21:23:26 +0000181static bool rewriteToSubscriptGetCommon(const ObjCMessageExpr *Msg,
182 Commit &commit) {
Ted Kremenek30660a82012-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 Kyrtzidiscacf7182012-06-04 21:23:26 +0000203static bool rewriteToArraySubscriptGet(const ObjCInterfaceDecl *IFace,
204 const ObjCMessageExpr *Msg,
205 const NSAPI &NS,
206 Commit &commit) {
Argyrios Kyrtzidis18387032012-07-06 00:07:09 +0000207 if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
Argyrios Kyrtzidisc2abbe02012-07-05 21:49:51 +0000208 NS.getObjectAtIndexedSubscriptSelector()))
Argyrios Kyrtzidiscacf7182012-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 Kyrtzidis18387032012-07-06 00:07:09 +0000217 if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
Argyrios Kyrtzidisc2abbe02012-07-05 21:49:51 +0000218 NS.getObjectForKeyedSubscriptSelector()))
Argyrios Kyrtzidiscacf7182012-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 Kremenek30660a82012-03-06 20:06:33 +0000226 Commit &commit) {
Argyrios Kyrtzidis18387032012-07-06 00:07:09 +0000227 if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
Argyrios Kyrtzidisc2abbe02012-07-05 21:49:51 +0000228 NS.getSetObjectAtIndexedSubscriptSelector()))
229 return false;
230
Ted Kremenek30660a82012-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 Kyrtzidiscacf7182012-06-04 21:23:26 +0000257static bool rewriteToDictionarySubscriptSet(const ObjCInterfaceDecl *IFace,
258 const ObjCMessageExpr *Msg,
259 const NSAPI &NS,
Ted Kremenek30660a82012-03-06 20:06:33 +0000260 Commit &commit) {
Argyrios Kyrtzidis18387032012-07-06 00:07:09 +0000261 if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
Argyrios Kyrtzidisc2abbe02012-07-05 21:49:51 +0000262 NS.getSetObjectForKeyedSubscriptSelector()))
263 return false;
264
Ted Kremenek30660a82012-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 Kyrtzidiscacf7182012-06-04 21:23:26 +0000291 const NSAPI &NS, Commit &commit) {
Ted Kremenek30660a82012-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
Dmitri Gribenko65303902013-02-14 13:49:48 +0000299 const ObjCInterfaceDecl *IFace =
300 NS.getASTContext().getObjContainingInterface(Method);
Ted Kremenek30660a82012-03-06 20:06:33 +0000301 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,
Argyrios Kyrtzidisa44b9702013-01-16 23:54:48 +0000328 const NSAPI &NS, Commit &commit,
329 const ParentMap *PMap);
Ted Kremenek30660a82012-03-06 20:06:33 +0000330static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
331 const NSAPI &NS, Commit &commit);
332static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
333 const NSAPI &NS, Commit &commit);
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000334static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
335 const NSAPI &NS, Commit &commit);
Argyrios Kyrtzidis7fe103c2012-05-15 22:22:10 +0000336static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
337 const NSAPI &NS, Commit &commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000338
339bool edit::rewriteToObjCLiteralSyntax(const ObjCMessageExpr *Msg,
Argyrios Kyrtzidisa44b9702013-01-16 23:54:48 +0000340 const NSAPI &NS, Commit &commit,
341 const ParentMap *PMap) {
Ted Kremenek30660a82012-03-06 20:06:33 +0000342 IdentifierInfo *II = 0;
Argyrios Kyrtzidis6b4db9b2012-06-06 22:23:12 +0000343 if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
Ted Kremenek30660a82012-03-06 20:06:33 +0000344 return false;
345
346 if (II == NS.getNSClassId(NSAPI::ClassId_NSArray))
Argyrios Kyrtzidisa44b9702013-01-16 23:54:48 +0000347 return rewriteToArrayLiteral(Msg, NS, commit, PMap);
Ted Kremenek30660a82012-03-06 20:06:33 +0000348 if (II == NS.getNSClassId(NSAPI::ClassId_NSDictionary))
349 return rewriteToDictionaryLiteral(Msg, NS, commit);
350 if (II == NS.getNSClassId(NSAPI::ClassId_NSNumber))
351 return rewriteToNumberLiteral(Msg, NS, commit);
Argyrios Kyrtzidis7fe103c2012-05-15 22:22:10 +0000352 if (II == NS.getNSClassId(NSAPI::ClassId_NSString))
353 return rewriteToStringBoxedExpression(Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000354
355 return false;
356}
357
Fariborz Jahanianafcb16f2013-07-05 20:46:03 +0000358bool edit::rewriteToObjCProperty(const ObjCMethodDecl *Getter,
359 const ObjCMethodDecl *Setter,
360 const NSAPI &NS, Commit &commit) {
Fariborz Jahanian1921b582013-07-08 21:42:08 +0000361 std::string PropertyString = "@property";
362 const ParmVarDecl *argDecl = *Setter->param_begin();
363 QualType ArgType = argDecl->getType();
364 Qualifiers::ObjCLifetime propertyLifetime = ArgType.getObjCLifetime();
Fariborz Jahaniane16a4752013-07-10 00:20:11 +0000365
366 if (ArgType->isObjCRetainableType() &&
367 propertyLifetime == Qualifiers::OCL_Strong) {
368 PropertyString += "(copy)";
Fariborz Jahanian1921b582013-07-08 21:42:08 +0000369 }
Fariborz Jahaniane16a4752013-07-10 00:20:11 +0000370 else if (propertyLifetime == Qualifiers::OCL_Weak)
371 PropertyString += "(weak)";
372 else
373 PropertyString += "(unsafe_unretained)";
374
Fariborz Jahanian1921b582013-07-08 21:42:08 +0000375 QualType PropQT = Getter->getResultType();
376 PropertyString += " ";
377 PropertyString += PropQT.getAsString(NS.getASTContext().getPrintingPolicy());
378 PropertyString += " ";
379 PropertyString += Getter->getNameAsString();
380 commit.replace(CharSourceRange::getCharRange(Getter->getLocStart(),
381 Getter->getDeclaratorEndLoc()),
382 PropertyString);
383 SourceLocation EndLoc = Setter->getDeclaratorEndLoc();
384 // Get location past ';'
385 EndLoc = EndLoc.getLocWithOffset(1);
386 commit.remove(CharSourceRange::getCharRange(Setter->getLocStart(), EndLoc));
387 return true;
Fariborz Jahanianafcb16f2013-07-05 20:46:03 +0000388}
389
Argyrios Kyrtzidisa44b9702013-01-16 23:54:48 +0000390/// \brief Returns true if the immediate message arguments of \c Msg should not
391/// be rewritten because it will interfere with the rewrite of the parent
392/// message expression. e.g.
393/// \code
394/// [NSDictionary dictionaryWithObjects:
395/// [NSArray arrayWithObjects:@"1", @"2", nil]
396/// forKeys:[NSArray arrayWithObjects:@"A", @"B", nil]];
397/// \endcode
398/// It will return true for this because we are going to rewrite this directly
399/// to a dictionary literal without any array literals.
400static bool shouldNotRewriteImmediateMessageArgs(const ObjCMessageExpr *Msg,
401 const NSAPI &NS);
402
Ted Kremenek30660a82012-03-06 20:06:33 +0000403//===----------------------------------------------------------------------===//
404// rewriteToArrayLiteral.
405//===----------------------------------------------------------------------===//
406
Argyrios Kyrtzidis055b3952012-05-14 22:01:53 +0000407/// \brief Adds an explicit cast to 'id' if the type is not objc object.
408static void objectifyExpr(const Expr *E, Commit &commit);
409
Ted Kremenek30660a82012-03-06 20:06:33 +0000410static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
Argyrios Kyrtzidisa44b9702013-01-16 23:54:48 +0000411 const NSAPI &NS, Commit &commit,
412 const ParentMap *PMap) {
413 if (PMap) {
414 const ObjCMessageExpr *ParentMsg =
415 dyn_cast_or_null<ObjCMessageExpr>(PMap->getParentIgnoreParenCasts(Msg));
416 if (shouldNotRewriteImmediateMessageArgs(ParentMsg, NS))
417 return false;
418 }
419
Ted Kremenek30660a82012-03-06 20:06:33 +0000420 Selector Sel = Msg->getSelector();
421 SourceRange MsgRange = Msg->getSourceRange();
422
423 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array)) {
424 if (Msg->getNumArgs() != 0)
425 return false;
426 commit.replace(MsgRange, "@[]");
427 return true;
428 }
429
430 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) {
431 if (Msg->getNumArgs() != 1)
432 return false;
Argyrios Kyrtzidis055b3952012-05-14 22:01:53 +0000433 objectifyExpr(Msg->getArg(0), commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000434 SourceRange ArgRange = Msg->getArg(0)->getSourceRange();
435 commit.replaceWithInner(MsgRange, ArgRange);
436 commit.insertWrap("@[", ArgRange, "]");
437 return true;
438 }
439
Argyrios Kyrtzidis6b4db9b2012-06-06 22:23:12 +0000440 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) ||
441 Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) {
Ted Kremenek30660a82012-03-06 20:06:33 +0000442 if (Msg->getNumArgs() == 0)
443 return false;
444 const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1);
445 if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
446 return false;
447
Argyrios Kyrtzidis055b3952012-05-14 22:01:53 +0000448 for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i)
449 objectifyExpr(Msg->getArg(i), commit);
450
Ted Kremenek30660a82012-03-06 20:06:33 +0000451 if (Msg->getNumArgs() == 1) {
452 commit.replace(MsgRange, "@[]");
453 return true;
454 }
455 SourceRange ArgRange(Msg->getArg(0)->getLocStart(),
456 Msg->getArg(Msg->getNumArgs()-2)->getLocEnd());
457 commit.replaceWithInner(MsgRange, ArgRange);
458 commit.insertWrap("@[", ArgRange, "]");
459 return true;
460 }
461
462 return false;
463}
464
465//===----------------------------------------------------------------------===//
466// rewriteToDictionaryLiteral.
467//===----------------------------------------------------------------------===//
468
Argyrios Kyrtzidisa44b9702013-01-16 23:54:48 +0000469/// \brief If \c Msg is an NSArray creation message or literal, this gets the
470/// objects that were used to create it.
471/// \returns true if it is an NSArray and we got objects, or false otherwise.
472static bool getNSArrayObjects(const Expr *E, const NSAPI &NS,
473 SmallVectorImpl<const Expr *> &Objs) {
474 if (!E)
475 return false;
476
477 E = E->IgnoreParenCasts();
478 if (!E)
479 return false;
480
481 if (const ObjCMessageExpr *Msg = dyn_cast<ObjCMessageExpr>(E)) {
482 IdentifierInfo *Cls = 0;
483 if (!checkForLiteralCreation(Msg, Cls, NS.getASTContext().getLangOpts()))
484 return false;
485
486 if (Cls != NS.getNSClassId(NSAPI::ClassId_NSArray))
487 return false;
488
489 Selector Sel = Msg->getSelector();
490 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array))
491 return true; // empty array.
492
493 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) {
494 if (Msg->getNumArgs() != 1)
495 return false;
496 Objs.push_back(Msg->getArg(0));
497 return true;
498 }
499
500 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) ||
501 Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) {
502 if (Msg->getNumArgs() == 0)
503 return false;
504 const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1);
505 if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
506 return false;
507
508 for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i)
509 Objs.push_back(Msg->getArg(i));
510 return true;
511 }
512
513 } else if (const ObjCArrayLiteral *ArrLit = dyn_cast<ObjCArrayLiteral>(E)) {
514 for (unsigned i = 0, e = ArrLit->getNumElements(); i != e; ++i)
515 Objs.push_back(ArrLit->getElement(i));
516 return true;
517 }
518
519 return false;
520}
521
Ted Kremenek30660a82012-03-06 20:06:33 +0000522static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
523 const NSAPI &NS, Commit &commit) {
524 Selector Sel = Msg->getSelector();
525 SourceRange MsgRange = Msg->getSourceRange();
526
527 if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_dictionary)) {
528 if (Msg->getNumArgs() != 0)
529 return false;
530 commit.replace(MsgRange, "@{}");
531 return true;
532 }
533
534 if (Sel == NS.getNSDictionarySelector(
535 NSAPI::NSDict_dictionaryWithObjectForKey)) {
536 if (Msg->getNumArgs() != 2)
537 return false;
Argyrios Kyrtzidis055b3952012-05-14 22:01:53 +0000538
539 objectifyExpr(Msg->getArg(0), commit);
540 objectifyExpr(Msg->getArg(1), commit);
541
Ted Kremenek30660a82012-03-06 20:06:33 +0000542 SourceRange ValRange = Msg->getArg(0)->getSourceRange();
543 SourceRange KeyRange = Msg->getArg(1)->getSourceRange();
544 // Insert key before the value.
545 commit.insertBefore(ValRange.getBegin(), ": ");
546 commit.insertFromRange(ValRange.getBegin(),
547 CharSourceRange::getTokenRange(KeyRange),
548 /*afterToken=*/false, /*beforePreviousInsertions=*/true);
549 commit.insertBefore(ValRange.getBegin(), "@{");
550 commit.insertAfterToken(ValRange.getEnd(), "}");
551 commit.replaceWithInner(MsgRange, ValRange);
552 return true;
553 }
554
555 if (Sel == NS.getNSDictionarySelector(
Argyrios Kyrtzidis6b4db9b2012-06-06 22:23:12 +0000556 NSAPI::NSDict_dictionaryWithObjectsAndKeys) ||
557 Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsAndKeys)) {
Ted Kremenek30660a82012-03-06 20:06:33 +0000558 if (Msg->getNumArgs() % 2 != 1)
559 return false;
560 unsigned SentinelIdx = Msg->getNumArgs() - 1;
561 const Expr *SentinelExpr = Msg->getArg(SentinelIdx);
562 if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
563 return false;
564
565 if (Msg->getNumArgs() == 1) {
566 commit.replace(MsgRange, "@{}");
567 return true;
568 }
569
570 for (unsigned i = 0; i < SentinelIdx; i += 2) {
Argyrios Kyrtzidis055b3952012-05-14 22:01:53 +0000571 objectifyExpr(Msg->getArg(i), commit);
572 objectifyExpr(Msg->getArg(i+1), commit);
573
Ted Kremenek30660a82012-03-06 20:06:33 +0000574 SourceRange ValRange = Msg->getArg(i)->getSourceRange();
575 SourceRange KeyRange = Msg->getArg(i+1)->getSourceRange();
576 // Insert value after key.
577 commit.insertAfterToken(KeyRange.getEnd(), ": ");
578 commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true);
579 commit.remove(CharSourceRange::getCharRange(ValRange.getBegin(),
580 KeyRange.getBegin()));
581 }
582 // Range of arguments up until and including the last key.
583 // The sentinel and first value are cut off, the value will move after the
584 // key.
585 SourceRange ArgRange(Msg->getArg(1)->getLocStart(),
586 Msg->getArg(SentinelIdx-1)->getLocEnd());
587 commit.insertWrap("@{", ArgRange, "}");
588 commit.replaceWithInner(MsgRange, ArgRange);
589 return true;
590 }
591
Argyrios Kyrtzidisa44b9702013-01-16 23:54:48 +0000592 if (Sel == NS.getNSDictionarySelector(
593 NSAPI::NSDict_dictionaryWithObjectsForKeys) ||
594 Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsForKeys)) {
595 if (Msg->getNumArgs() != 2)
596 return false;
597
598 SmallVector<const Expr *, 8> Vals;
599 if (!getNSArrayObjects(Msg->getArg(0), NS, Vals))
600 return false;
601
602 SmallVector<const Expr *, 8> Keys;
603 if (!getNSArrayObjects(Msg->getArg(1), NS, Keys))
604 return false;
605
606 if (Vals.size() != Keys.size())
607 return false;
608
609 if (Vals.empty()) {
610 commit.replace(MsgRange, "@{}");
611 return true;
612 }
613
614 for (unsigned i = 0, n = Vals.size(); i < n; ++i) {
615 objectifyExpr(Vals[i], commit);
616 objectifyExpr(Keys[i], commit);
617
618 SourceRange ValRange = Vals[i]->getSourceRange();
619 SourceRange KeyRange = Keys[i]->getSourceRange();
620 // Insert value after key.
621 commit.insertAfterToken(KeyRange.getEnd(), ": ");
622 commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true);
623 }
624 // Range of arguments up until and including the last key.
625 // The first value is cut off, the value will move after the key.
626 SourceRange ArgRange(Keys.front()->getLocStart(),
627 Keys.back()->getLocEnd());
628 commit.insertWrap("@{", ArgRange, "}");
629 commit.replaceWithInner(MsgRange, ArgRange);
630 return true;
631 }
632
633 return false;
634}
635
636static bool shouldNotRewriteImmediateMessageArgs(const ObjCMessageExpr *Msg,
637 const NSAPI &NS) {
638 if (!Msg)
639 return false;
640
641 IdentifierInfo *II = 0;
642 if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
643 return false;
644
645 if (II != NS.getNSClassId(NSAPI::ClassId_NSDictionary))
646 return false;
647
648 Selector Sel = Msg->getSelector();
649 if (Sel == NS.getNSDictionarySelector(
650 NSAPI::NSDict_dictionaryWithObjectsForKeys) ||
651 Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsForKeys)) {
652 if (Msg->getNumArgs() != 2)
653 return false;
654
655 SmallVector<const Expr *, 8> Vals;
656 if (!getNSArrayObjects(Msg->getArg(0), NS, Vals))
657 return false;
658
659 SmallVector<const Expr *, 8> Keys;
660 if (!getNSArrayObjects(Msg->getArg(1), NS, Keys))
661 return false;
662
663 if (Vals.size() != Keys.size())
664 return false;
665
666 return true;
667 }
668
Ted Kremenek30660a82012-03-06 20:06:33 +0000669 return false;
670}
671
672//===----------------------------------------------------------------------===//
673// rewriteToNumberLiteral.
674//===----------------------------------------------------------------------===//
675
676static bool rewriteToCharLiteral(const ObjCMessageExpr *Msg,
677 const CharacterLiteral *Arg,
678 const NSAPI &NS, Commit &commit) {
679 if (Arg->getKind() != CharacterLiteral::Ascii)
680 return false;
681 if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithChar,
682 Msg->getSelector())) {
683 SourceRange ArgRange = Arg->getSourceRange();
684 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
685 commit.insert(ArgRange.getBegin(), "@");
686 return true;
687 }
688
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000689 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000690}
691
692static bool rewriteToBoolLiteral(const ObjCMessageExpr *Msg,
693 const Expr *Arg,
694 const NSAPI &NS, Commit &commit) {
695 if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithBool,
696 Msg->getSelector())) {
697 SourceRange ArgRange = Arg->getSourceRange();
698 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
699 commit.insert(ArgRange.getBegin(), "@");
700 return true;
701 }
702
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000703 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000704}
705
706namespace {
707
708struct LiteralInfo {
709 bool Hex, Octal;
710 StringRef U, F, L, LL;
711 CharSourceRange WithoutSuffRange;
712};
713
714}
715
716static bool getLiteralInfo(SourceRange literalRange,
717 bool isFloat, bool isIntZero,
718 ASTContext &Ctx, LiteralInfo &Info) {
719 if (literalRange.getBegin().isMacroID() ||
720 literalRange.getEnd().isMacroID())
721 return false;
722 StringRef text = Lexer::getSourceText(
723 CharSourceRange::getTokenRange(literalRange),
David Blaikie4e4d0842012-03-11 07:00:24 +0000724 Ctx.getSourceManager(), Ctx.getLangOpts());
Ted Kremenek30660a82012-03-06 20:06:33 +0000725 if (text.empty())
726 return false;
727
David Blaikiedc84cd52013-02-20 22:23:23 +0000728 Optional<bool> UpperU, UpperL;
Ted Kremenek30660a82012-03-06 20:06:33 +0000729 bool UpperF = false;
730
731 struct Suff {
732 static bool has(StringRef suff, StringRef &text) {
733 if (text.endswith(suff)) {
734 text = text.substr(0, text.size()-suff.size());
735 return true;
736 }
737 return false;
738 }
739 };
740
741 while (1) {
742 if (Suff::has("u", text)) {
743 UpperU = false;
744 } else if (Suff::has("U", text)) {
745 UpperU = true;
746 } else if (Suff::has("ll", text)) {
747 UpperL = false;
748 } else if (Suff::has("LL", text)) {
749 UpperL = true;
750 } else if (Suff::has("l", text)) {
751 UpperL = false;
752 } else if (Suff::has("L", text)) {
753 UpperL = true;
754 } else if (isFloat && Suff::has("f", text)) {
755 UpperF = false;
756 } else if (isFloat && Suff::has("F", text)) {
757 UpperF = true;
758 } else
759 break;
760 }
761
762 if (!UpperU.hasValue() && !UpperL.hasValue())
763 UpperU = UpperL = true;
764 else if (UpperU.hasValue() && !UpperL.hasValue())
765 UpperL = UpperU;
766 else if (UpperL.hasValue() && !UpperU.hasValue())
767 UpperU = UpperL;
768
769 Info.U = *UpperU ? "U" : "u";
770 Info.L = *UpperL ? "L" : "l";
771 Info.LL = *UpperL ? "LL" : "ll";
772 Info.F = UpperF ? "F" : "f";
773
774 Info.Hex = Info.Octal = false;
775 if (text.startswith("0x"))
776 Info.Hex = true;
777 else if (!isFloat && !isIntZero && text.startswith("0"))
778 Info.Octal = true;
779
780 SourceLocation B = literalRange.getBegin();
781 Info.WithoutSuffRange =
782 CharSourceRange::getCharRange(B, B.getLocWithOffset(text.size()));
783 return true;
784}
785
786static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
787 const NSAPI &NS, Commit &commit) {
788 if (Msg->getNumArgs() != 1)
789 return false;
790
791 const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts();
792 if (const CharacterLiteral *CharE = dyn_cast<CharacterLiteral>(Arg))
793 return rewriteToCharLiteral(Msg, CharE, NS, commit);
794 if (const ObjCBoolLiteralExpr *BE = dyn_cast<ObjCBoolLiteralExpr>(Arg))
795 return rewriteToBoolLiteral(Msg, BE, NS, commit);
796 if (const CXXBoolLiteralExpr *BE = dyn_cast<CXXBoolLiteralExpr>(Arg))
797 return rewriteToBoolLiteral(Msg, BE, NS, commit);
798
799 const Expr *literalE = Arg;
800 if (const UnaryOperator *UOE = dyn_cast<UnaryOperator>(literalE)) {
801 if (UOE->getOpcode() == UO_Plus || UOE->getOpcode() == UO_Minus)
802 literalE = UOE->getSubExpr();
803 }
804
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000805 // Only integer and floating literals, otherwise try to rewrite to boxed
806 // expression.
Ted Kremenek30660a82012-03-06 20:06:33 +0000807 if (!isa<IntegerLiteral>(literalE) && !isa<FloatingLiteral>(literalE))
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000808 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000809
810 ASTContext &Ctx = NS.getASTContext();
811 Selector Sel = Msg->getSelector();
David Blaikiedc84cd52013-02-20 22:23:23 +0000812 Optional<NSAPI::NSNumberLiteralMethodKind>
Ted Kremenek30660a82012-03-06 20:06:33 +0000813 MKOpt = NS.getNSNumberLiteralMethodKind(Sel);
814 if (!MKOpt)
815 return false;
816 NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;
817
Benjamin Kramerbea6c0a2012-03-13 17:05:43 +0000818 bool CallIsUnsigned = false, CallIsLong = false, CallIsLongLong = false;
Ted Kremenek30660a82012-03-06 20:06:33 +0000819 bool CallIsFloating = false, CallIsDouble = false;
820
821 switch (MK) {
822 // We cannot have these calls with int/float literals.
823 case NSAPI::NSNumberWithChar:
824 case NSAPI::NSNumberWithUnsignedChar:
825 case NSAPI::NSNumberWithShort:
826 case NSAPI::NSNumberWithUnsignedShort:
827 case NSAPI::NSNumberWithBool:
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000828 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000829
830 case NSAPI::NSNumberWithUnsignedInt:
831 case NSAPI::NSNumberWithUnsignedInteger:
832 CallIsUnsigned = true;
833 case NSAPI::NSNumberWithInt:
834 case NSAPI::NSNumberWithInteger:
Ted Kremenek30660a82012-03-06 20:06:33 +0000835 break;
836
837 case NSAPI::NSNumberWithUnsignedLong:
838 CallIsUnsigned = true;
839 case NSAPI::NSNumberWithLong:
Benjamin Kramerbea6c0a2012-03-13 17:05:43 +0000840 CallIsLong = true;
Ted Kremenek30660a82012-03-06 20:06:33 +0000841 break;
842
843 case NSAPI::NSNumberWithUnsignedLongLong:
844 CallIsUnsigned = true;
845 case NSAPI::NSNumberWithLongLong:
Benjamin Kramerbea6c0a2012-03-13 17:05:43 +0000846 CallIsLongLong = true;
Ted Kremenek30660a82012-03-06 20:06:33 +0000847 break;
848
849 case NSAPI::NSNumberWithDouble:
850 CallIsDouble = true;
851 case NSAPI::NSNumberWithFloat:
852 CallIsFloating = true;
853 break;
854 }
855
856 SourceRange ArgRange = Arg->getSourceRange();
857 QualType ArgTy = Arg->getType();
858 QualType CallTy = Msg->getArg(0)->getType();
859
860 // Check for the easy case, the literal maps directly to the call.
861 if (Ctx.hasSameType(ArgTy, CallTy)) {
862 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
863 commit.insert(ArgRange.getBegin(), "@");
864 return true;
865 }
866
867 // We will need to modify the literal suffix to get the same type as the call.
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000868 // Try with boxed expression if it came from a macro.
Ted Kremenek30660a82012-03-06 20:06:33 +0000869 if (ArgRange.getBegin().isMacroID())
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000870 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000871
872 bool LitIsFloat = ArgTy->isFloatingType();
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000873 // For a float passed to integer call, don't try rewriting to objc literal.
874 // It is difficult and a very uncommon case anyway.
875 // But try with boxed expression.
Ted Kremenek30660a82012-03-06 20:06:33 +0000876 if (LitIsFloat && !CallIsFloating)
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000877 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000878
879 // Try to modify the literal make it the same type as the method call.
880 // -Modify the suffix, and/or
881 // -Change integer to float
882
883 LiteralInfo LitInfo;
884 bool isIntZero = false;
885 if (const IntegerLiteral *IntE = dyn_cast<IntegerLiteral>(literalE))
886 isIntZero = !IntE->getValue().getBoolValue();
887 if (!getLiteralInfo(ArgRange, LitIsFloat, isIntZero, Ctx, LitInfo))
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000888 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000889
890 // Not easy to do int -> float with hex/octal and uncommon anyway.
891 if (!LitIsFloat && CallIsFloating && (LitInfo.Hex || LitInfo.Octal))
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000892 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000893
894 SourceLocation LitB = LitInfo.WithoutSuffRange.getBegin();
895 SourceLocation LitE = LitInfo.WithoutSuffRange.getEnd();
896
897 commit.replaceWithInner(CharSourceRange::getTokenRange(Msg->getSourceRange()),
898 LitInfo.WithoutSuffRange);
899 commit.insert(LitB, "@");
900
901 if (!LitIsFloat && CallIsFloating)
902 commit.insert(LitE, ".0");
903
904 if (CallIsFloating) {
905 if (!CallIsDouble)
906 commit.insert(LitE, LitInfo.F);
907 } else {
908 if (CallIsUnsigned)
909 commit.insert(LitE, LitInfo.U);
910
911 if (CallIsLong)
912 commit.insert(LitE, LitInfo.L);
913 else if (CallIsLongLong)
914 commit.insert(LitE, LitInfo.LL);
915 }
916 return true;
917}
Argyrios Kyrtzidis055b3952012-05-14 22:01:53 +0000918
Argyrios Kyrtzidis20119a82012-05-14 23:33:49 +0000919// FIXME: Make determination of operator precedence more general and
920// make it broadly available.
921static bool subscriptOperatorNeedsParens(const Expr *FullExpr) {
922 const Expr* Expr = FullExpr->IgnoreImpCasts();
923 if (isa<ArraySubscriptExpr>(Expr) ||
924 isa<CallExpr>(Expr) ||
925 isa<DeclRefExpr>(Expr) ||
926 isa<CXXNamedCastExpr>(Expr) ||
927 isa<CXXConstructExpr>(Expr) ||
928 isa<CXXThisExpr>(Expr) ||
929 isa<CXXTypeidExpr>(Expr) ||
930 isa<CXXUnresolvedConstructExpr>(Expr) ||
931 isa<ObjCMessageExpr>(Expr) ||
932 isa<ObjCPropertyRefExpr>(Expr) ||
933 isa<ObjCProtocolExpr>(Expr) ||
934 isa<MemberExpr>(Expr) ||
Argyrios Kyrtzidis2bddd432012-05-22 00:47:53 +0000935 isa<ObjCIvarRefExpr>(Expr) ||
Argyrios Kyrtzidis20119a82012-05-14 23:33:49 +0000936 isa<ParenExpr>(FullExpr) ||
937 isa<ParenListExpr>(Expr) ||
938 isa<SizeOfPackExpr>(Expr))
939 return false;
940
941 return true;
942}
Argyrios Kyrtzidis055b3952012-05-14 22:01:53 +0000943static bool castOperatorNeedsParens(const Expr *FullExpr) {
944 const Expr* Expr = FullExpr->IgnoreImpCasts();
945 if (isa<ArraySubscriptExpr>(Expr) ||
946 isa<CallExpr>(Expr) ||
947 isa<DeclRefExpr>(Expr) ||
948 isa<CastExpr>(Expr) ||
949 isa<CXXNewExpr>(Expr) ||
950 isa<CXXConstructExpr>(Expr) ||
951 isa<CXXDeleteExpr>(Expr) ||
952 isa<CXXNoexceptExpr>(Expr) ||
953 isa<CXXPseudoDestructorExpr>(Expr) ||
954 isa<CXXScalarValueInitExpr>(Expr) ||
955 isa<CXXThisExpr>(Expr) ||
956 isa<CXXTypeidExpr>(Expr) ||
957 isa<CXXUnresolvedConstructExpr>(Expr) ||
958 isa<ObjCMessageExpr>(Expr) ||
959 isa<ObjCPropertyRefExpr>(Expr) ||
960 isa<ObjCProtocolExpr>(Expr) ||
961 isa<MemberExpr>(Expr) ||
Argyrios Kyrtzidis2bddd432012-05-22 00:47:53 +0000962 isa<ObjCIvarRefExpr>(Expr) ||
Argyrios Kyrtzidis055b3952012-05-14 22:01:53 +0000963 isa<ParenExpr>(FullExpr) ||
964 isa<ParenListExpr>(Expr) ||
965 isa<SizeOfPackExpr>(Expr) ||
966 isa<UnaryOperator>(Expr))
967 return false;
968
969 return true;
970}
971
972static void objectifyExpr(const Expr *E, Commit &commit) {
973 if (!E) return;
974
975 QualType T = E->getType();
976 if (T->isObjCObjectPointerType()) {
977 if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) {
978 if (ICE->getCastKind() != CK_CPointerToObjCPointerCast)
979 return;
980 } else {
981 return;
982 }
983 } else if (!T->isPointerType()) {
984 return;
985 }
986
987 SourceRange Range = E->getSourceRange();
988 if (castOperatorNeedsParens(E))
989 commit.insertWrap("(", Range, ")");
990 commit.insertBefore(Range.getBegin(), "(id)");
991}
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000992
993//===----------------------------------------------------------------------===//
994// rewriteToNumericBoxedExpression.
995//===----------------------------------------------------------------------===//
996
Argyrios Kyrtzidisc5d33e92012-05-15 22:59:54 +0000997static bool isEnumConstant(const Expr *E) {
998 if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts()))
999 if (const ValueDecl *VD = DRE->getDecl())
1000 return isa<EnumConstantDecl>(VD);
1001
1002 return false;
1003}
1004
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +00001005static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
1006 const NSAPI &NS, Commit &commit) {
1007 if (Msg->getNumArgs() != 1)
1008 return false;
1009
1010 const Expr *Arg = Msg->getArg(0);
1011 if (Arg->isTypeDependent())
1012 return false;
1013
1014 ASTContext &Ctx = NS.getASTContext();
1015 Selector Sel = Msg->getSelector();
David Blaikiedc84cd52013-02-20 22:23:23 +00001016 Optional<NSAPI::NSNumberLiteralMethodKind>
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +00001017 MKOpt = NS.getNSNumberLiteralMethodKind(Sel);
1018 if (!MKOpt)
1019 return false;
1020 NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;
1021
1022 const Expr *OrigArg = Arg->IgnoreImpCasts();
1023 QualType FinalTy = Arg->getType();
1024 QualType OrigTy = OrigArg->getType();
1025 uint64_t FinalTySize = Ctx.getTypeSize(FinalTy);
1026 uint64_t OrigTySize = Ctx.getTypeSize(OrigTy);
1027
1028 bool isTruncated = FinalTySize < OrigTySize;
1029 bool needsCast = false;
1030
1031 if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) {
1032 switch (ICE->getCastKind()) {
1033 case CK_LValueToRValue:
1034 case CK_NoOp:
1035 case CK_UserDefinedConversion:
1036 break;
1037
1038 case CK_IntegralCast: {
1039 if (MK == NSAPI::NSNumberWithBool && OrigTy->isBooleanType())
1040 break;
1041 // Be more liberal with Integer/UnsignedInteger which are very commonly
1042 // used.
1043 if ((MK == NSAPI::NSNumberWithInteger ||
1044 MK == NSAPI::NSNumberWithUnsignedInteger) &&
1045 !isTruncated) {
Argyrios Kyrtzidisc5d33e92012-05-15 22:59:54 +00001046 if (OrigTy->getAs<EnumType>() || isEnumConstant(OrigArg))
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +00001047 break;
1048 if ((MK==NSAPI::NSNumberWithInteger) == OrigTy->isSignedIntegerType() &&
1049 OrigTySize >= Ctx.getTypeSize(Ctx.IntTy))
1050 break;
1051 }
1052
1053 needsCast = true;
1054 break;
1055 }
1056
1057 case CK_PointerToBoolean:
1058 case CK_IntegralToBoolean:
1059 case CK_IntegralToFloating:
1060 case CK_FloatingToIntegral:
1061 case CK_FloatingToBoolean:
1062 case CK_FloatingCast:
1063 case CK_FloatingComplexToReal:
1064 case CK_FloatingComplexToBoolean:
1065 case CK_IntegralComplexToReal:
1066 case CK_IntegralComplexToBoolean:
1067 case CK_AtomicToNonAtomic:
1068 needsCast = true;
1069 break;
1070
1071 case CK_Dependent:
1072 case CK_BitCast:
1073 case CK_LValueBitCast:
1074 case CK_BaseToDerived:
1075 case CK_DerivedToBase:
1076 case CK_UncheckedDerivedToBase:
1077 case CK_Dynamic:
1078 case CK_ToUnion:
1079 case CK_ArrayToPointerDecay:
1080 case CK_FunctionToPointerDecay:
1081 case CK_NullToPointer:
1082 case CK_NullToMemberPointer:
1083 case CK_BaseToDerivedMemberPointer:
1084 case CK_DerivedToBaseMemberPointer:
1085 case CK_MemberPointerToBoolean:
1086 case CK_ReinterpretMemberPointer:
1087 case CK_ConstructorConversion:
1088 case CK_IntegralToPointer:
1089 case CK_PointerToIntegral:
1090 case CK_ToVoid:
1091 case CK_VectorSplat:
1092 case CK_CPointerToObjCPointerCast:
1093 case CK_BlockPointerToObjCPointerCast:
1094 case CK_AnyPointerToBlockPointerCast:
1095 case CK_ObjCObjectLValueCast:
1096 case CK_FloatingRealToComplex:
1097 case CK_FloatingComplexCast:
1098 case CK_FloatingComplexToIntegralComplex:
1099 case CK_IntegralRealToComplex:
1100 case CK_IntegralComplexCast:
1101 case CK_IntegralComplexToFloatingComplex:
1102 case CK_ARCProduceObject:
1103 case CK_ARCConsumeObject:
1104 case CK_ARCReclaimReturnedObject:
1105 case CK_ARCExtendBlockObject:
1106 case CK_NonAtomicToAtomic:
1107 case CK_CopyAndAutoreleaseBlockObject:
Eli Friedmana6c66ce2012-08-31 00:14:07 +00001108 case CK_BuiltinFnToFnPtr:
Guy Benyeie6b9d802013-01-20 12:31:11 +00001109 case CK_ZeroToOCLEvent:
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +00001110 return false;
1111 }
1112 }
1113
Argyrios Kyrtzidis013a2542012-05-24 16:48:23 +00001114 if (needsCast) {
1115 DiagnosticsEngine &Diags = Ctx.getDiagnostics();
1116 // FIXME: Use a custom category name to distinguish migration diagnostics.
1117 unsigned diagID = Diags.getCustomDiagID(DiagnosticsEngine::Warning,
Argyrios Kyrtzidis8b9fcd72012-06-20 01:28:32 +00001118 "converting to boxing syntax requires casting %0 to %1");
1119 Diags.Report(Msg->getExprLoc(), diagID) << OrigTy << FinalTy
1120 << Msg->getSourceRange();
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +00001121 return false;
Argyrios Kyrtzidis013a2542012-05-24 16:48:23 +00001122 }
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +00001123
1124 SourceRange ArgRange = OrigArg->getSourceRange();
Argyrios Kyrtzidis7fe103c2012-05-15 22:22:10 +00001125 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +00001126
1127 if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
1128 commit.insertBefore(ArgRange.getBegin(), "@");
1129 else
1130 commit.insertWrap("@(", ArgRange, ")");
1131
1132 return true;
1133}
Argyrios Kyrtzidis7fe103c2012-05-15 22:22:10 +00001134
1135//===----------------------------------------------------------------------===//
1136// rewriteToStringBoxedExpression.
1137//===----------------------------------------------------------------------===//
1138
1139static bool doRewriteToUTF8StringBoxedExpressionHelper(
1140 const ObjCMessageExpr *Msg,
1141 const NSAPI &NS, Commit &commit) {
1142 const Expr *Arg = Msg->getArg(0);
1143 if (Arg->isTypeDependent())
1144 return false;
1145
Argyrios Kyrtzidis07736592012-05-16 00:21:21 +00001146 ASTContext &Ctx = NS.getASTContext();
1147
Argyrios Kyrtzidis7fe103c2012-05-15 22:22:10 +00001148 const Expr *OrigArg = Arg->IgnoreImpCasts();
1149 QualType OrigTy = OrigArg->getType();
Argyrios Kyrtzidis07736592012-05-16 00:21:21 +00001150 if (OrigTy->isArrayType())
1151 OrigTy = Ctx.getArrayDecayedType(OrigTy);
Argyrios Kyrtzidis7fe103c2012-05-15 22:22:10 +00001152
1153 if (const StringLiteral *
1154 StrE = dyn_cast<StringLiteral>(OrigArg->IgnoreParens())) {
1155 commit.replaceWithInner(Msg->getSourceRange(), StrE->getSourceRange());
1156 commit.insert(StrE->getLocStart(), "@");
1157 return true;
1158 }
1159
Argyrios Kyrtzidis7fe103c2012-05-15 22:22:10 +00001160 if (const PointerType *PT = OrigTy->getAs<PointerType>()) {
1161 QualType PointeeType = PT->getPointeeType();
1162 if (Ctx.hasSameUnqualifiedType(PointeeType, Ctx.CharTy)) {
1163 SourceRange ArgRange = OrigArg->getSourceRange();
1164 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
1165
1166 if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
1167 commit.insertBefore(ArgRange.getBegin(), "@");
1168 else
1169 commit.insertWrap("@(", ArgRange, ")");
1170
1171 return true;
1172 }
1173 }
1174
1175 return false;
1176}
1177
1178static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
1179 const NSAPI &NS, Commit &commit) {
1180 Selector Sel = Msg->getSelector();
1181
1182 if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithUTF8String) ||
1183 Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCString)) {
1184 if (Msg->getNumArgs() != 1)
1185 return false;
1186 return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);
1187 }
1188
1189 if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCStringEncoding)) {
1190 if (Msg->getNumArgs() != 2)
1191 return false;
1192
1193 const Expr *encodingArg = Msg->getArg(1);
1194 if (NS.isNSUTF8StringEncodingConstant(encodingArg) ||
1195 NS.isNSASCIIStringEncodingConstant(encodingArg))
1196 return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);
1197 }
1198
1199 return false;
1200}