blob: 9af53037eacb6f8d8af57758a45aa2cc8f3458be [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) {
361 return false;
362}
363
Argyrios Kyrtzidisa44b9702013-01-16 23:54:48 +0000364/// \brief Returns true if the immediate message arguments of \c Msg should not
365/// be rewritten because it will interfere with the rewrite of the parent
366/// message expression. e.g.
367/// \code
368/// [NSDictionary dictionaryWithObjects:
369/// [NSArray arrayWithObjects:@"1", @"2", nil]
370/// forKeys:[NSArray arrayWithObjects:@"A", @"B", nil]];
371/// \endcode
372/// It will return true for this because we are going to rewrite this directly
373/// to a dictionary literal without any array literals.
374static bool shouldNotRewriteImmediateMessageArgs(const ObjCMessageExpr *Msg,
375 const NSAPI &NS);
376
Ted Kremenek30660a82012-03-06 20:06:33 +0000377//===----------------------------------------------------------------------===//
378// rewriteToArrayLiteral.
379//===----------------------------------------------------------------------===//
380
Argyrios Kyrtzidis055b3952012-05-14 22:01:53 +0000381/// \brief Adds an explicit cast to 'id' if the type is not objc object.
382static void objectifyExpr(const Expr *E, Commit &commit);
383
Ted Kremenek30660a82012-03-06 20:06:33 +0000384static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
Argyrios Kyrtzidisa44b9702013-01-16 23:54:48 +0000385 const NSAPI &NS, Commit &commit,
386 const ParentMap *PMap) {
387 if (PMap) {
388 const ObjCMessageExpr *ParentMsg =
389 dyn_cast_or_null<ObjCMessageExpr>(PMap->getParentIgnoreParenCasts(Msg));
390 if (shouldNotRewriteImmediateMessageArgs(ParentMsg, NS))
391 return false;
392 }
393
Ted Kremenek30660a82012-03-06 20:06:33 +0000394 Selector Sel = Msg->getSelector();
395 SourceRange MsgRange = Msg->getSourceRange();
396
397 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array)) {
398 if (Msg->getNumArgs() != 0)
399 return false;
400 commit.replace(MsgRange, "@[]");
401 return true;
402 }
403
404 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) {
405 if (Msg->getNumArgs() != 1)
406 return false;
Argyrios Kyrtzidis055b3952012-05-14 22:01:53 +0000407 objectifyExpr(Msg->getArg(0), commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000408 SourceRange ArgRange = Msg->getArg(0)->getSourceRange();
409 commit.replaceWithInner(MsgRange, ArgRange);
410 commit.insertWrap("@[", ArgRange, "]");
411 return true;
412 }
413
Argyrios Kyrtzidis6b4db9b2012-06-06 22:23:12 +0000414 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) ||
415 Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) {
Ted Kremenek30660a82012-03-06 20:06:33 +0000416 if (Msg->getNumArgs() == 0)
417 return false;
418 const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1);
419 if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
420 return false;
421
Argyrios Kyrtzidis055b3952012-05-14 22:01:53 +0000422 for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i)
423 objectifyExpr(Msg->getArg(i), commit);
424
Ted Kremenek30660a82012-03-06 20:06:33 +0000425 if (Msg->getNumArgs() == 1) {
426 commit.replace(MsgRange, "@[]");
427 return true;
428 }
429 SourceRange ArgRange(Msg->getArg(0)->getLocStart(),
430 Msg->getArg(Msg->getNumArgs()-2)->getLocEnd());
431 commit.replaceWithInner(MsgRange, ArgRange);
432 commit.insertWrap("@[", ArgRange, "]");
433 return true;
434 }
435
436 return false;
437}
438
439//===----------------------------------------------------------------------===//
440// rewriteToDictionaryLiteral.
441//===----------------------------------------------------------------------===//
442
Argyrios Kyrtzidisa44b9702013-01-16 23:54:48 +0000443/// \brief If \c Msg is an NSArray creation message or literal, this gets the
444/// objects that were used to create it.
445/// \returns true if it is an NSArray and we got objects, or false otherwise.
446static bool getNSArrayObjects(const Expr *E, const NSAPI &NS,
447 SmallVectorImpl<const Expr *> &Objs) {
448 if (!E)
449 return false;
450
451 E = E->IgnoreParenCasts();
452 if (!E)
453 return false;
454
455 if (const ObjCMessageExpr *Msg = dyn_cast<ObjCMessageExpr>(E)) {
456 IdentifierInfo *Cls = 0;
457 if (!checkForLiteralCreation(Msg, Cls, NS.getASTContext().getLangOpts()))
458 return false;
459
460 if (Cls != NS.getNSClassId(NSAPI::ClassId_NSArray))
461 return false;
462
463 Selector Sel = Msg->getSelector();
464 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array))
465 return true; // empty array.
466
467 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) {
468 if (Msg->getNumArgs() != 1)
469 return false;
470 Objs.push_back(Msg->getArg(0));
471 return true;
472 }
473
474 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) ||
475 Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) {
476 if (Msg->getNumArgs() == 0)
477 return false;
478 const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1);
479 if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
480 return false;
481
482 for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i)
483 Objs.push_back(Msg->getArg(i));
484 return true;
485 }
486
487 } else if (const ObjCArrayLiteral *ArrLit = dyn_cast<ObjCArrayLiteral>(E)) {
488 for (unsigned i = 0, e = ArrLit->getNumElements(); i != e; ++i)
489 Objs.push_back(ArrLit->getElement(i));
490 return true;
491 }
492
493 return false;
494}
495
Ted Kremenek30660a82012-03-06 20:06:33 +0000496static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
497 const NSAPI &NS, Commit &commit) {
498 Selector Sel = Msg->getSelector();
499 SourceRange MsgRange = Msg->getSourceRange();
500
501 if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_dictionary)) {
502 if (Msg->getNumArgs() != 0)
503 return false;
504 commit.replace(MsgRange, "@{}");
505 return true;
506 }
507
508 if (Sel == NS.getNSDictionarySelector(
509 NSAPI::NSDict_dictionaryWithObjectForKey)) {
510 if (Msg->getNumArgs() != 2)
511 return false;
Argyrios Kyrtzidis055b3952012-05-14 22:01:53 +0000512
513 objectifyExpr(Msg->getArg(0), commit);
514 objectifyExpr(Msg->getArg(1), commit);
515
Ted Kremenek30660a82012-03-06 20:06:33 +0000516 SourceRange ValRange = Msg->getArg(0)->getSourceRange();
517 SourceRange KeyRange = Msg->getArg(1)->getSourceRange();
518 // Insert key before the value.
519 commit.insertBefore(ValRange.getBegin(), ": ");
520 commit.insertFromRange(ValRange.getBegin(),
521 CharSourceRange::getTokenRange(KeyRange),
522 /*afterToken=*/false, /*beforePreviousInsertions=*/true);
523 commit.insertBefore(ValRange.getBegin(), "@{");
524 commit.insertAfterToken(ValRange.getEnd(), "}");
525 commit.replaceWithInner(MsgRange, ValRange);
526 return true;
527 }
528
529 if (Sel == NS.getNSDictionarySelector(
Argyrios Kyrtzidis6b4db9b2012-06-06 22:23:12 +0000530 NSAPI::NSDict_dictionaryWithObjectsAndKeys) ||
531 Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsAndKeys)) {
Ted Kremenek30660a82012-03-06 20:06:33 +0000532 if (Msg->getNumArgs() % 2 != 1)
533 return false;
534 unsigned SentinelIdx = Msg->getNumArgs() - 1;
535 const Expr *SentinelExpr = Msg->getArg(SentinelIdx);
536 if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
537 return false;
538
539 if (Msg->getNumArgs() == 1) {
540 commit.replace(MsgRange, "@{}");
541 return true;
542 }
543
544 for (unsigned i = 0; i < SentinelIdx; i += 2) {
Argyrios Kyrtzidis055b3952012-05-14 22:01:53 +0000545 objectifyExpr(Msg->getArg(i), commit);
546 objectifyExpr(Msg->getArg(i+1), commit);
547
Ted Kremenek30660a82012-03-06 20:06:33 +0000548 SourceRange ValRange = Msg->getArg(i)->getSourceRange();
549 SourceRange KeyRange = Msg->getArg(i+1)->getSourceRange();
550 // Insert value after key.
551 commit.insertAfterToken(KeyRange.getEnd(), ": ");
552 commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true);
553 commit.remove(CharSourceRange::getCharRange(ValRange.getBegin(),
554 KeyRange.getBegin()));
555 }
556 // Range of arguments up until and including the last key.
557 // The sentinel and first value are cut off, the value will move after the
558 // key.
559 SourceRange ArgRange(Msg->getArg(1)->getLocStart(),
560 Msg->getArg(SentinelIdx-1)->getLocEnd());
561 commit.insertWrap("@{", ArgRange, "}");
562 commit.replaceWithInner(MsgRange, ArgRange);
563 return true;
564 }
565
Argyrios Kyrtzidisa44b9702013-01-16 23:54:48 +0000566 if (Sel == NS.getNSDictionarySelector(
567 NSAPI::NSDict_dictionaryWithObjectsForKeys) ||
568 Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsForKeys)) {
569 if (Msg->getNumArgs() != 2)
570 return false;
571
572 SmallVector<const Expr *, 8> Vals;
573 if (!getNSArrayObjects(Msg->getArg(0), NS, Vals))
574 return false;
575
576 SmallVector<const Expr *, 8> Keys;
577 if (!getNSArrayObjects(Msg->getArg(1), NS, Keys))
578 return false;
579
580 if (Vals.size() != Keys.size())
581 return false;
582
583 if (Vals.empty()) {
584 commit.replace(MsgRange, "@{}");
585 return true;
586 }
587
588 for (unsigned i = 0, n = Vals.size(); i < n; ++i) {
589 objectifyExpr(Vals[i], commit);
590 objectifyExpr(Keys[i], commit);
591
592 SourceRange ValRange = Vals[i]->getSourceRange();
593 SourceRange KeyRange = Keys[i]->getSourceRange();
594 // Insert value after key.
595 commit.insertAfterToken(KeyRange.getEnd(), ": ");
596 commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true);
597 }
598 // Range of arguments up until and including the last key.
599 // The first value is cut off, the value will move after the key.
600 SourceRange ArgRange(Keys.front()->getLocStart(),
601 Keys.back()->getLocEnd());
602 commit.insertWrap("@{", ArgRange, "}");
603 commit.replaceWithInner(MsgRange, ArgRange);
604 return true;
605 }
606
607 return false;
608}
609
610static bool shouldNotRewriteImmediateMessageArgs(const ObjCMessageExpr *Msg,
611 const NSAPI &NS) {
612 if (!Msg)
613 return false;
614
615 IdentifierInfo *II = 0;
616 if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
617 return false;
618
619 if (II != NS.getNSClassId(NSAPI::ClassId_NSDictionary))
620 return false;
621
622 Selector Sel = Msg->getSelector();
623 if (Sel == NS.getNSDictionarySelector(
624 NSAPI::NSDict_dictionaryWithObjectsForKeys) ||
625 Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsForKeys)) {
626 if (Msg->getNumArgs() != 2)
627 return false;
628
629 SmallVector<const Expr *, 8> Vals;
630 if (!getNSArrayObjects(Msg->getArg(0), NS, Vals))
631 return false;
632
633 SmallVector<const Expr *, 8> Keys;
634 if (!getNSArrayObjects(Msg->getArg(1), NS, Keys))
635 return false;
636
637 if (Vals.size() != Keys.size())
638 return false;
639
640 return true;
641 }
642
Ted Kremenek30660a82012-03-06 20:06:33 +0000643 return false;
644}
645
646//===----------------------------------------------------------------------===//
647// rewriteToNumberLiteral.
648//===----------------------------------------------------------------------===//
649
650static bool rewriteToCharLiteral(const ObjCMessageExpr *Msg,
651 const CharacterLiteral *Arg,
652 const NSAPI &NS, Commit &commit) {
653 if (Arg->getKind() != CharacterLiteral::Ascii)
654 return false;
655 if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithChar,
656 Msg->getSelector())) {
657 SourceRange ArgRange = Arg->getSourceRange();
658 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
659 commit.insert(ArgRange.getBegin(), "@");
660 return true;
661 }
662
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000663 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000664}
665
666static bool rewriteToBoolLiteral(const ObjCMessageExpr *Msg,
667 const Expr *Arg,
668 const NSAPI &NS, Commit &commit) {
669 if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithBool,
670 Msg->getSelector())) {
671 SourceRange ArgRange = Arg->getSourceRange();
672 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
673 commit.insert(ArgRange.getBegin(), "@");
674 return true;
675 }
676
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000677 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000678}
679
680namespace {
681
682struct LiteralInfo {
683 bool Hex, Octal;
684 StringRef U, F, L, LL;
685 CharSourceRange WithoutSuffRange;
686};
687
688}
689
690static bool getLiteralInfo(SourceRange literalRange,
691 bool isFloat, bool isIntZero,
692 ASTContext &Ctx, LiteralInfo &Info) {
693 if (literalRange.getBegin().isMacroID() ||
694 literalRange.getEnd().isMacroID())
695 return false;
696 StringRef text = Lexer::getSourceText(
697 CharSourceRange::getTokenRange(literalRange),
David Blaikie4e4d0842012-03-11 07:00:24 +0000698 Ctx.getSourceManager(), Ctx.getLangOpts());
Ted Kremenek30660a82012-03-06 20:06:33 +0000699 if (text.empty())
700 return false;
701
David Blaikiedc84cd52013-02-20 22:23:23 +0000702 Optional<bool> UpperU, UpperL;
Ted Kremenek30660a82012-03-06 20:06:33 +0000703 bool UpperF = false;
704
705 struct Suff {
706 static bool has(StringRef suff, StringRef &text) {
707 if (text.endswith(suff)) {
708 text = text.substr(0, text.size()-suff.size());
709 return true;
710 }
711 return false;
712 }
713 };
714
715 while (1) {
716 if (Suff::has("u", text)) {
717 UpperU = false;
718 } else if (Suff::has("U", text)) {
719 UpperU = true;
720 } else if (Suff::has("ll", text)) {
721 UpperL = false;
722 } else if (Suff::has("LL", text)) {
723 UpperL = true;
724 } else if (Suff::has("l", text)) {
725 UpperL = false;
726 } else if (Suff::has("L", text)) {
727 UpperL = true;
728 } else if (isFloat && Suff::has("f", text)) {
729 UpperF = false;
730 } else if (isFloat && Suff::has("F", text)) {
731 UpperF = true;
732 } else
733 break;
734 }
735
736 if (!UpperU.hasValue() && !UpperL.hasValue())
737 UpperU = UpperL = true;
738 else if (UpperU.hasValue() && !UpperL.hasValue())
739 UpperL = UpperU;
740 else if (UpperL.hasValue() && !UpperU.hasValue())
741 UpperU = UpperL;
742
743 Info.U = *UpperU ? "U" : "u";
744 Info.L = *UpperL ? "L" : "l";
745 Info.LL = *UpperL ? "LL" : "ll";
746 Info.F = UpperF ? "F" : "f";
747
748 Info.Hex = Info.Octal = false;
749 if (text.startswith("0x"))
750 Info.Hex = true;
751 else if (!isFloat && !isIntZero && text.startswith("0"))
752 Info.Octal = true;
753
754 SourceLocation B = literalRange.getBegin();
755 Info.WithoutSuffRange =
756 CharSourceRange::getCharRange(B, B.getLocWithOffset(text.size()));
757 return true;
758}
759
760static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
761 const NSAPI &NS, Commit &commit) {
762 if (Msg->getNumArgs() != 1)
763 return false;
764
765 const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts();
766 if (const CharacterLiteral *CharE = dyn_cast<CharacterLiteral>(Arg))
767 return rewriteToCharLiteral(Msg, CharE, NS, commit);
768 if (const ObjCBoolLiteralExpr *BE = dyn_cast<ObjCBoolLiteralExpr>(Arg))
769 return rewriteToBoolLiteral(Msg, BE, NS, commit);
770 if (const CXXBoolLiteralExpr *BE = dyn_cast<CXXBoolLiteralExpr>(Arg))
771 return rewriteToBoolLiteral(Msg, BE, NS, commit);
772
773 const Expr *literalE = Arg;
774 if (const UnaryOperator *UOE = dyn_cast<UnaryOperator>(literalE)) {
775 if (UOE->getOpcode() == UO_Plus || UOE->getOpcode() == UO_Minus)
776 literalE = UOE->getSubExpr();
777 }
778
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000779 // Only integer and floating literals, otherwise try to rewrite to boxed
780 // expression.
Ted Kremenek30660a82012-03-06 20:06:33 +0000781 if (!isa<IntegerLiteral>(literalE) && !isa<FloatingLiteral>(literalE))
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000782 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000783
784 ASTContext &Ctx = NS.getASTContext();
785 Selector Sel = Msg->getSelector();
David Blaikiedc84cd52013-02-20 22:23:23 +0000786 Optional<NSAPI::NSNumberLiteralMethodKind>
Ted Kremenek30660a82012-03-06 20:06:33 +0000787 MKOpt = NS.getNSNumberLiteralMethodKind(Sel);
788 if (!MKOpt)
789 return false;
790 NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;
791
Benjamin Kramerbea6c0a2012-03-13 17:05:43 +0000792 bool CallIsUnsigned = false, CallIsLong = false, CallIsLongLong = false;
Ted Kremenek30660a82012-03-06 20:06:33 +0000793 bool CallIsFloating = false, CallIsDouble = false;
794
795 switch (MK) {
796 // We cannot have these calls with int/float literals.
797 case NSAPI::NSNumberWithChar:
798 case NSAPI::NSNumberWithUnsignedChar:
799 case NSAPI::NSNumberWithShort:
800 case NSAPI::NSNumberWithUnsignedShort:
801 case NSAPI::NSNumberWithBool:
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000802 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000803
804 case NSAPI::NSNumberWithUnsignedInt:
805 case NSAPI::NSNumberWithUnsignedInteger:
806 CallIsUnsigned = true;
807 case NSAPI::NSNumberWithInt:
808 case NSAPI::NSNumberWithInteger:
Ted Kremenek30660a82012-03-06 20:06:33 +0000809 break;
810
811 case NSAPI::NSNumberWithUnsignedLong:
812 CallIsUnsigned = true;
813 case NSAPI::NSNumberWithLong:
Benjamin Kramerbea6c0a2012-03-13 17:05:43 +0000814 CallIsLong = true;
Ted Kremenek30660a82012-03-06 20:06:33 +0000815 break;
816
817 case NSAPI::NSNumberWithUnsignedLongLong:
818 CallIsUnsigned = true;
819 case NSAPI::NSNumberWithLongLong:
Benjamin Kramerbea6c0a2012-03-13 17:05:43 +0000820 CallIsLongLong = true;
Ted Kremenek30660a82012-03-06 20:06:33 +0000821 break;
822
823 case NSAPI::NSNumberWithDouble:
824 CallIsDouble = true;
825 case NSAPI::NSNumberWithFloat:
826 CallIsFloating = true;
827 break;
828 }
829
830 SourceRange ArgRange = Arg->getSourceRange();
831 QualType ArgTy = Arg->getType();
832 QualType CallTy = Msg->getArg(0)->getType();
833
834 // Check for the easy case, the literal maps directly to the call.
835 if (Ctx.hasSameType(ArgTy, CallTy)) {
836 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
837 commit.insert(ArgRange.getBegin(), "@");
838 return true;
839 }
840
841 // We will need to modify the literal suffix to get the same type as the call.
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000842 // Try with boxed expression if it came from a macro.
Ted Kremenek30660a82012-03-06 20:06:33 +0000843 if (ArgRange.getBegin().isMacroID())
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000844 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000845
846 bool LitIsFloat = ArgTy->isFloatingType();
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000847 // For a float passed to integer call, don't try rewriting to objc literal.
848 // It is difficult and a very uncommon case anyway.
849 // But try with boxed expression.
Ted Kremenek30660a82012-03-06 20:06:33 +0000850 if (LitIsFloat && !CallIsFloating)
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000851 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000852
853 // Try to modify the literal make it the same type as the method call.
854 // -Modify the suffix, and/or
855 // -Change integer to float
856
857 LiteralInfo LitInfo;
858 bool isIntZero = false;
859 if (const IntegerLiteral *IntE = dyn_cast<IntegerLiteral>(literalE))
860 isIntZero = !IntE->getValue().getBoolValue();
861 if (!getLiteralInfo(ArgRange, LitIsFloat, isIntZero, Ctx, LitInfo))
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000862 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000863
864 // Not easy to do int -> float with hex/octal and uncommon anyway.
865 if (!LitIsFloat && CallIsFloating && (LitInfo.Hex || LitInfo.Octal))
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000866 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000867
868 SourceLocation LitB = LitInfo.WithoutSuffRange.getBegin();
869 SourceLocation LitE = LitInfo.WithoutSuffRange.getEnd();
870
871 commit.replaceWithInner(CharSourceRange::getTokenRange(Msg->getSourceRange()),
872 LitInfo.WithoutSuffRange);
873 commit.insert(LitB, "@");
874
875 if (!LitIsFloat && CallIsFloating)
876 commit.insert(LitE, ".0");
877
878 if (CallIsFloating) {
879 if (!CallIsDouble)
880 commit.insert(LitE, LitInfo.F);
881 } else {
882 if (CallIsUnsigned)
883 commit.insert(LitE, LitInfo.U);
884
885 if (CallIsLong)
886 commit.insert(LitE, LitInfo.L);
887 else if (CallIsLongLong)
888 commit.insert(LitE, LitInfo.LL);
889 }
890 return true;
891}
Argyrios Kyrtzidis055b3952012-05-14 22:01:53 +0000892
Argyrios Kyrtzidis20119a82012-05-14 23:33:49 +0000893// FIXME: Make determination of operator precedence more general and
894// make it broadly available.
895static bool subscriptOperatorNeedsParens(const Expr *FullExpr) {
896 const Expr* Expr = FullExpr->IgnoreImpCasts();
897 if (isa<ArraySubscriptExpr>(Expr) ||
898 isa<CallExpr>(Expr) ||
899 isa<DeclRefExpr>(Expr) ||
900 isa<CXXNamedCastExpr>(Expr) ||
901 isa<CXXConstructExpr>(Expr) ||
902 isa<CXXThisExpr>(Expr) ||
903 isa<CXXTypeidExpr>(Expr) ||
904 isa<CXXUnresolvedConstructExpr>(Expr) ||
905 isa<ObjCMessageExpr>(Expr) ||
906 isa<ObjCPropertyRefExpr>(Expr) ||
907 isa<ObjCProtocolExpr>(Expr) ||
908 isa<MemberExpr>(Expr) ||
Argyrios Kyrtzidis2bddd432012-05-22 00:47:53 +0000909 isa<ObjCIvarRefExpr>(Expr) ||
Argyrios Kyrtzidis20119a82012-05-14 23:33:49 +0000910 isa<ParenExpr>(FullExpr) ||
911 isa<ParenListExpr>(Expr) ||
912 isa<SizeOfPackExpr>(Expr))
913 return false;
914
915 return true;
916}
Argyrios Kyrtzidis055b3952012-05-14 22:01:53 +0000917static bool castOperatorNeedsParens(const Expr *FullExpr) {
918 const Expr* Expr = FullExpr->IgnoreImpCasts();
919 if (isa<ArraySubscriptExpr>(Expr) ||
920 isa<CallExpr>(Expr) ||
921 isa<DeclRefExpr>(Expr) ||
922 isa<CastExpr>(Expr) ||
923 isa<CXXNewExpr>(Expr) ||
924 isa<CXXConstructExpr>(Expr) ||
925 isa<CXXDeleteExpr>(Expr) ||
926 isa<CXXNoexceptExpr>(Expr) ||
927 isa<CXXPseudoDestructorExpr>(Expr) ||
928 isa<CXXScalarValueInitExpr>(Expr) ||
929 isa<CXXThisExpr>(Expr) ||
930 isa<CXXTypeidExpr>(Expr) ||
931 isa<CXXUnresolvedConstructExpr>(Expr) ||
932 isa<ObjCMessageExpr>(Expr) ||
933 isa<ObjCPropertyRefExpr>(Expr) ||
934 isa<ObjCProtocolExpr>(Expr) ||
935 isa<MemberExpr>(Expr) ||
Argyrios Kyrtzidis2bddd432012-05-22 00:47:53 +0000936 isa<ObjCIvarRefExpr>(Expr) ||
Argyrios Kyrtzidis055b3952012-05-14 22:01:53 +0000937 isa<ParenExpr>(FullExpr) ||
938 isa<ParenListExpr>(Expr) ||
939 isa<SizeOfPackExpr>(Expr) ||
940 isa<UnaryOperator>(Expr))
941 return false;
942
943 return true;
944}
945
946static void objectifyExpr(const Expr *E, Commit &commit) {
947 if (!E) return;
948
949 QualType T = E->getType();
950 if (T->isObjCObjectPointerType()) {
951 if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) {
952 if (ICE->getCastKind() != CK_CPointerToObjCPointerCast)
953 return;
954 } else {
955 return;
956 }
957 } else if (!T->isPointerType()) {
958 return;
959 }
960
961 SourceRange Range = E->getSourceRange();
962 if (castOperatorNeedsParens(E))
963 commit.insertWrap("(", Range, ")");
964 commit.insertBefore(Range.getBegin(), "(id)");
965}
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000966
967//===----------------------------------------------------------------------===//
968// rewriteToNumericBoxedExpression.
969//===----------------------------------------------------------------------===//
970
Argyrios Kyrtzidisc5d33e92012-05-15 22:59:54 +0000971static bool isEnumConstant(const Expr *E) {
972 if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts()))
973 if (const ValueDecl *VD = DRE->getDecl())
974 return isa<EnumConstantDecl>(VD);
975
976 return false;
977}
978
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000979static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
980 const NSAPI &NS, Commit &commit) {
981 if (Msg->getNumArgs() != 1)
982 return false;
983
984 const Expr *Arg = Msg->getArg(0);
985 if (Arg->isTypeDependent())
986 return false;
987
988 ASTContext &Ctx = NS.getASTContext();
989 Selector Sel = Msg->getSelector();
David Blaikiedc84cd52013-02-20 22:23:23 +0000990 Optional<NSAPI::NSNumberLiteralMethodKind>
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000991 MKOpt = NS.getNSNumberLiteralMethodKind(Sel);
992 if (!MKOpt)
993 return false;
994 NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;
995
996 const Expr *OrigArg = Arg->IgnoreImpCasts();
997 QualType FinalTy = Arg->getType();
998 QualType OrigTy = OrigArg->getType();
999 uint64_t FinalTySize = Ctx.getTypeSize(FinalTy);
1000 uint64_t OrigTySize = Ctx.getTypeSize(OrigTy);
1001
1002 bool isTruncated = FinalTySize < OrigTySize;
1003 bool needsCast = false;
1004
1005 if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) {
1006 switch (ICE->getCastKind()) {
1007 case CK_LValueToRValue:
1008 case CK_NoOp:
1009 case CK_UserDefinedConversion:
1010 break;
1011
1012 case CK_IntegralCast: {
1013 if (MK == NSAPI::NSNumberWithBool && OrigTy->isBooleanType())
1014 break;
1015 // Be more liberal with Integer/UnsignedInteger which are very commonly
1016 // used.
1017 if ((MK == NSAPI::NSNumberWithInteger ||
1018 MK == NSAPI::NSNumberWithUnsignedInteger) &&
1019 !isTruncated) {
Argyrios Kyrtzidisc5d33e92012-05-15 22:59:54 +00001020 if (OrigTy->getAs<EnumType>() || isEnumConstant(OrigArg))
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +00001021 break;
1022 if ((MK==NSAPI::NSNumberWithInteger) == OrigTy->isSignedIntegerType() &&
1023 OrigTySize >= Ctx.getTypeSize(Ctx.IntTy))
1024 break;
1025 }
1026
1027 needsCast = true;
1028 break;
1029 }
1030
1031 case CK_PointerToBoolean:
1032 case CK_IntegralToBoolean:
1033 case CK_IntegralToFloating:
1034 case CK_FloatingToIntegral:
1035 case CK_FloatingToBoolean:
1036 case CK_FloatingCast:
1037 case CK_FloatingComplexToReal:
1038 case CK_FloatingComplexToBoolean:
1039 case CK_IntegralComplexToReal:
1040 case CK_IntegralComplexToBoolean:
1041 case CK_AtomicToNonAtomic:
1042 needsCast = true;
1043 break;
1044
1045 case CK_Dependent:
1046 case CK_BitCast:
1047 case CK_LValueBitCast:
1048 case CK_BaseToDerived:
1049 case CK_DerivedToBase:
1050 case CK_UncheckedDerivedToBase:
1051 case CK_Dynamic:
1052 case CK_ToUnion:
1053 case CK_ArrayToPointerDecay:
1054 case CK_FunctionToPointerDecay:
1055 case CK_NullToPointer:
1056 case CK_NullToMemberPointer:
1057 case CK_BaseToDerivedMemberPointer:
1058 case CK_DerivedToBaseMemberPointer:
1059 case CK_MemberPointerToBoolean:
1060 case CK_ReinterpretMemberPointer:
1061 case CK_ConstructorConversion:
1062 case CK_IntegralToPointer:
1063 case CK_PointerToIntegral:
1064 case CK_ToVoid:
1065 case CK_VectorSplat:
1066 case CK_CPointerToObjCPointerCast:
1067 case CK_BlockPointerToObjCPointerCast:
1068 case CK_AnyPointerToBlockPointerCast:
1069 case CK_ObjCObjectLValueCast:
1070 case CK_FloatingRealToComplex:
1071 case CK_FloatingComplexCast:
1072 case CK_FloatingComplexToIntegralComplex:
1073 case CK_IntegralRealToComplex:
1074 case CK_IntegralComplexCast:
1075 case CK_IntegralComplexToFloatingComplex:
1076 case CK_ARCProduceObject:
1077 case CK_ARCConsumeObject:
1078 case CK_ARCReclaimReturnedObject:
1079 case CK_ARCExtendBlockObject:
1080 case CK_NonAtomicToAtomic:
1081 case CK_CopyAndAutoreleaseBlockObject:
Eli Friedmana6c66ce2012-08-31 00:14:07 +00001082 case CK_BuiltinFnToFnPtr:
Guy Benyeie6b9d802013-01-20 12:31:11 +00001083 case CK_ZeroToOCLEvent:
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +00001084 return false;
1085 }
1086 }
1087
Argyrios Kyrtzidis013a2542012-05-24 16:48:23 +00001088 if (needsCast) {
1089 DiagnosticsEngine &Diags = Ctx.getDiagnostics();
1090 // FIXME: Use a custom category name to distinguish migration diagnostics.
1091 unsigned diagID = Diags.getCustomDiagID(DiagnosticsEngine::Warning,
Argyrios Kyrtzidis8b9fcd72012-06-20 01:28:32 +00001092 "converting to boxing syntax requires casting %0 to %1");
1093 Diags.Report(Msg->getExprLoc(), diagID) << OrigTy << FinalTy
1094 << Msg->getSourceRange();
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +00001095 return false;
Argyrios Kyrtzidis013a2542012-05-24 16:48:23 +00001096 }
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +00001097
1098 SourceRange ArgRange = OrigArg->getSourceRange();
Argyrios Kyrtzidis7fe103c2012-05-15 22:22:10 +00001099 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +00001100
1101 if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
1102 commit.insertBefore(ArgRange.getBegin(), "@");
1103 else
1104 commit.insertWrap("@(", ArgRange, ")");
1105
1106 return true;
1107}
Argyrios Kyrtzidis7fe103c2012-05-15 22:22:10 +00001108
1109//===----------------------------------------------------------------------===//
1110// rewriteToStringBoxedExpression.
1111//===----------------------------------------------------------------------===//
1112
1113static bool doRewriteToUTF8StringBoxedExpressionHelper(
1114 const ObjCMessageExpr *Msg,
1115 const NSAPI &NS, Commit &commit) {
1116 const Expr *Arg = Msg->getArg(0);
1117 if (Arg->isTypeDependent())
1118 return false;
1119
Argyrios Kyrtzidis07736592012-05-16 00:21:21 +00001120 ASTContext &Ctx = NS.getASTContext();
1121
Argyrios Kyrtzidis7fe103c2012-05-15 22:22:10 +00001122 const Expr *OrigArg = Arg->IgnoreImpCasts();
1123 QualType OrigTy = OrigArg->getType();
Argyrios Kyrtzidis07736592012-05-16 00:21:21 +00001124 if (OrigTy->isArrayType())
1125 OrigTy = Ctx.getArrayDecayedType(OrigTy);
Argyrios Kyrtzidis7fe103c2012-05-15 22:22:10 +00001126
1127 if (const StringLiteral *
1128 StrE = dyn_cast<StringLiteral>(OrigArg->IgnoreParens())) {
1129 commit.replaceWithInner(Msg->getSourceRange(), StrE->getSourceRange());
1130 commit.insert(StrE->getLocStart(), "@");
1131 return true;
1132 }
1133
Argyrios Kyrtzidis7fe103c2012-05-15 22:22:10 +00001134 if (const PointerType *PT = OrigTy->getAs<PointerType>()) {
1135 QualType PointeeType = PT->getPointeeType();
1136 if (Ctx.hasSameUnqualifiedType(PointeeType, Ctx.CharTy)) {
1137 SourceRange ArgRange = OrigArg->getSourceRange();
1138 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
1139
1140 if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
1141 commit.insertBefore(ArgRange.getBegin(), "@");
1142 else
1143 commit.insertWrap("@(", ArgRange, ")");
1144
1145 return true;
1146 }
1147 }
1148
1149 return false;
1150}
1151
1152static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
1153 const NSAPI &NS, Commit &commit) {
1154 Selector Sel = Msg->getSelector();
1155
1156 if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithUTF8String) ||
1157 Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCString)) {
1158 if (Msg->getNumArgs() != 1)
1159 return false;
1160 return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);
1161 }
1162
1163 if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCStringEncoding)) {
1164 if (Msg->getNumArgs() != 2)
1165 return false;
1166
1167 const Expr *encodingArg = Msg->getArg(1);
1168 if (NS.isNSUTF8StringEncodingConstant(encodingArg) ||
1169 NS.isNSASCIIStringEncodingConstant(encodingArg))
1170 return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);
1171 }
1172
1173 return false;
1174}