1 //===--- 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"
15 #include "clang/Edit/Commit.h"
16 #include "clang/Lex/Lexer.h"
17 #include "clang/AST/ExprObjC.h"
18 #include "clang/AST/ExprCXX.h"
19 #include "clang/AST/NSAPI.h"
20 
21 using namespace clang;
22 using namespace edit;
23 
24 static bool checkForLiteralCreation(const ObjCMessageExpr *Msg,
25                                     IdentifierInfo *&ClassId,
26                                     const LangOptions &LangOpts) {
27   if (!Msg || Msg->isImplicit() || !Msg->getMethodDecl())
28     return false;
29 
30   const ObjCInterfaceDecl *Receiver = Msg->getReceiverInterface();
31   if (!Receiver)
32     return false;
33   ClassId = Receiver->getIdentifier();
34 
35   if (Msg->getReceiverKind() == ObjCMessageExpr::Class)
36     return true;
37 
38   // When in ARC mode we also convert "[[.. alloc] init]" messages to literals,
39   // since the change from +1 to +0 will be handled fine by ARC.
40   if (LangOpts.ObjCAutoRefCount) {
41     if (Msg->getReceiverKind() == ObjCMessageExpr::Instance) {
42       if (const ObjCMessageExpr *Rec = dyn_cast<ObjCMessageExpr>(
43                            Msg->getInstanceReceiver()->IgnoreParenImpCasts())) {
44         if (Rec->getMethodFamily() == OMF_alloc)
45           return true;
46       }
47     }
48   }
49 
50   return false;
51 }
52 
53 //===----------------------------------------------------------------------===//
54 // rewriteObjCRedundantCallWithLiteral.
55 //===----------------------------------------------------------------------===//
56 
57 bool edit::rewriteObjCRedundantCallWithLiteral(const ObjCMessageExpr *Msg,
58                                               const NSAPI &NS, Commit &commit) {
59   IdentifierInfo *II = 0;
60   if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
61     return false;
62   if (Msg->getNumArgs() != 1)
63     return false;
64 
65   const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts();
66   Selector Sel = Msg->getSelector();
67 
68   if ((isa<ObjCStringLiteral>(Arg) &&
69        NS.getNSClassId(NSAPI::ClassId_NSString) == II &&
70        (NS.getNSStringSelector(NSAPI::NSStr_stringWithString) == Sel ||
71         NS.getNSStringSelector(NSAPI::NSStr_initWithString) == Sel))   ||
72 
73       (isa<ObjCArrayLiteral>(Arg) &&
74        NS.getNSClassId(NSAPI::ClassId_NSArray) == II &&
75        (NS.getNSArraySelector(NSAPI::NSArr_arrayWithArray) == Sel ||
76         NS.getNSArraySelector(NSAPI::NSArr_initWithArray) == Sel))     ||
77 
78       (isa<ObjCDictionaryLiteral>(Arg) &&
79        NS.getNSClassId(NSAPI::ClassId_NSDictionary) == II &&
80        (NS.getNSDictionarySelector(
81                               NSAPI::NSDict_dictionaryWithDictionary) == Sel ||
82         NS.getNSDictionarySelector(NSAPI::NSDict_initWithDictionary) == Sel))) {
83 
84     commit.replaceWithInner(Msg->getSourceRange(),
85                            Msg->getArg(0)->getSourceRange());
86     return true;
87   }
88 
89   return false;
90 }
91 
92 //===----------------------------------------------------------------------===//
93 // rewriteToObjCSubscriptSyntax.
94 //===----------------------------------------------------------------------===//
95 
96 static bool subscriptOperatorNeedsParens(const Expr *FullExpr);
97 
98 static void maybePutParensOnReceiver(const Expr *Receiver, Commit &commit) {
99   if (subscriptOperatorNeedsParens(Receiver)) {
100     SourceRange RecRange = Receiver->getSourceRange();
101     commit.insertWrap("(", RecRange, ")");
102   }
103 }
104 
105 static bool rewriteToSubscriptGetCommon(const ObjCMessageExpr *Msg,
106                                         Commit &commit) {
107   if (Msg->getNumArgs() != 1)
108     return false;
109   const Expr *Rec = Msg->getInstanceReceiver();
110   if (!Rec)
111     return false;
112 
113   SourceRange MsgRange = Msg->getSourceRange();
114   SourceRange RecRange = Rec->getSourceRange();
115   SourceRange ArgRange = Msg->getArg(0)->getSourceRange();
116 
117   commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
118                                                        ArgRange.getBegin()),
119                          CharSourceRange::getTokenRange(RecRange));
120   commit.replaceWithInner(SourceRange(ArgRange.getBegin(), MsgRange.getEnd()),
121                          ArgRange);
122   commit.insertWrap("[", ArgRange, "]");
123   maybePutParensOnReceiver(Rec, commit);
124   return true;
125 }
126 
127 static bool rewriteToArraySubscriptGet(const ObjCInterfaceDecl *IFace,
128                                        const ObjCMessageExpr *Msg,
129                                        const NSAPI &NS,
130                                        Commit &commit) {
131   if (!IFace->getInstanceMethod(NS.getObjectAtIndexedSubscriptSelector()))
132     return false;
133   return rewriteToSubscriptGetCommon(Msg, commit);
134 }
135 
136 static bool rewriteToDictionarySubscriptGet(const ObjCInterfaceDecl *IFace,
137                                             const ObjCMessageExpr *Msg,
138                                             const NSAPI &NS,
139                                             Commit &commit) {
140   if (!IFace->getInstanceMethod(NS.getObjectForKeyedSubscriptSelector()))
141     return false;
142   return rewriteToSubscriptGetCommon(Msg, commit);
143 }
144 
145 static bool rewriteToArraySubscriptSet(const ObjCInterfaceDecl *IFace,
146                                        const ObjCMessageExpr *Msg,
147                                        const NSAPI &NS,
148                                        Commit &commit) {
149   if (Msg->getNumArgs() != 2)
150     return false;
151   const Expr *Rec = Msg->getInstanceReceiver();
152   if (!Rec)
153     return false;
154   if (!IFace->getInstanceMethod(NS.getSetObjectAtIndexedSubscriptSelector()))
155     return false;
156 
157   SourceRange MsgRange = Msg->getSourceRange();
158   SourceRange RecRange = Rec->getSourceRange();
159   SourceRange Arg0Range = Msg->getArg(0)->getSourceRange();
160   SourceRange Arg1Range = Msg->getArg(1)->getSourceRange();
161 
162   commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
163                                                        Arg0Range.getBegin()),
164                          CharSourceRange::getTokenRange(RecRange));
165   commit.replaceWithInner(CharSourceRange::getCharRange(Arg0Range.getBegin(),
166                                                        Arg1Range.getBegin()),
167                          CharSourceRange::getTokenRange(Arg0Range));
168   commit.replaceWithInner(SourceRange(Arg1Range.getBegin(), MsgRange.getEnd()),
169                          Arg1Range);
170   commit.insertWrap("[", CharSourceRange::getCharRange(Arg0Range.getBegin(),
171                                                        Arg1Range.getBegin()),
172                     "] = ");
173   maybePutParensOnReceiver(Rec, commit);
174   return true;
175 }
176 
177 static bool rewriteToDictionarySubscriptSet(const ObjCInterfaceDecl *IFace,
178                                             const ObjCMessageExpr *Msg,
179                                             const NSAPI &NS,
180                                             Commit &commit) {
181   if (Msg->getNumArgs() != 2)
182     return false;
183   const Expr *Rec = Msg->getInstanceReceiver();
184   if (!Rec)
185     return false;
186   if (!IFace->getInstanceMethod(NS.getSetObjectForKeyedSubscriptSelector()))
187     return false;
188 
189   SourceRange MsgRange = Msg->getSourceRange();
190   SourceRange RecRange = Rec->getSourceRange();
191   SourceRange Arg0Range = Msg->getArg(0)->getSourceRange();
192   SourceRange Arg1Range = Msg->getArg(1)->getSourceRange();
193 
194   SourceLocation LocBeforeVal = Arg0Range.getBegin();
195   commit.insertBefore(LocBeforeVal, "] = ");
196   commit.insertFromRange(LocBeforeVal, Arg1Range, /*afterToken=*/false,
197                          /*beforePreviousInsertions=*/true);
198   commit.insertBefore(LocBeforeVal, "[");
199   commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
200                                                        Arg0Range.getBegin()),
201                          CharSourceRange::getTokenRange(RecRange));
202   commit.replaceWithInner(SourceRange(Arg0Range.getBegin(), MsgRange.getEnd()),
203                          Arg0Range);
204   maybePutParensOnReceiver(Rec, commit);
205   return true;
206 }
207 
208 bool edit::rewriteToObjCSubscriptSyntax(const ObjCMessageExpr *Msg,
209                                         const NSAPI &NS, Commit &commit) {
210   if (!Msg || Msg->isImplicit() ||
211       Msg->getReceiverKind() != ObjCMessageExpr::Instance)
212     return false;
213   const ObjCMethodDecl *Method = Msg->getMethodDecl();
214   if (!Method)
215     return false;
216 
217   const ObjCInterfaceDecl *
218     IFace = NS.getASTContext().getObjContainingInterface(
219                                           const_cast<ObjCMethodDecl *>(Method));
220   if (!IFace)
221     return false;
222   IdentifierInfo *II = IFace->getIdentifier();
223   Selector Sel = Msg->getSelector();
224 
225   if (II == NS.getNSClassId(NSAPI::ClassId_NSArray) &&
226       Sel == NS.getNSArraySelector(NSAPI::NSArr_objectAtIndex))
227     return rewriteToArraySubscriptGet(IFace, Msg, NS, commit);
228 
229   if (II == NS.getNSClassId(NSAPI::ClassId_NSDictionary) &&
230       Sel == NS.getNSDictionarySelector(NSAPI::NSDict_objectForKey))
231     return rewriteToDictionarySubscriptGet(IFace, Msg, NS, commit);
232 
233   if (Msg->getNumArgs() != 2)
234     return false;
235 
236   if (II == NS.getNSClassId(NSAPI::ClassId_NSMutableArray) &&
237       Sel == NS.getNSArraySelector(NSAPI::NSMutableArr_replaceObjectAtIndex))
238     return rewriteToArraySubscriptSet(IFace, Msg, NS, commit);
239 
240   if (II == NS.getNSClassId(NSAPI::ClassId_NSMutableDictionary) &&
241       Sel == NS.getNSDictionarySelector(NSAPI::NSMutableDict_setObjectForKey))
242     return rewriteToDictionarySubscriptSet(IFace, Msg, NS, commit);
243 
244   return false;
245 }
246 
247 //===----------------------------------------------------------------------===//
248 // rewriteToObjCLiteralSyntax.
249 //===----------------------------------------------------------------------===//
250 
251 static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
252                                   const NSAPI &NS, Commit &commit);
253 static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
254                                   const NSAPI &NS, Commit &commit);
255 static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
256                                   const NSAPI &NS, Commit &commit);
257 static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
258                                             const NSAPI &NS, Commit &commit);
259 static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
260                                            const NSAPI &NS, Commit &commit);
261 
262 bool edit::rewriteToObjCLiteralSyntax(const ObjCMessageExpr *Msg,
263                                       const NSAPI &NS, Commit &commit) {
264   IdentifierInfo *II = 0;
265   if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
266     return false;
267 
268   if (II == NS.getNSClassId(NSAPI::ClassId_NSArray))
269     return rewriteToArrayLiteral(Msg, NS, commit);
270   if (II == NS.getNSClassId(NSAPI::ClassId_NSDictionary))
271     return rewriteToDictionaryLiteral(Msg, NS, commit);
272   if (II == NS.getNSClassId(NSAPI::ClassId_NSNumber))
273     return rewriteToNumberLiteral(Msg, NS, commit);
274   if (II == NS.getNSClassId(NSAPI::ClassId_NSString))
275     return rewriteToStringBoxedExpression(Msg, NS, commit);
276 
277   return false;
278 }
279 
280 //===----------------------------------------------------------------------===//
281 // rewriteToArrayLiteral.
282 //===----------------------------------------------------------------------===//
283 
284 /// \brief Adds an explicit cast to 'id' if the type is not objc object.
285 static void objectifyExpr(const Expr *E, Commit &commit);
286 
287 static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
288                                   const NSAPI &NS, Commit &commit) {
289   Selector Sel = Msg->getSelector();
290   SourceRange MsgRange = Msg->getSourceRange();
291 
292   if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array)) {
293     if (Msg->getNumArgs() != 0)
294       return false;
295     commit.replace(MsgRange, "@[]");
296     return true;
297   }
298 
299   if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) {
300     if (Msg->getNumArgs() != 1)
301       return false;
302     objectifyExpr(Msg->getArg(0), commit);
303     SourceRange ArgRange = Msg->getArg(0)->getSourceRange();
304     commit.replaceWithInner(MsgRange, ArgRange);
305     commit.insertWrap("@[", ArgRange, "]");
306     return true;
307   }
308 
309   if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) ||
310       Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) {
311     if (Msg->getNumArgs() == 0)
312       return false;
313     const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1);
314     if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
315       return false;
316 
317     for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i)
318       objectifyExpr(Msg->getArg(i), commit);
319 
320     if (Msg->getNumArgs() == 1) {
321       commit.replace(MsgRange, "@[]");
322       return true;
323     }
324     SourceRange ArgRange(Msg->getArg(0)->getLocStart(),
325                          Msg->getArg(Msg->getNumArgs()-2)->getLocEnd());
326     commit.replaceWithInner(MsgRange, ArgRange);
327     commit.insertWrap("@[", ArgRange, "]");
328     return true;
329   }
330 
331   return false;
332 }
333 
334 //===----------------------------------------------------------------------===//
335 // rewriteToDictionaryLiteral.
336 //===----------------------------------------------------------------------===//
337 
338 static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
339                                        const NSAPI &NS, Commit &commit) {
340   Selector Sel = Msg->getSelector();
341   SourceRange MsgRange = Msg->getSourceRange();
342 
343   if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_dictionary)) {
344     if (Msg->getNumArgs() != 0)
345       return false;
346     commit.replace(MsgRange, "@{}");
347     return true;
348   }
349 
350   if (Sel == NS.getNSDictionarySelector(
351                                     NSAPI::NSDict_dictionaryWithObjectForKey)) {
352     if (Msg->getNumArgs() != 2)
353       return false;
354 
355     objectifyExpr(Msg->getArg(0), commit);
356     objectifyExpr(Msg->getArg(1), commit);
357 
358     SourceRange ValRange = Msg->getArg(0)->getSourceRange();
359     SourceRange KeyRange = Msg->getArg(1)->getSourceRange();
360     // Insert key before the value.
361     commit.insertBefore(ValRange.getBegin(), ": ");
362     commit.insertFromRange(ValRange.getBegin(),
363                            CharSourceRange::getTokenRange(KeyRange),
364                        /*afterToken=*/false, /*beforePreviousInsertions=*/true);
365     commit.insertBefore(ValRange.getBegin(), "@{");
366     commit.insertAfterToken(ValRange.getEnd(), "}");
367     commit.replaceWithInner(MsgRange, ValRange);
368     return true;
369   }
370 
371   if (Sel == NS.getNSDictionarySelector(
372                                   NSAPI::NSDict_dictionaryWithObjectsAndKeys) ||
373       Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsAndKeys)) {
374     if (Msg->getNumArgs() % 2 != 1)
375       return false;
376     unsigned SentinelIdx = Msg->getNumArgs() - 1;
377     const Expr *SentinelExpr = Msg->getArg(SentinelIdx);
378     if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
379       return false;
380 
381     if (Msg->getNumArgs() == 1) {
382       commit.replace(MsgRange, "@{}");
383       return true;
384     }
385 
386     for (unsigned i = 0; i < SentinelIdx; i += 2) {
387       objectifyExpr(Msg->getArg(i), commit);
388       objectifyExpr(Msg->getArg(i+1), commit);
389 
390       SourceRange ValRange = Msg->getArg(i)->getSourceRange();
391       SourceRange KeyRange = Msg->getArg(i+1)->getSourceRange();
392       // Insert value after key.
393       commit.insertAfterToken(KeyRange.getEnd(), ": ");
394       commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true);
395       commit.remove(CharSourceRange::getCharRange(ValRange.getBegin(),
396                                                   KeyRange.getBegin()));
397     }
398     // Range of arguments up until and including the last key.
399     // The sentinel and first value are cut off, the value will move after the
400     // key.
401     SourceRange ArgRange(Msg->getArg(1)->getLocStart(),
402                          Msg->getArg(SentinelIdx-1)->getLocEnd());
403     commit.insertWrap("@{", ArgRange, "}");
404     commit.replaceWithInner(MsgRange, ArgRange);
405     return true;
406   }
407 
408   return false;
409 }
410 
411 //===----------------------------------------------------------------------===//
412 // rewriteToNumberLiteral.
413 //===----------------------------------------------------------------------===//
414 
415 static bool rewriteToCharLiteral(const ObjCMessageExpr *Msg,
416                                    const CharacterLiteral *Arg,
417                                    const NSAPI &NS, Commit &commit) {
418   if (Arg->getKind() != CharacterLiteral::Ascii)
419     return false;
420   if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithChar,
421                                    Msg->getSelector())) {
422     SourceRange ArgRange = Arg->getSourceRange();
423     commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
424     commit.insert(ArgRange.getBegin(), "@");
425     return true;
426   }
427 
428   return rewriteToNumericBoxedExpression(Msg, NS, commit);
429 }
430 
431 static bool rewriteToBoolLiteral(const ObjCMessageExpr *Msg,
432                                    const Expr *Arg,
433                                    const NSAPI &NS, Commit &commit) {
434   if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithBool,
435                                    Msg->getSelector())) {
436     SourceRange ArgRange = Arg->getSourceRange();
437     commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
438     commit.insert(ArgRange.getBegin(), "@");
439     return true;
440   }
441 
442   return rewriteToNumericBoxedExpression(Msg, NS, commit);
443 }
444 
445 namespace {
446 
447 struct LiteralInfo {
448   bool Hex, Octal;
449   StringRef U, F, L, LL;
450   CharSourceRange WithoutSuffRange;
451 };
452 
453 }
454 
455 static bool getLiteralInfo(SourceRange literalRange,
456                            bool isFloat, bool isIntZero,
457                           ASTContext &Ctx, LiteralInfo &Info) {
458   if (literalRange.getBegin().isMacroID() ||
459       literalRange.getEnd().isMacroID())
460     return false;
461   StringRef text = Lexer::getSourceText(
462                                   CharSourceRange::getTokenRange(literalRange),
463                                   Ctx.getSourceManager(), Ctx.getLangOpts());
464   if (text.empty())
465     return false;
466 
467   llvm::Optional<bool> UpperU, UpperL;
468   bool UpperF = false;
469 
470   struct Suff {
471     static bool has(StringRef suff, StringRef &text) {
472       if (text.endswith(suff)) {
473         text = text.substr(0, text.size()-suff.size());
474         return true;
475       }
476       return false;
477     }
478   };
479 
480   while (1) {
481     if (Suff::has("u", text)) {
482       UpperU = false;
483     } else if (Suff::has("U", text)) {
484       UpperU = true;
485     } else if (Suff::has("ll", text)) {
486       UpperL = false;
487     } else if (Suff::has("LL", text)) {
488       UpperL = true;
489     } else if (Suff::has("l", text)) {
490       UpperL = false;
491     } else if (Suff::has("L", text)) {
492       UpperL = true;
493     } else if (isFloat && Suff::has("f", text)) {
494       UpperF = false;
495     } else if (isFloat && Suff::has("F", text)) {
496       UpperF = true;
497     } else
498       break;
499   }
500 
501   if (!UpperU.hasValue() && !UpperL.hasValue())
502     UpperU = UpperL = true;
503   else if (UpperU.hasValue() && !UpperL.hasValue())
504     UpperL = UpperU;
505   else if (UpperL.hasValue() && !UpperU.hasValue())
506     UpperU = UpperL;
507 
508   Info.U = *UpperU ? "U" : "u";
509   Info.L = *UpperL ? "L" : "l";
510   Info.LL = *UpperL ? "LL" : "ll";
511   Info.F = UpperF ? "F" : "f";
512 
513   Info.Hex = Info.Octal = false;
514   if (text.startswith("0x"))
515     Info.Hex = true;
516   else if (!isFloat && !isIntZero && text.startswith("0"))
517     Info.Octal = true;
518 
519   SourceLocation B = literalRange.getBegin();
520   Info.WithoutSuffRange =
521       CharSourceRange::getCharRange(B, B.getLocWithOffset(text.size()));
522   return true;
523 }
524 
525 static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
526                                    const NSAPI &NS, Commit &commit) {
527   if (Msg->getNumArgs() != 1)
528     return false;
529 
530   const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts();
531   if (const CharacterLiteral *CharE = dyn_cast<CharacterLiteral>(Arg))
532     return rewriteToCharLiteral(Msg, CharE, NS, commit);
533   if (const ObjCBoolLiteralExpr *BE = dyn_cast<ObjCBoolLiteralExpr>(Arg))
534     return rewriteToBoolLiteral(Msg, BE, NS, commit);
535   if (const CXXBoolLiteralExpr *BE = dyn_cast<CXXBoolLiteralExpr>(Arg))
536     return rewriteToBoolLiteral(Msg, BE, NS, commit);
537 
538   const Expr *literalE = Arg;
539   if (const UnaryOperator *UOE = dyn_cast<UnaryOperator>(literalE)) {
540     if (UOE->getOpcode() == UO_Plus || UOE->getOpcode() == UO_Minus)
541       literalE = UOE->getSubExpr();
542   }
543 
544   // Only integer and floating literals, otherwise try to rewrite to boxed
545   // expression.
546   if (!isa<IntegerLiteral>(literalE) && !isa<FloatingLiteral>(literalE))
547     return rewriteToNumericBoxedExpression(Msg, NS, commit);
548 
549   ASTContext &Ctx = NS.getASTContext();
550   Selector Sel = Msg->getSelector();
551   llvm::Optional<NSAPI::NSNumberLiteralMethodKind>
552     MKOpt = NS.getNSNumberLiteralMethodKind(Sel);
553   if (!MKOpt)
554     return false;
555   NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;
556 
557   bool CallIsUnsigned = false, CallIsLong = false, CallIsLongLong = false;
558   bool CallIsFloating = false, CallIsDouble = false;
559 
560   switch (MK) {
561   // We cannot have these calls with int/float literals.
562   case NSAPI::NSNumberWithChar:
563   case NSAPI::NSNumberWithUnsignedChar:
564   case NSAPI::NSNumberWithShort:
565   case NSAPI::NSNumberWithUnsignedShort:
566   case NSAPI::NSNumberWithBool:
567     return rewriteToNumericBoxedExpression(Msg, NS, commit);
568 
569   case NSAPI::NSNumberWithUnsignedInt:
570   case NSAPI::NSNumberWithUnsignedInteger:
571     CallIsUnsigned = true;
572   case NSAPI::NSNumberWithInt:
573   case NSAPI::NSNumberWithInteger:
574     break;
575 
576   case NSAPI::NSNumberWithUnsignedLong:
577     CallIsUnsigned = true;
578   case NSAPI::NSNumberWithLong:
579     CallIsLong = true;
580     break;
581 
582   case NSAPI::NSNumberWithUnsignedLongLong:
583     CallIsUnsigned = true;
584   case NSAPI::NSNumberWithLongLong:
585     CallIsLongLong = true;
586     break;
587 
588   case NSAPI::NSNumberWithDouble:
589     CallIsDouble = true;
590   case NSAPI::NSNumberWithFloat:
591     CallIsFloating = true;
592     break;
593   }
594 
595   SourceRange ArgRange = Arg->getSourceRange();
596   QualType ArgTy = Arg->getType();
597   QualType CallTy = Msg->getArg(0)->getType();
598 
599   // Check for the easy case, the literal maps directly to the call.
600   if (Ctx.hasSameType(ArgTy, CallTy)) {
601     commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
602     commit.insert(ArgRange.getBegin(), "@");
603     return true;
604   }
605 
606   // We will need to modify the literal suffix to get the same type as the call.
607   // Try with boxed expression if it came from a macro.
608   if (ArgRange.getBegin().isMacroID())
609     return rewriteToNumericBoxedExpression(Msg, NS, commit);
610 
611   bool LitIsFloat = ArgTy->isFloatingType();
612   // For a float passed to integer call, don't try rewriting to objc literal.
613   // It is difficult and a very uncommon case anyway.
614   // But try with boxed expression.
615   if (LitIsFloat && !CallIsFloating)
616     return rewriteToNumericBoxedExpression(Msg, NS, commit);
617 
618   // Try to modify the literal make it the same type as the method call.
619   // -Modify the suffix, and/or
620   // -Change integer to float
621 
622   LiteralInfo LitInfo;
623   bool isIntZero = false;
624   if (const IntegerLiteral *IntE = dyn_cast<IntegerLiteral>(literalE))
625     isIntZero = !IntE->getValue().getBoolValue();
626   if (!getLiteralInfo(ArgRange, LitIsFloat, isIntZero, Ctx, LitInfo))
627     return rewriteToNumericBoxedExpression(Msg, NS, commit);
628 
629   // Not easy to do int -> float with hex/octal and uncommon anyway.
630   if (!LitIsFloat && CallIsFloating && (LitInfo.Hex || LitInfo.Octal))
631     return rewriteToNumericBoxedExpression(Msg, NS, commit);
632 
633   SourceLocation LitB = LitInfo.WithoutSuffRange.getBegin();
634   SourceLocation LitE = LitInfo.WithoutSuffRange.getEnd();
635 
636   commit.replaceWithInner(CharSourceRange::getTokenRange(Msg->getSourceRange()),
637                          LitInfo.WithoutSuffRange);
638   commit.insert(LitB, "@");
639 
640   if (!LitIsFloat && CallIsFloating)
641     commit.insert(LitE, ".0");
642 
643   if (CallIsFloating) {
644     if (!CallIsDouble)
645       commit.insert(LitE, LitInfo.F);
646   } else {
647     if (CallIsUnsigned)
648       commit.insert(LitE, LitInfo.U);
649 
650     if (CallIsLong)
651       commit.insert(LitE, LitInfo.L);
652     else if (CallIsLongLong)
653       commit.insert(LitE, LitInfo.LL);
654   }
655   return true;
656 }
657 
658 // FIXME: Make determination of operator precedence more general and
659 // make it broadly available.
660 static bool subscriptOperatorNeedsParens(const Expr *FullExpr) {
661   const Expr* Expr = FullExpr->IgnoreImpCasts();
662   if (isa<ArraySubscriptExpr>(Expr) ||
663       isa<CallExpr>(Expr) ||
664       isa<DeclRefExpr>(Expr) ||
665       isa<CXXNamedCastExpr>(Expr) ||
666       isa<CXXConstructExpr>(Expr) ||
667       isa<CXXThisExpr>(Expr) ||
668       isa<CXXTypeidExpr>(Expr) ||
669       isa<CXXUnresolvedConstructExpr>(Expr) ||
670       isa<ObjCMessageExpr>(Expr) ||
671       isa<ObjCPropertyRefExpr>(Expr) ||
672       isa<ObjCProtocolExpr>(Expr) ||
673       isa<MemberExpr>(Expr) ||
674       isa<ObjCIvarRefExpr>(Expr) ||
675       isa<ParenExpr>(FullExpr) ||
676       isa<ParenListExpr>(Expr) ||
677       isa<SizeOfPackExpr>(Expr))
678     return false;
679 
680   return true;
681 }
682 static bool castOperatorNeedsParens(const Expr *FullExpr) {
683   const Expr* Expr = FullExpr->IgnoreImpCasts();
684   if (isa<ArraySubscriptExpr>(Expr) ||
685       isa<CallExpr>(Expr) ||
686       isa<DeclRefExpr>(Expr) ||
687       isa<CastExpr>(Expr) ||
688       isa<CXXNewExpr>(Expr) ||
689       isa<CXXConstructExpr>(Expr) ||
690       isa<CXXDeleteExpr>(Expr) ||
691       isa<CXXNoexceptExpr>(Expr) ||
692       isa<CXXPseudoDestructorExpr>(Expr) ||
693       isa<CXXScalarValueInitExpr>(Expr) ||
694       isa<CXXThisExpr>(Expr) ||
695       isa<CXXTypeidExpr>(Expr) ||
696       isa<CXXUnresolvedConstructExpr>(Expr) ||
697       isa<ObjCMessageExpr>(Expr) ||
698       isa<ObjCPropertyRefExpr>(Expr) ||
699       isa<ObjCProtocolExpr>(Expr) ||
700       isa<MemberExpr>(Expr) ||
701       isa<ObjCIvarRefExpr>(Expr) ||
702       isa<ParenExpr>(FullExpr) ||
703       isa<ParenListExpr>(Expr) ||
704       isa<SizeOfPackExpr>(Expr) ||
705       isa<UnaryOperator>(Expr))
706     return false;
707 
708   return true;
709 }
710 
711 static void objectifyExpr(const Expr *E, Commit &commit) {
712   if (!E) return;
713 
714   QualType T = E->getType();
715   if (T->isObjCObjectPointerType()) {
716     if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) {
717       if (ICE->getCastKind() != CK_CPointerToObjCPointerCast)
718         return;
719     } else {
720       return;
721     }
722   } else if (!T->isPointerType()) {
723     return;
724   }
725 
726   SourceRange Range = E->getSourceRange();
727   if (castOperatorNeedsParens(E))
728     commit.insertWrap("(", Range, ")");
729   commit.insertBefore(Range.getBegin(), "(id)");
730 }
731 
732 //===----------------------------------------------------------------------===//
733 // rewriteToNumericBoxedExpression.
734 //===----------------------------------------------------------------------===//
735 
736 static bool isEnumConstant(const Expr *E) {
737   if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts()))
738     if (const ValueDecl *VD = DRE->getDecl())
739       return isa<EnumConstantDecl>(VD);
740 
741   return false;
742 }
743 
744 static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
745                                             const NSAPI &NS, Commit &commit) {
746   if (Msg->getNumArgs() != 1)
747     return false;
748 
749   const Expr *Arg = Msg->getArg(0);
750   if (Arg->isTypeDependent())
751     return false;
752 
753   ASTContext &Ctx = NS.getASTContext();
754   Selector Sel = Msg->getSelector();
755   llvm::Optional<NSAPI::NSNumberLiteralMethodKind>
756     MKOpt = NS.getNSNumberLiteralMethodKind(Sel);
757   if (!MKOpt)
758     return false;
759   NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;
760 
761   const Expr *OrigArg = Arg->IgnoreImpCasts();
762   QualType FinalTy = Arg->getType();
763   QualType OrigTy = OrigArg->getType();
764   uint64_t FinalTySize = Ctx.getTypeSize(FinalTy);
765   uint64_t OrigTySize = Ctx.getTypeSize(OrigTy);
766 
767   bool isTruncated = FinalTySize < OrigTySize;
768   bool needsCast = false;
769 
770   if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) {
771     switch (ICE->getCastKind()) {
772     case CK_LValueToRValue:
773     case CK_NoOp:
774     case CK_UserDefinedConversion:
775       break;
776 
777     case CK_IntegralCast: {
778       if (MK == NSAPI::NSNumberWithBool && OrigTy->isBooleanType())
779         break;
780       // Be more liberal with Integer/UnsignedInteger which are very commonly
781       // used.
782       if ((MK == NSAPI::NSNumberWithInteger ||
783            MK == NSAPI::NSNumberWithUnsignedInteger) &&
784           !isTruncated) {
785         if (OrigTy->getAs<EnumType>() || isEnumConstant(OrigArg))
786           break;
787         if ((MK==NSAPI::NSNumberWithInteger) == OrigTy->isSignedIntegerType() &&
788             OrigTySize >= Ctx.getTypeSize(Ctx.IntTy))
789           break;
790       }
791 
792       needsCast = true;
793       break;
794     }
795 
796     case CK_PointerToBoolean:
797     case CK_IntegralToBoolean:
798     case CK_IntegralToFloating:
799     case CK_FloatingToIntegral:
800     case CK_FloatingToBoolean:
801     case CK_FloatingCast:
802     case CK_FloatingComplexToReal:
803     case CK_FloatingComplexToBoolean:
804     case CK_IntegralComplexToReal:
805     case CK_IntegralComplexToBoolean:
806     case CK_AtomicToNonAtomic:
807       needsCast = true;
808       break;
809 
810     case CK_Dependent:
811     case CK_BitCast:
812     case CK_LValueBitCast:
813     case CK_BaseToDerived:
814     case CK_DerivedToBase:
815     case CK_UncheckedDerivedToBase:
816     case CK_Dynamic:
817     case CK_ToUnion:
818     case CK_ArrayToPointerDecay:
819     case CK_FunctionToPointerDecay:
820     case CK_NullToPointer:
821     case CK_NullToMemberPointer:
822     case CK_BaseToDerivedMemberPointer:
823     case CK_DerivedToBaseMemberPointer:
824     case CK_MemberPointerToBoolean:
825     case CK_ReinterpretMemberPointer:
826     case CK_ConstructorConversion:
827     case CK_IntegralToPointer:
828     case CK_PointerToIntegral:
829     case CK_ToVoid:
830     case CK_VectorSplat:
831     case CK_CPointerToObjCPointerCast:
832     case CK_BlockPointerToObjCPointerCast:
833     case CK_AnyPointerToBlockPointerCast:
834     case CK_ObjCObjectLValueCast:
835     case CK_FloatingRealToComplex:
836     case CK_FloatingComplexCast:
837     case CK_FloatingComplexToIntegralComplex:
838     case CK_IntegralRealToComplex:
839     case CK_IntegralComplexCast:
840     case CK_IntegralComplexToFloatingComplex:
841     case CK_ARCProduceObject:
842     case CK_ARCConsumeObject:
843     case CK_ARCReclaimReturnedObject:
844     case CK_ARCExtendBlockObject:
845     case CK_NonAtomicToAtomic:
846     case CK_CopyAndAutoreleaseBlockObject:
847       return false;
848     }
849   }
850 
851   if (needsCast) {
852     DiagnosticsEngine &Diags = Ctx.getDiagnostics();
853     // FIXME: Use a custom category name to distinguish migration diagnostics.
854     unsigned diagID = Diags.getCustomDiagID(DiagnosticsEngine::Warning,
855                       "converting to boxing syntax requires a cast");
856     Diags.Report(Msg->getExprLoc(), diagID) << Msg->getSourceRange();
857     return false;
858   }
859 
860   SourceRange ArgRange = OrigArg->getSourceRange();
861   commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
862 
863   if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
864     commit.insertBefore(ArgRange.getBegin(), "@");
865   else
866     commit.insertWrap("@(", ArgRange, ")");
867 
868   return true;
869 }
870 
871 //===----------------------------------------------------------------------===//
872 // rewriteToStringBoxedExpression.
873 //===----------------------------------------------------------------------===//
874 
875 static bool doRewriteToUTF8StringBoxedExpressionHelper(
876                                               const ObjCMessageExpr *Msg,
877                                               const NSAPI &NS, Commit &commit) {
878   const Expr *Arg = Msg->getArg(0);
879   if (Arg->isTypeDependent())
880     return false;
881 
882   ASTContext &Ctx = NS.getASTContext();
883 
884   const Expr *OrigArg = Arg->IgnoreImpCasts();
885   QualType OrigTy = OrigArg->getType();
886   if (OrigTy->isArrayType())
887     OrigTy = Ctx.getArrayDecayedType(OrigTy);
888 
889   if (const StringLiteral *
890         StrE = dyn_cast<StringLiteral>(OrigArg->IgnoreParens())) {
891     commit.replaceWithInner(Msg->getSourceRange(), StrE->getSourceRange());
892     commit.insert(StrE->getLocStart(), "@");
893     return true;
894   }
895 
896   if (const PointerType *PT = OrigTy->getAs<PointerType>()) {
897     QualType PointeeType = PT->getPointeeType();
898     if (Ctx.hasSameUnqualifiedType(PointeeType, Ctx.CharTy)) {
899       SourceRange ArgRange = OrigArg->getSourceRange();
900       commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
901 
902       if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
903         commit.insertBefore(ArgRange.getBegin(), "@");
904       else
905         commit.insertWrap("@(", ArgRange, ")");
906 
907       return true;
908     }
909   }
910 
911   return false;
912 }
913 
914 static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
915                                            const NSAPI &NS, Commit &commit) {
916   Selector Sel = Msg->getSelector();
917 
918   if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithUTF8String) ||
919       Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCString)) {
920     if (Msg->getNumArgs() != 1)
921       return false;
922     return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);
923   }
924 
925   if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCStringEncoding)) {
926     if (Msg->getNumArgs() != 2)
927       return false;
928 
929     const Expr *encodingArg = Msg->getArg(1);
930     if (NS.isNSUTF8StringEncodingConstant(encodingArg) ||
931         NS.isNSASCIIStringEncodingConstant(encodingArg))
932       return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);
933   }
934 
935   return false;
936 }
937