blob: 6f4a880b649a68f16bf5b35495a25cf7c2ac4f40 [file] [log] [blame]
Ted Kremenekf7639e12012-03-06 20:06:33 +00001//===--- RewriteObjCFoundationAPI.cpp - Foundation API Rewriter -----------===//
2//
Chandler Carruth2946cd72019-01-19 08:50:56 +00003// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
Ted Kremenekf7639e12012-03-06 20:06:33 +00006//
7//===----------------------------------------------------------------------===//
8//
9// Rewrites legacy method calls to modern syntax.
10//
11//===----------------------------------------------------------------------===//
12
13#include "clang/Edit/Rewriters.h"
Chandler Carruth3a022472012-12-04 09:13:33 +000014#include "clang/AST/ASTContext.h"
15#include "clang/AST/ExprCXX.h"
16#include "clang/AST/ExprObjC.h"
17#include "clang/AST/NSAPI.h"
Argyrios Kyrtzidis6b4f3412013-01-16 23:54:48 +000018#include "clang/AST/ParentMap.h"
Ted Kremenekf7639e12012-03-06 20:06:33 +000019#include "clang/Edit/Commit.h"
20#include "clang/Lex/Lexer.h"
Ted Kremenekf7639e12012-03-06 20:06:33 +000021
22using namespace clang;
23using namespace edit;
24
25static bool checkForLiteralCreation(const ObjCMessageExpr *Msg,
Argyrios Kyrtzidis63aebfb2012-06-06 22:23:12 +000026 IdentifierInfo *&ClassId,
27 const LangOptions &LangOpts) {
Ted Kremenekf7639e12012-03-06 20:06:33 +000028 if (!Msg || Msg->isImplicit() || !Msg->getMethodDecl())
29 return false;
30
31 const ObjCInterfaceDecl *Receiver = Msg->getReceiverInterface();
32 if (!Receiver)
33 return false;
34 ClassId = Receiver->getIdentifier();
35
36 if (Msg->getReceiverKind() == ObjCMessageExpr::Class)
37 return true;
38
Argyrios Kyrtzidis63aebfb2012-06-06 22:23:12 +000039 // When in ARC mode we also convert "[[.. alloc] init]" messages to literals,
40 // since the change from +1 to +0 will be handled fine by ARC.
41 if (LangOpts.ObjCAutoRefCount) {
42 if (Msg->getReceiverKind() == ObjCMessageExpr::Instance) {
43 if (const ObjCMessageExpr *Rec = dyn_cast<ObjCMessageExpr>(
44 Msg->getInstanceReceiver()->IgnoreParenImpCasts())) {
45 if (Rec->getMethodFamily() == OMF_alloc)
46 return true;
47 }
48 }
49 }
50
Ted Kremenekf7639e12012-03-06 20:06:33 +000051 return false;
52}
53
54//===----------------------------------------------------------------------===//
55// rewriteObjCRedundantCallWithLiteral.
56//===----------------------------------------------------------------------===//
57
58bool edit::rewriteObjCRedundantCallWithLiteral(const ObjCMessageExpr *Msg,
59 const NSAPI &NS, Commit &commit) {
Craig Topper2145bc02014-05-09 08:15:10 +000060 IdentifierInfo *II = nullptr;
Argyrios Kyrtzidis63aebfb2012-06-06 22:23:12 +000061 if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
Ted Kremenekf7639e12012-03-06 20:06:33 +000062 return false;
63 if (Msg->getNumArgs() != 1)
64 return false;
65
66 const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts();
67 Selector Sel = Msg->getSelector();
68
69 if ((isa<ObjCStringLiteral>(Arg) &&
70 NS.getNSClassId(NSAPI::ClassId_NSString) == II &&
Argyrios Kyrtzidis63aebfb2012-06-06 22:23:12 +000071 (NS.getNSStringSelector(NSAPI::NSStr_stringWithString) == Sel ||
72 NS.getNSStringSelector(NSAPI::NSStr_initWithString) == Sel)) ||
Ted Kremenekf7639e12012-03-06 20:06:33 +000073
74 (isa<ObjCArrayLiteral>(Arg) &&
75 NS.getNSClassId(NSAPI::ClassId_NSArray) == II &&
Argyrios Kyrtzidis63aebfb2012-06-06 22:23:12 +000076 (NS.getNSArraySelector(NSAPI::NSArr_arrayWithArray) == Sel ||
77 NS.getNSArraySelector(NSAPI::NSArr_initWithArray) == Sel)) ||
Ted Kremenekf7639e12012-03-06 20:06:33 +000078
79 (isa<ObjCDictionaryLiteral>(Arg) &&
80 NS.getNSClassId(NSAPI::ClassId_NSDictionary) == II &&
Argyrios Kyrtzidis63aebfb2012-06-06 22:23:12 +000081 (NS.getNSDictionarySelector(
82 NSAPI::NSDict_dictionaryWithDictionary) == Sel ||
83 NS.getNSDictionarySelector(NSAPI::NSDict_initWithDictionary) == Sel))) {
Fangrui Song6907ce22018-07-30 19:24:48 +000084
Ted Kremenekf7639e12012-03-06 20:06:33 +000085 commit.replaceWithInner(Msg->getSourceRange(),
86 Msg->getArg(0)->getSourceRange());
87 return true;
88 }
89
90 return false;
91}
92
93//===----------------------------------------------------------------------===//
94// rewriteToObjCSubscriptSyntax.
95//===----------------------------------------------------------------------===//
96
Adrian Prantl9fc8faf2018-05-09 01:00:01 +000097/// Check for classes that accept 'objectForKey:' (or the other selectors
Argyrios Kyrtzidis89b928e2012-07-06 00:07:09 +000098/// that the migrator handles) but return their instances as 'id', resulting
99/// in the compiler resolving 'objectForKey:' as the method from NSDictionary.
100///
101/// When checking if we can convert to subscripting syntax, check whether
102/// the receiver is a result of a class method from a hardcoded list of
103/// such classes. In such a case return the specific class as the interface
104/// of the receiver.
105///
106/// FIXME: Remove this when these classes start using 'instancetype'.
107static const ObjCInterfaceDecl *
108maybeAdjustInterfaceForSubscriptingCheck(const ObjCInterfaceDecl *IFace,
109 const Expr *Receiver,
110 ASTContext &Ctx) {
111 assert(IFace && Receiver);
112
113 // If the receiver has type 'id'...
114 if (!Ctx.isObjCIdType(Receiver->getType().getUnqualifiedType()))
115 return IFace;
116
117 const ObjCMessageExpr *
118 InnerMsg = dyn_cast<ObjCMessageExpr>(Receiver->IgnoreParenCasts());
119 if (!InnerMsg)
120 return IFace;
121
122 QualType ClassRec;
123 switch (InnerMsg->getReceiverKind()) {
124 case ObjCMessageExpr::Instance:
125 case ObjCMessageExpr::SuperInstance:
126 return IFace;
127
128 case ObjCMessageExpr::Class:
129 ClassRec = InnerMsg->getClassReceiver();
130 break;
131 case ObjCMessageExpr::SuperClass:
132 ClassRec = InnerMsg->getSuperType();
133 break;
134 }
135
136 if (ClassRec.isNull())
137 return IFace;
138
139 // ...and it is the result of a class message...
140
141 const ObjCObjectType *ObjTy = ClassRec->getAs<ObjCObjectType>();
142 if (!ObjTy)
143 return IFace;
144 const ObjCInterfaceDecl *OID = ObjTy->getInterface();
145
146 // ...and the receiving class is NSMapTable or NSLocale, return that
147 // class as the receiving interface.
148 if (OID->getName() == "NSMapTable" ||
149 OID->getName() == "NSLocale")
150 return OID;
151
152 return IFace;
153}
154
155static bool canRewriteToSubscriptSyntax(const ObjCInterfaceDecl *&IFace,
156 const ObjCMessageExpr *Msg,
157 ASTContext &Ctx,
Argyrios Kyrtzidis13b92922012-07-05 21:49:51 +0000158 Selector subscriptSel) {
Argyrios Kyrtzidis89b928e2012-07-06 00:07:09 +0000159 const Expr *Rec = Msg->getInstanceReceiver();
160 if (!Rec)
161 return false;
162 IFace = maybeAdjustInterfaceForSubscriptingCheck(IFace, Rec, Ctx);
163
Argyrios Kyrtzidis13b92922012-07-05 21:49:51 +0000164 if (const ObjCMethodDecl *MD = IFace->lookupInstanceMethod(subscriptSel)) {
165 if (!MD->isUnavailable())
166 return true;
167 }
168 return false;
169}
170
Argyrios Kyrtzidis0bbe94f2012-05-14 23:33:49 +0000171static bool subscriptOperatorNeedsParens(const Expr *FullExpr);
172
Ted Kremenekf7639e12012-03-06 20:06:33 +0000173static void maybePutParensOnReceiver(const Expr *Receiver, Commit &commit) {
Argyrios Kyrtzidis0bbe94f2012-05-14 23:33:49 +0000174 if (subscriptOperatorNeedsParens(Receiver)) {
Ted Kremenekf7639e12012-03-06 20:06:33 +0000175 SourceRange RecRange = Receiver->getSourceRange();
176 commit.insertWrap("(", RecRange, ")");
177 }
178}
179
Argyrios Kyrtzidis6310fdd2012-06-04 21:23:26 +0000180static bool rewriteToSubscriptGetCommon(const ObjCMessageExpr *Msg,
181 Commit &commit) {
Ted Kremenekf7639e12012-03-06 20:06:33 +0000182 if (Msg->getNumArgs() != 1)
183 return false;
184 const Expr *Rec = Msg->getInstanceReceiver();
185 if (!Rec)
186 return false;
187
188 SourceRange MsgRange = Msg->getSourceRange();
189 SourceRange RecRange = Rec->getSourceRange();
190 SourceRange ArgRange = Msg->getArg(0)->getSourceRange();
191
192 commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
193 ArgRange.getBegin()),
194 CharSourceRange::getTokenRange(RecRange));
195 commit.replaceWithInner(SourceRange(ArgRange.getBegin(), MsgRange.getEnd()),
196 ArgRange);
197 commit.insertWrap("[", ArgRange, "]");
198 maybePutParensOnReceiver(Rec, commit);
199 return true;
200}
201
Argyrios Kyrtzidis6310fdd2012-06-04 21:23:26 +0000202static bool rewriteToArraySubscriptGet(const ObjCInterfaceDecl *IFace,
203 const ObjCMessageExpr *Msg,
204 const NSAPI &NS,
205 Commit &commit) {
Argyrios Kyrtzidis89b928e2012-07-06 00:07:09 +0000206 if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
Argyrios Kyrtzidis13b92922012-07-05 21:49:51 +0000207 NS.getObjectAtIndexedSubscriptSelector()))
Argyrios Kyrtzidis6310fdd2012-06-04 21:23:26 +0000208 return false;
209 return rewriteToSubscriptGetCommon(Msg, commit);
210}
211
212static bool rewriteToDictionarySubscriptGet(const ObjCInterfaceDecl *IFace,
213 const ObjCMessageExpr *Msg,
214 const NSAPI &NS,
215 Commit &commit) {
Argyrios Kyrtzidis89b928e2012-07-06 00:07:09 +0000216 if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
Argyrios Kyrtzidis13b92922012-07-05 21:49:51 +0000217 NS.getObjectForKeyedSubscriptSelector()))
Argyrios Kyrtzidis6310fdd2012-06-04 21:23:26 +0000218 return false;
219 return rewriteToSubscriptGetCommon(Msg, commit);
220}
221
222static bool rewriteToArraySubscriptSet(const ObjCInterfaceDecl *IFace,
223 const ObjCMessageExpr *Msg,
224 const NSAPI &NS,
Ted Kremenekf7639e12012-03-06 20:06:33 +0000225 Commit &commit) {
Argyrios Kyrtzidis89b928e2012-07-06 00:07:09 +0000226 if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
Argyrios Kyrtzidis13b92922012-07-05 21:49:51 +0000227 NS.getSetObjectAtIndexedSubscriptSelector()))
228 return false;
229
Ted Kremenekf7639e12012-03-06 20:06:33 +0000230 if (Msg->getNumArgs() != 2)
231 return false;
232 const Expr *Rec = Msg->getInstanceReceiver();
233 if (!Rec)
234 return false;
235
236 SourceRange MsgRange = Msg->getSourceRange();
237 SourceRange RecRange = Rec->getSourceRange();
238 SourceRange Arg0Range = Msg->getArg(0)->getSourceRange();
239 SourceRange Arg1Range = Msg->getArg(1)->getSourceRange();
240
241 commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
242 Arg0Range.getBegin()),
243 CharSourceRange::getTokenRange(RecRange));
244 commit.replaceWithInner(CharSourceRange::getCharRange(Arg0Range.getBegin(),
245 Arg1Range.getBegin()),
246 CharSourceRange::getTokenRange(Arg0Range));
247 commit.replaceWithInner(SourceRange(Arg1Range.getBegin(), MsgRange.getEnd()),
248 Arg1Range);
249 commit.insertWrap("[", CharSourceRange::getCharRange(Arg0Range.getBegin(),
250 Arg1Range.getBegin()),
251 "] = ");
252 maybePutParensOnReceiver(Rec, commit);
253 return true;
254}
255
Argyrios Kyrtzidis6310fdd2012-06-04 21:23:26 +0000256static bool rewriteToDictionarySubscriptSet(const ObjCInterfaceDecl *IFace,
257 const ObjCMessageExpr *Msg,
258 const NSAPI &NS,
Ted Kremenekf7639e12012-03-06 20:06:33 +0000259 Commit &commit) {
Argyrios Kyrtzidis89b928e2012-07-06 00:07:09 +0000260 if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
Argyrios Kyrtzidis13b92922012-07-05 21:49:51 +0000261 NS.getSetObjectForKeyedSubscriptSelector()))
262 return false;
263
Ted Kremenekf7639e12012-03-06 20:06:33 +0000264 if (Msg->getNumArgs() != 2)
265 return false;
266 const Expr *Rec = Msg->getInstanceReceiver();
267 if (!Rec)
268 return false;
269
270 SourceRange MsgRange = Msg->getSourceRange();
271 SourceRange RecRange = Rec->getSourceRange();
272 SourceRange Arg0Range = Msg->getArg(0)->getSourceRange();
273 SourceRange Arg1Range = Msg->getArg(1)->getSourceRange();
274
275 SourceLocation LocBeforeVal = Arg0Range.getBegin();
276 commit.insertBefore(LocBeforeVal, "] = ");
277 commit.insertFromRange(LocBeforeVal, Arg1Range, /*afterToken=*/false,
278 /*beforePreviousInsertions=*/true);
279 commit.insertBefore(LocBeforeVal, "[");
280 commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
281 Arg0Range.getBegin()),
282 CharSourceRange::getTokenRange(RecRange));
283 commit.replaceWithInner(SourceRange(Arg0Range.getBegin(), MsgRange.getEnd()),
284 Arg0Range);
285 maybePutParensOnReceiver(Rec, commit);
286 return true;
287}
288
289bool edit::rewriteToObjCSubscriptSyntax(const ObjCMessageExpr *Msg,
Argyrios Kyrtzidis6310fdd2012-06-04 21:23:26 +0000290 const NSAPI &NS, Commit &commit) {
Ted Kremenekf7639e12012-03-06 20:06:33 +0000291 if (!Msg || Msg->isImplicit() ||
292 Msg->getReceiverKind() != ObjCMessageExpr::Instance)
293 return false;
294 const ObjCMethodDecl *Method = Msg->getMethodDecl();
295 if (!Method)
296 return false;
297
Dmitri Gribenko60e92e52013-02-14 13:49:48 +0000298 const ObjCInterfaceDecl *IFace =
299 NS.getASTContext().getObjContainingInterface(Method);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000300 if (!IFace)
301 return false;
Ted Kremenekf7639e12012-03-06 20:06:33 +0000302 Selector Sel = Msg->getSelector();
303
Argyrios Kyrtzidis13b92922012-07-05 21:49:51 +0000304 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_objectAtIndex))
Argyrios Kyrtzidis6310fdd2012-06-04 21:23:26 +0000305 return rewriteToArraySubscriptGet(IFace, Msg, NS, commit);
306
Argyrios Kyrtzidis13b92922012-07-05 21:49:51 +0000307 if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_objectForKey))
Argyrios Kyrtzidis6310fdd2012-06-04 21:23:26 +0000308 return rewriteToDictionarySubscriptGet(IFace, Msg, NS, commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000309
310 if (Msg->getNumArgs() != 2)
311 return false;
312
Argyrios Kyrtzidis13b92922012-07-05 21:49:51 +0000313 if (Sel == NS.getNSArraySelector(NSAPI::NSMutableArr_replaceObjectAtIndex))
Argyrios Kyrtzidis6310fdd2012-06-04 21:23:26 +0000314 return rewriteToArraySubscriptSet(IFace, Msg, NS, commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000315
Argyrios Kyrtzidis13b92922012-07-05 21:49:51 +0000316 if (Sel == NS.getNSDictionarySelector(NSAPI::NSMutableDict_setObjectForKey))
Argyrios Kyrtzidis6310fdd2012-06-04 21:23:26 +0000317 return rewriteToDictionarySubscriptSet(IFace, Msg, NS, commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000318
319 return false;
320}
321
322//===----------------------------------------------------------------------===//
323// rewriteToObjCLiteralSyntax.
324//===----------------------------------------------------------------------===//
325
326static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
Argyrios Kyrtzidis6b4f3412013-01-16 23:54:48 +0000327 const NSAPI &NS, Commit &commit,
328 const ParentMap *PMap);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000329static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
330 const NSAPI &NS, Commit &commit);
331static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
332 const NSAPI &NS, Commit &commit);
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000333static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
334 const NSAPI &NS, Commit &commit);
Argyrios Kyrtzidis7bd957c2012-05-15 22:22:10 +0000335static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
336 const NSAPI &NS, Commit &commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000337
338bool edit::rewriteToObjCLiteralSyntax(const ObjCMessageExpr *Msg,
Argyrios Kyrtzidis6b4f3412013-01-16 23:54:48 +0000339 const NSAPI &NS, Commit &commit,
340 const ParentMap *PMap) {
Craig Topper2145bc02014-05-09 08:15:10 +0000341 IdentifierInfo *II = nullptr;
Argyrios Kyrtzidis63aebfb2012-06-06 22:23:12 +0000342 if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
Ted Kremenekf7639e12012-03-06 20:06:33 +0000343 return false;
344
345 if (II == NS.getNSClassId(NSAPI::ClassId_NSArray))
Argyrios Kyrtzidis6b4f3412013-01-16 23:54:48 +0000346 return rewriteToArrayLiteral(Msg, NS, commit, PMap);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000347 if (II == NS.getNSClassId(NSAPI::ClassId_NSDictionary))
348 return rewriteToDictionaryLiteral(Msg, NS, commit);
349 if (II == NS.getNSClassId(NSAPI::ClassId_NSNumber))
350 return rewriteToNumberLiteral(Msg, NS, commit);
Argyrios Kyrtzidis7bd957c2012-05-15 22:22:10 +0000351 if (II == NS.getNSClassId(NSAPI::ClassId_NSString))
352 return rewriteToStringBoxedExpression(Msg, NS, commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000353
354 return false;
355}
356
Adrian Prantl9fc8faf2018-05-09 01:00:01 +0000357/// Returns true if the immediate message arguments of \c Msg should not
Argyrios Kyrtzidis6b4f3412013-01-16 23:54:48 +0000358/// be rewritten because it will interfere with the rewrite of the parent
359/// message expression. e.g.
360/// \code
361/// [NSDictionary dictionaryWithObjects:
362/// [NSArray arrayWithObjects:@"1", @"2", nil]
363/// forKeys:[NSArray arrayWithObjects:@"A", @"B", nil]];
364/// \endcode
365/// It will return true for this because we are going to rewrite this directly
366/// to a dictionary literal without any array literals.
367static bool shouldNotRewriteImmediateMessageArgs(const ObjCMessageExpr *Msg,
368 const NSAPI &NS);
369
Ted Kremenekf7639e12012-03-06 20:06:33 +0000370//===----------------------------------------------------------------------===//
371// rewriteToArrayLiteral.
372//===----------------------------------------------------------------------===//
373
Adrian Prantl9fc8faf2018-05-09 01:00:01 +0000374/// Adds an explicit cast to 'id' if the type is not objc object.
Argyrios Kyrtzidisc1dfed62012-05-14 22:01:53 +0000375static void objectifyExpr(const Expr *E, Commit &commit);
376
Ted Kremenekf7639e12012-03-06 20:06:33 +0000377static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
Argyrios Kyrtzidis6b4f3412013-01-16 23:54:48 +0000378 const NSAPI &NS, Commit &commit,
379 const ParentMap *PMap) {
380 if (PMap) {
381 const ObjCMessageExpr *ParentMsg =
382 dyn_cast_or_null<ObjCMessageExpr>(PMap->getParentIgnoreParenCasts(Msg));
383 if (shouldNotRewriteImmediateMessageArgs(ParentMsg, NS))
384 return false;
385 }
386
Ted Kremenekf7639e12012-03-06 20:06:33 +0000387 Selector Sel = Msg->getSelector();
388 SourceRange MsgRange = Msg->getSourceRange();
389
390 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array)) {
391 if (Msg->getNumArgs() != 0)
392 return false;
393 commit.replace(MsgRange, "@[]");
394 return true;
395 }
396
397 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) {
398 if (Msg->getNumArgs() != 1)
399 return false;
Argyrios Kyrtzidisc1dfed62012-05-14 22:01:53 +0000400 objectifyExpr(Msg->getArg(0), commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000401 SourceRange ArgRange = Msg->getArg(0)->getSourceRange();
402 commit.replaceWithInner(MsgRange, ArgRange);
403 commit.insertWrap("@[", ArgRange, "]");
404 return true;
405 }
406
Argyrios Kyrtzidis63aebfb2012-06-06 22:23:12 +0000407 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) ||
408 Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) {
Ted Kremenekf7639e12012-03-06 20:06:33 +0000409 if (Msg->getNumArgs() == 0)
410 return false;
411 const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1);
412 if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
413 return false;
414
Argyrios Kyrtzidisc1dfed62012-05-14 22:01:53 +0000415 for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i)
416 objectifyExpr(Msg->getArg(i), commit);
417
Ted Kremenekf7639e12012-03-06 20:06:33 +0000418 if (Msg->getNumArgs() == 1) {
419 commit.replace(MsgRange, "@[]");
420 return true;
421 }
Stephen Kellyf2ceec42018-08-09 21:08:08 +0000422 SourceRange ArgRange(Msg->getArg(0)->getBeginLoc(),
Stephen Kelly1c301dc2018-08-09 21:09:38 +0000423 Msg->getArg(Msg->getNumArgs() - 2)->getEndLoc());
Ted Kremenekf7639e12012-03-06 20:06:33 +0000424 commit.replaceWithInner(MsgRange, ArgRange);
425 commit.insertWrap("@[", ArgRange, "]");
426 return true;
427 }
428
429 return false;
430}
431
432//===----------------------------------------------------------------------===//
433// rewriteToDictionaryLiteral.
434//===----------------------------------------------------------------------===//
435
Adrian Prantl9fc8faf2018-05-09 01:00:01 +0000436/// If \c Msg is an NSArray creation message or literal, this gets the
Argyrios Kyrtzidis6b4f3412013-01-16 23:54:48 +0000437/// objects that were used to create it.
438/// \returns true if it is an NSArray and we got objects, or false otherwise.
439static bool getNSArrayObjects(const Expr *E, const NSAPI &NS,
440 SmallVectorImpl<const Expr *> &Objs) {
441 if (!E)
442 return false;
443
444 E = E->IgnoreParenCasts();
445 if (!E)
446 return false;
447
448 if (const ObjCMessageExpr *Msg = dyn_cast<ObjCMessageExpr>(E)) {
Craig Topper2145bc02014-05-09 08:15:10 +0000449 IdentifierInfo *Cls = nullptr;
Argyrios Kyrtzidis6b4f3412013-01-16 23:54:48 +0000450 if (!checkForLiteralCreation(Msg, Cls, NS.getASTContext().getLangOpts()))
451 return false;
452
453 if (Cls != NS.getNSClassId(NSAPI::ClassId_NSArray))
454 return false;
455
456 Selector Sel = Msg->getSelector();
457 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array))
458 return true; // empty array.
459
460 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) {
461 if (Msg->getNumArgs() != 1)
462 return false;
463 Objs.push_back(Msg->getArg(0));
464 return true;
465 }
466
467 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) ||
468 Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) {
469 if (Msg->getNumArgs() == 0)
470 return false;
471 const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1);
472 if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
473 return false;
474
475 for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i)
476 Objs.push_back(Msg->getArg(i));
477 return true;
478 }
479
480 } else if (const ObjCArrayLiteral *ArrLit = dyn_cast<ObjCArrayLiteral>(E)) {
481 for (unsigned i = 0, e = ArrLit->getNumElements(); i != e; ++i)
482 Objs.push_back(ArrLit->getElement(i));
483 return true;
484 }
485
486 return false;
487}
488
Ted Kremenekf7639e12012-03-06 20:06:33 +0000489static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
490 const NSAPI &NS, Commit &commit) {
491 Selector Sel = Msg->getSelector();
492 SourceRange MsgRange = Msg->getSourceRange();
493
494 if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_dictionary)) {
495 if (Msg->getNumArgs() != 0)
496 return false;
497 commit.replace(MsgRange, "@{}");
498 return true;
499 }
500
501 if (Sel == NS.getNSDictionarySelector(
502 NSAPI::NSDict_dictionaryWithObjectForKey)) {
503 if (Msg->getNumArgs() != 2)
504 return false;
Argyrios Kyrtzidisc1dfed62012-05-14 22:01:53 +0000505
506 objectifyExpr(Msg->getArg(0), commit);
507 objectifyExpr(Msg->getArg(1), commit);
508
Ted Kremenekf7639e12012-03-06 20:06:33 +0000509 SourceRange ValRange = Msg->getArg(0)->getSourceRange();
510 SourceRange KeyRange = Msg->getArg(1)->getSourceRange();
511 // Insert key before the value.
512 commit.insertBefore(ValRange.getBegin(), ": ");
513 commit.insertFromRange(ValRange.getBegin(),
514 CharSourceRange::getTokenRange(KeyRange),
515 /*afterToken=*/false, /*beforePreviousInsertions=*/true);
516 commit.insertBefore(ValRange.getBegin(), "@{");
517 commit.insertAfterToken(ValRange.getEnd(), "}");
518 commit.replaceWithInner(MsgRange, ValRange);
519 return true;
520 }
521
522 if (Sel == NS.getNSDictionarySelector(
Argyrios Kyrtzidis63aebfb2012-06-06 22:23:12 +0000523 NSAPI::NSDict_dictionaryWithObjectsAndKeys) ||
524 Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsAndKeys)) {
Ted Kremenekf7639e12012-03-06 20:06:33 +0000525 if (Msg->getNumArgs() % 2 != 1)
526 return false;
527 unsigned SentinelIdx = Msg->getNumArgs() - 1;
528 const Expr *SentinelExpr = Msg->getArg(SentinelIdx);
529 if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
530 return false;
531
532 if (Msg->getNumArgs() == 1) {
533 commit.replace(MsgRange, "@{}");
534 return true;
535 }
536
537 for (unsigned i = 0; i < SentinelIdx; i += 2) {
Argyrios Kyrtzidisc1dfed62012-05-14 22:01:53 +0000538 objectifyExpr(Msg->getArg(i), commit);
539 objectifyExpr(Msg->getArg(i+1), commit);
540
Ted Kremenekf7639e12012-03-06 20:06:33 +0000541 SourceRange ValRange = Msg->getArg(i)->getSourceRange();
542 SourceRange KeyRange = Msg->getArg(i+1)->getSourceRange();
543 // Insert value after key.
544 commit.insertAfterToken(KeyRange.getEnd(), ": ");
545 commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true);
546 commit.remove(CharSourceRange::getCharRange(ValRange.getBegin(),
547 KeyRange.getBegin()));
548 }
549 // Range of arguments up until and including the last key.
550 // The sentinel and first value are cut off, the value will move after the
551 // key.
Stephen Kellyf2ceec42018-08-09 21:08:08 +0000552 SourceRange ArgRange(Msg->getArg(1)->getBeginLoc(),
Stephen Kelly1c301dc2018-08-09 21:09:38 +0000553 Msg->getArg(SentinelIdx - 1)->getEndLoc());
Ted Kremenekf7639e12012-03-06 20:06:33 +0000554 commit.insertWrap("@{", ArgRange, "}");
555 commit.replaceWithInner(MsgRange, ArgRange);
556 return true;
557 }
558
Argyrios Kyrtzidis6b4f3412013-01-16 23:54:48 +0000559 if (Sel == NS.getNSDictionarySelector(
560 NSAPI::NSDict_dictionaryWithObjectsForKeys) ||
561 Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsForKeys)) {
562 if (Msg->getNumArgs() != 2)
563 return false;
564
565 SmallVector<const Expr *, 8> Vals;
566 if (!getNSArrayObjects(Msg->getArg(0), NS, Vals))
567 return false;
568
569 SmallVector<const Expr *, 8> Keys;
570 if (!getNSArrayObjects(Msg->getArg(1), NS, Keys))
571 return false;
572
573 if (Vals.size() != Keys.size())
574 return false;
575
576 if (Vals.empty()) {
577 commit.replace(MsgRange, "@{}");
578 return true;
579 }
580
581 for (unsigned i = 0, n = Vals.size(); i < n; ++i) {
582 objectifyExpr(Vals[i], commit);
583 objectifyExpr(Keys[i], commit);
584
585 SourceRange ValRange = Vals[i]->getSourceRange();
586 SourceRange KeyRange = Keys[i]->getSourceRange();
587 // Insert value after key.
588 commit.insertAfterToken(KeyRange.getEnd(), ": ");
589 commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true);
590 }
591 // Range of arguments up until and including the last key.
592 // The first value is cut off, the value will move after the key.
Stephen Kelly1c301dc2018-08-09 21:09:38 +0000593 SourceRange ArgRange(Keys.front()->getBeginLoc(), Keys.back()->getEndLoc());
Argyrios Kyrtzidis6b4f3412013-01-16 23:54:48 +0000594 commit.insertWrap("@{", ArgRange, "}");
595 commit.replaceWithInner(MsgRange, ArgRange);
596 return true;
597 }
598
599 return false;
600}
601
602static bool shouldNotRewriteImmediateMessageArgs(const ObjCMessageExpr *Msg,
603 const NSAPI &NS) {
604 if (!Msg)
605 return false;
606
Craig Topper2145bc02014-05-09 08:15:10 +0000607 IdentifierInfo *II = nullptr;
Argyrios Kyrtzidis6b4f3412013-01-16 23:54:48 +0000608 if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
609 return false;
610
611 if (II != NS.getNSClassId(NSAPI::ClassId_NSDictionary))
612 return false;
613
614 Selector Sel = Msg->getSelector();
615 if (Sel == NS.getNSDictionarySelector(
616 NSAPI::NSDict_dictionaryWithObjectsForKeys) ||
617 Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsForKeys)) {
618 if (Msg->getNumArgs() != 2)
619 return false;
620
621 SmallVector<const Expr *, 8> Vals;
622 if (!getNSArrayObjects(Msg->getArg(0), NS, Vals))
623 return false;
624
625 SmallVector<const Expr *, 8> Keys;
626 if (!getNSArrayObjects(Msg->getArg(1), NS, Keys))
627 return false;
628
629 if (Vals.size() != Keys.size())
630 return false;
631
632 return true;
633 }
634
Ted Kremenekf7639e12012-03-06 20:06:33 +0000635 return false;
636}
637
638//===----------------------------------------------------------------------===//
639// rewriteToNumberLiteral.
640//===----------------------------------------------------------------------===//
641
642static bool rewriteToCharLiteral(const ObjCMessageExpr *Msg,
643 const CharacterLiteral *Arg,
644 const NSAPI &NS, Commit &commit) {
645 if (Arg->getKind() != CharacterLiteral::Ascii)
646 return false;
647 if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithChar,
648 Msg->getSelector())) {
649 SourceRange ArgRange = Arg->getSourceRange();
650 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
651 commit.insert(ArgRange.getBegin(), "@");
652 return true;
653 }
654
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000655 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000656}
657
658static bool rewriteToBoolLiteral(const ObjCMessageExpr *Msg,
659 const Expr *Arg,
660 const NSAPI &NS, Commit &commit) {
661 if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithBool,
662 Msg->getSelector())) {
663 SourceRange ArgRange = Arg->getSourceRange();
664 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
665 commit.insert(ArgRange.getBegin(), "@");
666 return true;
667 }
668
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000669 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000670}
671
672namespace {
673
674struct LiteralInfo {
675 bool Hex, Octal;
676 StringRef U, F, L, LL;
677 CharSourceRange WithoutSuffRange;
678};
679
680}
681
682static bool getLiteralInfo(SourceRange literalRange,
683 bool isFloat, bool isIntZero,
684 ASTContext &Ctx, LiteralInfo &Info) {
685 if (literalRange.getBegin().isMacroID() ||
686 literalRange.getEnd().isMacroID())
687 return false;
688 StringRef text = Lexer::getSourceText(
689 CharSourceRange::getTokenRange(literalRange),
David Blaikiebbafb8a2012-03-11 07:00:24 +0000690 Ctx.getSourceManager(), Ctx.getLangOpts());
Ted Kremenekf7639e12012-03-06 20:06:33 +0000691 if (text.empty())
692 return false;
693
David Blaikie05785d12013-02-20 22:23:23 +0000694 Optional<bool> UpperU, UpperL;
Ted Kremenekf7639e12012-03-06 20:06:33 +0000695 bool UpperF = false;
696
697 struct Suff {
698 static bool has(StringRef suff, StringRef &text) {
699 if (text.endswith(suff)) {
700 text = text.substr(0, text.size()-suff.size());
701 return true;
702 }
703 return false;
704 }
705 };
706
707 while (1) {
708 if (Suff::has("u", text)) {
709 UpperU = false;
710 } else if (Suff::has("U", text)) {
711 UpperU = true;
712 } else if (Suff::has("ll", text)) {
713 UpperL = false;
714 } else if (Suff::has("LL", text)) {
715 UpperL = true;
716 } else if (Suff::has("l", text)) {
717 UpperL = false;
718 } else if (Suff::has("L", text)) {
719 UpperL = true;
720 } else if (isFloat && Suff::has("f", text)) {
721 UpperF = false;
722 } else if (isFloat && Suff::has("F", text)) {
723 UpperF = true;
724 } else
725 break;
726 }
Fangrui Song6907ce22018-07-30 19:24:48 +0000727
Ted Kremenekf7639e12012-03-06 20:06:33 +0000728 if (!UpperU.hasValue() && !UpperL.hasValue())
729 UpperU = UpperL = true;
730 else if (UpperU.hasValue() && !UpperL.hasValue())
731 UpperL = UpperU;
732 else if (UpperL.hasValue() && !UpperU.hasValue())
733 UpperU = UpperL;
734
735 Info.U = *UpperU ? "U" : "u";
736 Info.L = *UpperL ? "L" : "l";
737 Info.LL = *UpperL ? "LL" : "ll";
738 Info.F = UpperF ? "F" : "f";
Fangrui Song6907ce22018-07-30 19:24:48 +0000739
Ted Kremenekf7639e12012-03-06 20:06:33 +0000740 Info.Hex = Info.Octal = false;
741 if (text.startswith("0x"))
742 Info.Hex = true;
743 else if (!isFloat && !isIntZero && text.startswith("0"))
744 Info.Octal = true;
745
746 SourceLocation B = literalRange.getBegin();
747 Info.WithoutSuffRange =
748 CharSourceRange::getCharRange(B, B.getLocWithOffset(text.size()));
749 return true;
750}
751
752static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
753 const NSAPI &NS, Commit &commit) {
754 if (Msg->getNumArgs() != 1)
755 return false;
756
757 const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts();
758 if (const CharacterLiteral *CharE = dyn_cast<CharacterLiteral>(Arg))
759 return rewriteToCharLiteral(Msg, CharE, NS, commit);
760 if (const ObjCBoolLiteralExpr *BE = dyn_cast<ObjCBoolLiteralExpr>(Arg))
761 return rewriteToBoolLiteral(Msg, BE, NS, commit);
762 if (const CXXBoolLiteralExpr *BE = dyn_cast<CXXBoolLiteralExpr>(Arg))
763 return rewriteToBoolLiteral(Msg, BE, NS, commit);
764
765 const Expr *literalE = Arg;
766 if (const UnaryOperator *UOE = dyn_cast<UnaryOperator>(literalE)) {
767 if (UOE->getOpcode() == UO_Plus || UOE->getOpcode() == UO_Minus)
768 literalE = UOE->getSubExpr();
769 }
770
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000771 // Only integer and floating literals, otherwise try to rewrite to boxed
772 // expression.
Ted Kremenekf7639e12012-03-06 20:06:33 +0000773 if (!isa<IntegerLiteral>(literalE) && !isa<FloatingLiteral>(literalE))
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000774 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000775
776 ASTContext &Ctx = NS.getASTContext();
777 Selector Sel = Msg->getSelector();
David Blaikie05785d12013-02-20 22:23:23 +0000778 Optional<NSAPI::NSNumberLiteralMethodKind>
Ted Kremenekf7639e12012-03-06 20:06:33 +0000779 MKOpt = NS.getNSNumberLiteralMethodKind(Sel);
780 if (!MKOpt)
781 return false;
782 NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;
783
Benjamin Kramerece209a2012-03-13 17:05:43 +0000784 bool CallIsUnsigned = false, CallIsLong = false, CallIsLongLong = false;
Ted Kremenekf7639e12012-03-06 20:06:33 +0000785 bool CallIsFloating = false, CallIsDouble = false;
786
787 switch (MK) {
788 // We cannot have these calls with int/float literals.
789 case NSAPI::NSNumberWithChar:
790 case NSAPI::NSNumberWithUnsignedChar:
791 case NSAPI::NSNumberWithShort:
792 case NSAPI::NSNumberWithUnsignedShort:
793 case NSAPI::NSNumberWithBool:
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000794 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000795
796 case NSAPI::NSNumberWithUnsignedInt:
797 case NSAPI::NSNumberWithUnsignedInteger:
798 CallIsUnsigned = true;
Galina Kistanovaefa71242017-06-03 06:30:08 +0000799 LLVM_FALLTHROUGH;
Ted Kremenekf7639e12012-03-06 20:06:33 +0000800 case NSAPI::NSNumberWithInt:
801 case NSAPI::NSNumberWithInteger:
Ted Kremenekf7639e12012-03-06 20:06:33 +0000802 break;
803
804 case NSAPI::NSNumberWithUnsignedLong:
805 CallIsUnsigned = true;
Galina Kistanovaefa71242017-06-03 06:30:08 +0000806 LLVM_FALLTHROUGH;
Ted Kremenekf7639e12012-03-06 20:06:33 +0000807 case NSAPI::NSNumberWithLong:
Benjamin Kramerece209a2012-03-13 17:05:43 +0000808 CallIsLong = true;
Ted Kremenekf7639e12012-03-06 20:06:33 +0000809 break;
810
811 case NSAPI::NSNumberWithUnsignedLongLong:
812 CallIsUnsigned = true;
Galina Kistanovaefa71242017-06-03 06:30:08 +0000813 LLVM_FALLTHROUGH;
Ted Kremenekf7639e12012-03-06 20:06:33 +0000814 case NSAPI::NSNumberWithLongLong:
Benjamin Kramerece209a2012-03-13 17:05:43 +0000815 CallIsLongLong = true;
Ted Kremenekf7639e12012-03-06 20:06:33 +0000816 break;
817
818 case NSAPI::NSNumberWithDouble:
819 CallIsDouble = true;
Galina Kistanovaefa71242017-06-03 06:30:08 +0000820 LLVM_FALLTHROUGH;
Ted Kremenekf7639e12012-03-06 20:06:33 +0000821 case NSAPI::NSNumberWithFloat:
822 CallIsFloating = true;
823 break;
824 }
825
826 SourceRange ArgRange = Arg->getSourceRange();
827 QualType ArgTy = Arg->getType();
828 QualType CallTy = Msg->getArg(0)->getType();
829
830 // Check for the easy case, the literal maps directly to the call.
831 if (Ctx.hasSameType(ArgTy, CallTy)) {
832 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
833 commit.insert(ArgRange.getBegin(), "@");
834 return true;
835 }
836
837 // We will need to modify the literal suffix to get the same type as the call.
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000838 // Try with boxed expression if it came from a macro.
Ted Kremenekf7639e12012-03-06 20:06:33 +0000839 if (ArgRange.getBegin().isMacroID())
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000840 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000841
842 bool LitIsFloat = ArgTy->isFloatingType();
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000843 // For a float passed to integer call, don't try rewriting to objc literal.
844 // It is difficult and a very uncommon case anyway.
845 // But try with boxed expression.
Ted Kremenekf7639e12012-03-06 20:06:33 +0000846 if (LitIsFloat && !CallIsFloating)
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000847 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000848
849 // Try to modify the literal make it the same type as the method call.
850 // -Modify the suffix, and/or
851 // -Change integer to float
Fangrui Song6907ce22018-07-30 19:24:48 +0000852
Ted Kremenekf7639e12012-03-06 20:06:33 +0000853 LiteralInfo LitInfo;
854 bool isIntZero = false;
855 if (const IntegerLiteral *IntE = dyn_cast<IntegerLiteral>(literalE))
856 isIntZero = !IntE->getValue().getBoolValue();
857 if (!getLiteralInfo(ArgRange, LitIsFloat, isIntZero, Ctx, LitInfo))
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000858 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000859
860 // Not easy to do int -> float with hex/octal and uncommon anyway.
861 if (!LitIsFloat && CallIsFloating && (LitInfo.Hex || LitInfo.Octal))
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000862 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Fangrui Song6907ce22018-07-30 19:24:48 +0000863
Ted Kremenekf7639e12012-03-06 20:06:33 +0000864 SourceLocation LitB = LitInfo.WithoutSuffRange.getBegin();
865 SourceLocation LitE = LitInfo.WithoutSuffRange.getEnd();
866
867 commit.replaceWithInner(CharSourceRange::getTokenRange(Msg->getSourceRange()),
868 LitInfo.WithoutSuffRange);
869 commit.insert(LitB, "@");
870
871 if (!LitIsFloat && CallIsFloating)
872 commit.insert(LitE, ".0");
873
874 if (CallIsFloating) {
875 if (!CallIsDouble)
876 commit.insert(LitE, LitInfo.F);
877 } else {
878 if (CallIsUnsigned)
879 commit.insert(LitE, LitInfo.U);
Fangrui Song6907ce22018-07-30 19:24:48 +0000880
Ted Kremenekf7639e12012-03-06 20:06:33 +0000881 if (CallIsLong)
882 commit.insert(LitE, LitInfo.L);
883 else if (CallIsLongLong)
884 commit.insert(LitE, LitInfo.LL);
885 }
886 return true;
887}
Argyrios Kyrtzidisc1dfed62012-05-14 22:01:53 +0000888
Argyrios Kyrtzidis0bbe94f2012-05-14 23:33:49 +0000889// FIXME: Make determination of operator precedence more general and
890// make it broadly available.
891static bool subscriptOperatorNeedsParens(const Expr *FullExpr) {
892 const Expr* Expr = FullExpr->IgnoreImpCasts();
893 if (isa<ArraySubscriptExpr>(Expr) ||
894 isa<CallExpr>(Expr) ||
895 isa<DeclRefExpr>(Expr) ||
896 isa<CXXNamedCastExpr>(Expr) ||
897 isa<CXXConstructExpr>(Expr) ||
898 isa<CXXThisExpr>(Expr) ||
899 isa<CXXTypeidExpr>(Expr) ||
900 isa<CXXUnresolvedConstructExpr>(Expr) ||
901 isa<ObjCMessageExpr>(Expr) ||
902 isa<ObjCPropertyRefExpr>(Expr) ||
903 isa<ObjCProtocolExpr>(Expr) ||
904 isa<MemberExpr>(Expr) ||
Argyrios Kyrtzidis94442982012-05-22 00:47:53 +0000905 isa<ObjCIvarRefExpr>(Expr) ||
Argyrios Kyrtzidis0bbe94f2012-05-14 23:33:49 +0000906 isa<ParenExpr>(FullExpr) ||
907 isa<ParenListExpr>(Expr) ||
908 isa<SizeOfPackExpr>(Expr))
909 return false;
910
911 return true;
912}
Argyrios Kyrtzidisc1dfed62012-05-14 22:01:53 +0000913static bool castOperatorNeedsParens(const Expr *FullExpr) {
914 const Expr* Expr = FullExpr->IgnoreImpCasts();
915 if (isa<ArraySubscriptExpr>(Expr) ||
916 isa<CallExpr>(Expr) ||
917 isa<DeclRefExpr>(Expr) ||
918 isa<CastExpr>(Expr) ||
919 isa<CXXNewExpr>(Expr) ||
920 isa<CXXConstructExpr>(Expr) ||
921 isa<CXXDeleteExpr>(Expr) ||
922 isa<CXXNoexceptExpr>(Expr) ||
923 isa<CXXPseudoDestructorExpr>(Expr) ||
924 isa<CXXScalarValueInitExpr>(Expr) ||
925 isa<CXXThisExpr>(Expr) ||
926 isa<CXXTypeidExpr>(Expr) ||
927 isa<CXXUnresolvedConstructExpr>(Expr) ||
928 isa<ObjCMessageExpr>(Expr) ||
929 isa<ObjCPropertyRefExpr>(Expr) ||
930 isa<ObjCProtocolExpr>(Expr) ||
931 isa<MemberExpr>(Expr) ||
Argyrios Kyrtzidis94442982012-05-22 00:47:53 +0000932 isa<ObjCIvarRefExpr>(Expr) ||
Argyrios Kyrtzidisc1dfed62012-05-14 22:01:53 +0000933 isa<ParenExpr>(FullExpr) ||
934 isa<ParenListExpr>(Expr) ||
935 isa<SizeOfPackExpr>(Expr) ||
936 isa<UnaryOperator>(Expr))
937 return false;
938
939 return true;
940}
941
942static void objectifyExpr(const Expr *E, Commit &commit) {
943 if (!E) return;
944
945 QualType T = E->getType();
946 if (T->isObjCObjectPointerType()) {
947 if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) {
948 if (ICE->getCastKind() != CK_CPointerToObjCPointerCast)
949 return;
950 } else {
951 return;
952 }
953 } else if (!T->isPointerType()) {
954 return;
955 }
956
957 SourceRange Range = E->getSourceRange();
958 if (castOperatorNeedsParens(E))
959 commit.insertWrap("(", Range, ")");
960 commit.insertBefore(Range.getBegin(), "(id)");
961}
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000962
963//===----------------------------------------------------------------------===//
964// rewriteToNumericBoxedExpression.
965//===----------------------------------------------------------------------===//
966
Argyrios Kyrtzidiscebe7222012-05-15 22:59:54 +0000967static bool isEnumConstant(const Expr *E) {
968 if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts()))
969 if (const ValueDecl *VD = DRE->getDecl())
970 return isa<EnumConstantDecl>(VD);
971
972 return false;
973}
974
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000975static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
976 const NSAPI &NS, Commit &commit) {
977 if (Msg->getNumArgs() != 1)
978 return false;
979
980 const Expr *Arg = Msg->getArg(0);
981 if (Arg->isTypeDependent())
982 return false;
983
984 ASTContext &Ctx = NS.getASTContext();
985 Selector Sel = Msg->getSelector();
David Blaikie05785d12013-02-20 22:23:23 +0000986 Optional<NSAPI::NSNumberLiteralMethodKind>
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000987 MKOpt = NS.getNSNumberLiteralMethodKind(Sel);
988 if (!MKOpt)
989 return false;
990 NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;
991
992 const Expr *OrigArg = Arg->IgnoreImpCasts();
993 QualType FinalTy = Arg->getType();
994 QualType OrigTy = OrigArg->getType();
995 uint64_t FinalTySize = Ctx.getTypeSize(FinalTy);
996 uint64_t OrigTySize = Ctx.getTypeSize(OrigTy);
997
Fangrui Song6907ce22018-07-30 19:24:48 +0000998 bool isTruncated = FinalTySize < OrigTySize;
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000999 bool needsCast = false;
1000
1001 if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) {
1002 switch (ICE->getCastKind()) {
1003 case CK_LValueToRValue:
1004 case CK_NoOp:
1005 case CK_UserDefinedConversion:
1006 break;
1007
1008 case CK_IntegralCast: {
1009 if (MK == NSAPI::NSNumberWithBool && OrigTy->isBooleanType())
1010 break;
1011 // Be more liberal with Integer/UnsignedInteger which are very commonly
1012 // used.
1013 if ((MK == NSAPI::NSNumberWithInteger ||
1014 MK == NSAPI::NSNumberWithUnsignedInteger) &&
1015 !isTruncated) {
Argyrios Kyrtzidiscebe7222012-05-15 22:59:54 +00001016 if (OrigTy->getAs<EnumType>() || isEnumConstant(OrigArg))
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +00001017 break;
1018 if ((MK==NSAPI::NSNumberWithInteger) == OrigTy->isSignedIntegerType() &&
1019 OrigTySize >= Ctx.getTypeSize(Ctx.IntTy))
1020 break;
1021 }
1022
1023 needsCast = true;
1024 break;
1025 }
1026
1027 case CK_PointerToBoolean:
1028 case CK_IntegralToBoolean:
1029 case CK_IntegralToFloating:
1030 case CK_FloatingToIntegral:
1031 case CK_FloatingToBoolean:
1032 case CK_FloatingCast:
1033 case CK_FloatingComplexToReal:
1034 case CK_FloatingComplexToBoolean:
1035 case CK_IntegralComplexToReal:
1036 case CK_IntegralComplexToBoolean:
1037 case CK_AtomicToNonAtomic:
David Tweede1468322013-12-11 13:39:46 +00001038 case CK_AddressSpaceConversion:
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +00001039 needsCast = true;
1040 break;
1041
1042 case CK_Dependent:
1043 case CK_BitCast:
1044 case CK_LValueBitCast:
Erik Pilkingtoneee944e2019-07-02 18:28:13 +00001045 case CK_LValueToRValueBitCast:
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +00001046 case CK_BaseToDerived:
1047 case CK_DerivedToBase:
1048 case CK_UncheckedDerivedToBase:
1049 case CK_Dynamic:
1050 case CK_ToUnion:
1051 case CK_ArrayToPointerDecay:
1052 case CK_FunctionToPointerDecay:
1053 case CK_NullToPointer:
1054 case CK_NullToMemberPointer:
1055 case CK_BaseToDerivedMemberPointer:
1056 case CK_DerivedToBaseMemberPointer:
1057 case CK_MemberPointerToBoolean:
1058 case CK_ReinterpretMemberPointer:
1059 case CK_ConstructorConversion:
1060 case CK_IntegralToPointer:
1061 case CK_PointerToIntegral:
1062 case CK_ToVoid:
1063 case CK_VectorSplat:
1064 case CK_CPointerToObjCPointerCast:
1065 case CK_BlockPointerToObjCPointerCast:
1066 case CK_AnyPointerToBlockPointerCast:
1067 case CK_ObjCObjectLValueCast:
1068 case CK_FloatingRealToComplex:
1069 case CK_FloatingComplexCast:
1070 case CK_FloatingComplexToIntegralComplex:
1071 case CK_IntegralRealToComplex:
1072 case CK_IntegralComplexCast:
1073 case CK_IntegralComplexToFloatingComplex:
1074 case CK_ARCProduceObject:
1075 case CK_ARCConsumeObject:
1076 case CK_ARCReclaimReturnedObject:
1077 case CK_ARCExtendBlockObject:
1078 case CK_NonAtomicToAtomic:
1079 case CK_CopyAndAutoreleaseBlockObject:
Eli Friedman34866c72012-08-31 00:14:07 +00001080 case CK_BuiltinFnToFnPtr:
Andrew Savonichevb555b762018-10-23 15:19:20 +00001081 case CK_ZeroToOCLOpaqueType:
Yaxun Liu0bc4b2d2016-07-28 19:26:30 +00001082 case CK_IntToOCLSampler:
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +00001083 return false;
George Burgess IVdf1ed002016-01-13 01:52:39 +00001084
1085 case CK_BooleanToSignedIntegral:
1086 llvm_unreachable("OpenCL-specific cast in Objective-C?");
Leonard Chan99bda372018-10-15 16:07:02 +00001087
1088 case CK_FixedPointCast:
Leonard Chanb4ba4672018-10-23 17:55:35 +00001089 case CK_FixedPointToBoolean:
Leonard Chan8f7caae2019-03-06 00:28:43 +00001090 case CK_FixedPointToIntegral:
1091 case CK_IntegralToFixedPoint:
Leonard Chan99bda372018-10-15 16:07:02 +00001092 llvm_unreachable("Fixed point types are disabled for Objective-C");
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +00001093 }
1094 }
1095
Argyrios Kyrtzidisb4822602012-05-24 16:48:23 +00001096 if (needsCast) {
Fangrui Song6907ce22018-07-30 19:24:48 +00001097 DiagnosticsEngine &Diags = Ctx.getDiagnostics();
Argyrios Kyrtzidisb4822602012-05-24 16:48:23 +00001098 // FIXME: Use a custom category name to distinguish migration diagnostics.
1099 unsigned diagID = Diags.getCustomDiagID(DiagnosticsEngine::Warning,
Argyrios Kyrtzidis927a4372012-06-20 01:28:32 +00001100 "converting to boxing syntax requires casting %0 to %1");
1101 Diags.Report(Msg->getExprLoc(), diagID) << OrigTy << FinalTy
1102 << Msg->getSourceRange();
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +00001103 return false;
Argyrios Kyrtzidisb4822602012-05-24 16:48:23 +00001104 }
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +00001105
1106 SourceRange ArgRange = OrigArg->getSourceRange();
Argyrios Kyrtzidis7bd957c2012-05-15 22:22:10 +00001107 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +00001108
1109 if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
1110 commit.insertBefore(ArgRange.getBegin(), "@");
1111 else
1112 commit.insertWrap("@(", ArgRange, ")");
1113
1114 return true;
1115}
Argyrios Kyrtzidis7bd957c2012-05-15 22:22:10 +00001116
1117//===----------------------------------------------------------------------===//
1118// rewriteToStringBoxedExpression.
1119//===----------------------------------------------------------------------===//
1120
1121static bool doRewriteToUTF8StringBoxedExpressionHelper(
1122 const ObjCMessageExpr *Msg,
1123 const NSAPI &NS, Commit &commit) {
1124 const Expr *Arg = Msg->getArg(0);
1125 if (Arg->isTypeDependent())
1126 return false;
1127
Argyrios Kyrtzidis81541472012-05-16 00:21:21 +00001128 ASTContext &Ctx = NS.getASTContext();
1129
Argyrios Kyrtzidis7bd957c2012-05-15 22:22:10 +00001130 const Expr *OrigArg = Arg->IgnoreImpCasts();
1131 QualType OrigTy = OrigArg->getType();
Argyrios Kyrtzidis81541472012-05-16 00:21:21 +00001132 if (OrigTy->isArrayType())
1133 OrigTy = Ctx.getArrayDecayedType(OrigTy);
Argyrios Kyrtzidis7bd957c2012-05-15 22:22:10 +00001134
1135 if (const StringLiteral *
1136 StrE = dyn_cast<StringLiteral>(OrigArg->IgnoreParens())) {
1137 commit.replaceWithInner(Msg->getSourceRange(), StrE->getSourceRange());
Stephen Kellyf2ceec42018-08-09 21:08:08 +00001138 commit.insert(StrE->getBeginLoc(), "@");
Argyrios Kyrtzidis7bd957c2012-05-15 22:22:10 +00001139 return true;
1140 }
1141
Argyrios Kyrtzidis7bd957c2012-05-15 22:22:10 +00001142 if (const PointerType *PT = OrigTy->getAs<PointerType>()) {
1143 QualType PointeeType = PT->getPointeeType();
1144 if (Ctx.hasSameUnqualifiedType(PointeeType, Ctx.CharTy)) {
1145 SourceRange ArgRange = OrigArg->getSourceRange();
1146 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
1147
1148 if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
1149 commit.insertBefore(ArgRange.getBegin(), "@");
1150 else
1151 commit.insertWrap("@(", ArgRange, ")");
Fangrui Song6907ce22018-07-30 19:24:48 +00001152
Argyrios Kyrtzidis7bd957c2012-05-15 22:22:10 +00001153 return true;
1154 }
1155 }
1156
1157 return false;
1158}
1159
1160static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
1161 const NSAPI &NS, Commit &commit) {
1162 Selector Sel = Msg->getSelector();
1163
1164 if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithUTF8String) ||
Fariborz Jahanianbe7bf722014-08-25 20:22:25 +00001165 Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCString) ||
1166 Sel == NS.getNSStringSelector(NSAPI::NSStr_initWithUTF8String)) {
Argyrios Kyrtzidis7bd957c2012-05-15 22:22:10 +00001167 if (Msg->getNumArgs() != 1)
1168 return false;
1169 return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);
1170 }
1171
1172 if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCStringEncoding)) {
1173 if (Msg->getNumArgs() != 2)
1174 return false;
1175
1176 const Expr *encodingArg = Msg->getArg(1);
1177 if (NS.isNSUTF8StringEncodingConstant(encodingArg) ||
1178 NS.isNSASCIIStringEncodingConstant(encodingArg))
1179 return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);
1180 }
1181
1182 return false;
1183}