RewriteObjCFoundationAPI.cpp revision 20119a87fbb7719c161d81fc5f721f1ee6ed7e66
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
21using namespace clang;
22using namespace edit;
23
24static bool checkForLiteralCreation(const ObjCMessageExpr *Msg,
25                                    IdentifierInfo *&ClassId) {
26  if (!Msg || Msg->isImplicit() || !Msg->getMethodDecl())
27    return false;
28
29  const ObjCInterfaceDecl *Receiver = Msg->getReceiverInterface();
30  if (!Receiver)
31    return false;
32  ClassId = Receiver->getIdentifier();
33
34  if (Msg->getReceiverKind() == ObjCMessageExpr::Class)
35    return true;
36
37  return false;
38}
39
40//===----------------------------------------------------------------------===//
41// rewriteObjCRedundantCallWithLiteral.
42//===----------------------------------------------------------------------===//
43
44bool edit::rewriteObjCRedundantCallWithLiteral(const ObjCMessageExpr *Msg,
45                                              const NSAPI &NS, Commit &commit) {
46  IdentifierInfo *II = 0;
47  if (!checkForLiteralCreation(Msg, II))
48    return false;
49  if (Msg->getNumArgs() != 1)
50    return false;
51
52  const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts();
53  Selector Sel = Msg->getSelector();
54
55  if ((isa<ObjCStringLiteral>(Arg) &&
56       NS.getNSClassId(NSAPI::ClassId_NSString) == II &&
57       NS.getNSStringSelector(NSAPI::NSStr_stringWithString) == Sel)    ||
58
59      (isa<ObjCArrayLiteral>(Arg) &&
60       NS.getNSClassId(NSAPI::ClassId_NSArray) == II &&
61       NS.getNSArraySelector(NSAPI::NSArr_arrayWithArray) == Sel)      ||
62
63      (isa<ObjCDictionaryLiteral>(Arg) &&
64       NS.getNSClassId(NSAPI::ClassId_NSDictionary) == II &&
65       NS.getNSDictionarySelector(
66                              NSAPI::NSDict_dictionaryWithDictionary) == Sel)) {
67
68    commit.replaceWithInner(Msg->getSourceRange(),
69                           Msg->getArg(0)->getSourceRange());
70    return true;
71  }
72
73  return false;
74}
75
76//===----------------------------------------------------------------------===//
77// rewriteToObjCSubscriptSyntax.
78//===----------------------------------------------------------------------===//
79
80static bool subscriptOperatorNeedsParens(const Expr *FullExpr);
81
82static void maybePutParensOnReceiver(const Expr *Receiver, Commit &commit) {
83  if (subscriptOperatorNeedsParens(Receiver)) {
84    SourceRange RecRange = Receiver->getSourceRange();
85    commit.insertWrap("(", RecRange, ")");
86  }
87}
88
89static bool rewriteToSubscriptGet(const ObjCMessageExpr *Msg, Commit &commit) {
90  if (Msg->getNumArgs() != 1)
91    return false;
92  const Expr *Rec = Msg->getInstanceReceiver();
93  if (!Rec)
94    return false;
95
96  SourceRange MsgRange = Msg->getSourceRange();
97  SourceRange RecRange = Rec->getSourceRange();
98  SourceRange ArgRange = Msg->getArg(0)->getSourceRange();
99
100  commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
101                                                       ArgRange.getBegin()),
102                         CharSourceRange::getTokenRange(RecRange));
103  commit.replaceWithInner(SourceRange(ArgRange.getBegin(), MsgRange.getEnd()),
104                         ArgRange);
105  commit.insertWrap("[", ArgRange, "]");
106  maybePutParensOnReceiver(Rec, commit);
107  return true;
108}
109
110static bool rewriteToArraySubscriptSet(const ObjCMessageExpr *Msg,
111                                       Commit &commit) {
112  if (Msg->getNumArgs() != 2)
113    return false;
114  const Expr *Rec = Msg->getInstanceReceiver();
115  if (!Rec)
116    return false;
117
118  SourceRange MsgRange = Msg->getSourceRange();
119  SourceRange RecRange = Rec->getSourceRange();
120  SourceRange Arg0Range = Msg->getArg(0)->getSourceRange();
121  SourceRange Arg1Range = Msg->getArg(1)->getSourceRange();
122
123  commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
124                                                       Arg0Range.getBegin()),
125                         CharSourceRange::getTokenRange(RecRange));
126  commit.replaceWithInner(CharSourceRange::getCharRange(Arg0Range.getBegin(),
127                                                       Arg1Range.getBegin()),
128                         CharSourceRange::getTokenRange(Arg0Range));
129  commit.replaceWithInner(SourceRange(Arg1Range.getBegin(), MsgRange.getEnd()),
130                         Arg1Range);
131  commit.insertWrap("[", CharSourceRange::getCharRange(Arg0Range.getBegin(),
132                                                       Arg1Range.getBegin()),
133                    "] = ");
134  maybePutParensOnReceiver(Rec, commit);
135  return true;
136}
137
138static bool rewriteToDictionarySubscriptSet(const ObjCMessageExpr *Msg,
139                                            Commit &commit) {
140  if (Msg->getNumArgs() != 2)
141    return false;
142  const Expr *Rec = Msg->getInstanceReceiver();
143  if (!Rec)
144    return false;
145
146  SourceRange MsgRange = Msg->getSourceRange();
147  SourceRange RecRange = Rec->getSourceRange();
148  SourceRange Arg0Range = Msg->getArg(0)->getSourceRange();
149  SourceRange Arg1Range = Msg->getArg(1)->getSourceRange();
150
151  SourceLocation LocBeforeVal = Arg0Range.getBegin();
152  commit.insertBefore(LocBeforeVal, "] = ");
153  commit.insertFromRange(LocBeforeVal, Arg1Range, /*afterToken=*/false,
154                         /*beforePreviousInsertions=*/true);
155  commit.insertBefore(LocBeforeVal, "[");
156  commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
157                                                       Arg0Range.getBegin()),
158                         CharSourceRange::getTokenRange(RecRange));
159  commit.replaceWithInner(SourceRange(Arg0Range.getBegin(), MsgRange.getEnd()),
160                         Arg0Range);
161  maybePutParensOnReceiver(Rec, commit);
162  return true;
163}
164
165bool edit::rewriteToObjCSubscriptSyntax(const ObjCMessageExpr *Msg,
166                                           const NSAPI &NS, Commit &commit) {
167  if (!Msg || Msg->isImplicit() ||
168      Msg->getReceiverKind() != ObjCMessageExpr::Instance)
169    return false;
170  const ObjCMethodDecl *Method = Msg->getMethodDecl();
171  if (!Method)
172    return false;
173
174  const ObjCInterfaceDecl *
175    IFace = NS.getASTContext().getObjContainingInterface(
176                                          const_cast<ObjCMethodDecl *>(Method));
177  if (!IFace)
178    return false;
179  IdentifierInfo *II = IFace->getIdentifier();
180  Selector Sel = Msg->getSelector();
181
182  if ((II == NS.getNSClassId(NSAPI::ClassId_NSArray) &&
183       Sel == NS.getNSArraySelector(NSAPI::NSArr_objectAtIndex)) ||
184      (II == NS.getNSClassId(NSAPI::ClassId_NSDictionary) &&
185       Sel == NS.getNSDictionarySelector(NSAPI::NSDict_objectForKey)))
186    return rewriteToSubscriptGet(Msg, commit);
187
188  if (Msg->getNumArgs() != 2)
189    return false;
190
191  if (II == NS.getNSClassId(NSAPI::ClassId_NSMutableArray) &&
192      Sel == NS.getNSArraySelector(NSAPI::NSMutableArr_replaceObjectAtIndex))
193    return rewriteToArraySubscriptSet(Msg, commit);
194
195  if (II == NS.getNSClassId(NSAPI::ClassId_NSMutableDictionary) &&
196      Sel == NS.getNSDictionarySelector(NSAPI::NSMutableDict_setObjectForKey))
197    return rewriteToDictionarySubscriptSet(Msg, commit);
198
199  return false;
200}
201
202//===----------------------------------------------------------------------===//
203// rewriteToObjCLiteralSyntax.
204//===----------------------------------------------------------------------===//
205
206static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
207                                  const NSAPI &NS, Commit &commit);
208static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
209                                  const NSAPI &NS, Commit &commit);
210static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
211                                  const NSAPI &NS, Commit &commit);
212
213bool edit::rewriteToObjCLiteralSyntax(const ObjCMessageExpr *Msg,
214                                      const NSAPI &NS, Commit &commit) {
215  IdentifierInfo *II = 0;
216  if (!checkForLiteralCreation(Msg, II))
217    return false;
218
219  if (II == NS.getNSClassId(NSAPI::ClassId_NSArray))
220    return rewriteToArrayLiteral(Msg, NS, commit);
221  if (II == NS.getNSClassId(NSAPI::ClassId_NSDictionary))
222    return rewriteToDictionaryLiteral(Msg, NS, commit);
223  if (II == NS.getNSClassId(NSAPI::ClassId_NSNumber))
224    return rewriteToNumberLiteral(Msg, NS, commit);
225
226  return false;
227}
228
229//===----------------------------------------------------------------------===//
230// rewriteToArrayLiteral.
231//===----------------------------------------------------------------------===//
232
233/// \brief Adds an explicit cast to 'id' if the type is not objc object.
234static void objectifyExpr(const Expr *E, Commit &commit);
235
236static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
237                                  const NSAPI &NS, Commit &commit) {
238  Selector Sel = Msg->getSelector();
239  SourceRange MsgRange = Msg->getSourceRange();
240
241  if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array)) {
242    if (Msg->getNumArgs() != 0)
243      return false;
244    commit.replace(MsgRange, "@[]");
245    return true;
246  }
247
248  if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) {
249    if (Msg->getNumArgs() != 1)
250      return false;
251    objectifyExpr(Msg->getArg(0), commit);
252    SourceRange ArgRange = Msg->getArg(0)->getSourceRange();
253    commit.replaceWithInner(MsgRange, ArgRange);
254    commit.insertWrap("@[", ArgRange, "]");
255    return true;
256  }
257
258  if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects)) {
259    if (Msg->getNumArgs() == 0)
260      return false;
261    const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1);
262    if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
263      return false;
264
265    for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i)
266      objectifyExpr(Msg->getArg(i), commit);
267
268    if (Msg->getNumArgs() == 1) {
269      commit.replace(MsgRange, "@[]");
270      return true;
271    }
272    SourceRange ArgRange(Msg->getArg(0)->getLocStart(),
273                         Msg->getArg(Msg->getNumArgs()-2)->getLocEnd());
274    commit.replaceWithInner(MsgRange, ArgRange);
275    commit.insertWrap("@[", ArgRange, "]");
276    return true;
277  }
278
279  return false;
280}
281
282//===----------------------------------------------------------------------===//
283// rewriteToDictionaryLiteral.
284//===----------------------------------------------------------------------===//
285
286static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
287                                       const NSAPI &NS, Commit &commit) {
288  Selector Sel = Msg->getSelector();
289  SourceRange MsgRange = Msg->getSourceRange();
290
291  if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_dictionary)) {
292    if (Msg->getNumArgs() != 0)
293      return false;
294    commit.replace(MsgRange, "@{}");
295    return true;
296  }
297
298  if (Sel == NS.getNSDictionarySelector(
299                                    NSAPI::NSDict_dictionaryWithObjectForKey)) {
300    if (Msg->getNumArgs() != 2)
301      return false;
302
303    objectifyExpr(Msg->getArg(0), commit);
304    objectifyExpr(Msg->getArg(1), commit);
305
306    SourceRange ValRange = Msg->getArg(0)->getSourceRange();
307    SourceRange KeyRange = Msg->getArg(1)->getSourceRange();
308    // Insert key before the value.
309    commit.insertBefore(ValRange.getBegin(), ": ");
310    commit.insertFromRange(ValRange.getBegin(),
311                           CharSourceRange::getTokenRange(KeyRange),
312                       /*afterToken=*/false, /*beforePreviousInsertions=*/true);
313    commit.insertBefore(ValRange.getBegin(), "@{");
314    commit.insertAfterToken(ValRange.getEnd(), "}");
315    commit.replaceWithInner(MsgRange, ValRange);
316    return true;
317  }
318
319  if (Sel == NS.getNSDictionarySelector(
320                                  NSAPI::NSDict_dictionaryWithObjectsAndKeys)) {
321    if (Msg->getNumArgs() % 2 != 1)
322      return false;
323    unsigned SentinelIdx = Msg->getNumArgs() - 1;
324    const Expr *SentinelExpr = Msg->getArg(SentinelIdx);
325    if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
326      return false;
327
328    if (Msg->getNumArgs() == 1) {
329      commit.replace(MsgRange, "@{}");
330      return true;
331    }
332
333    for (unsigned i = 0; i < SentinelIdx; i += 2) {
334      objectifyExpr(Msg->getArg(i), commit);
335      objectifyExpr(Msg->getArg(i+1), commit);
336
337      SourceRange ValRange = Msg->getArg(i)->getSourceRange();
338      SourceRange KeyRange = Msg->getArg(i+1)->getSourceRange();
339      // Insert value after key.
340      commit.insertAfterToken(KeyRange.getEnd(), ": ");
341      commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true);
342      commit.remove(CharSourceRange::getCharRange(ValRange.getBegin(),
343                                                  KeyRange.getBegin()));
344    }
345    // Range of arguments up until and including the last key.
346    // The sentinel and first value are cut off, the value will move after the
347    // key.
348    SourceRange ArgRange(Msg->getArg(1)->getLocStart(),
349                         Msg->getArg(SentinelIdx-1)->getLocEnd());
350    commit.insertWrap("@{", ArgRange, "}");
351    commit.replaceWithInner(MsgRange, ArgRange);
352    return true;
353  }
354
355  return false;
356}
357
358//===----------------------------------------------------------------------===//
359// rewriteToNumberLiteral.
360//===----------------------------------------------------------------------===//
361
362static bool rewriteToCharLiteral(const ObjCMessageExpr *Msg,
363                                   const CharacterLiteral *Arg,
364                                   const NSAPI &NS, Commit &commit) {
365  if (Arg->getKind() != CharacterLiteral::Ascii)
366    return false;
367  if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithChar,
368                                   Msg->getSelector())) {
369    SourceRange ArgRange = Arg->getSourceRange();
370    commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
371    commit.insert(ArgRange.getBegin(), "@");
372    return true;
373  }
374
375  return false;
376}
377
378static bool rewriteToBoolLiteral(const ObjCMessageExpr *Msg,
379                                   const Expr *Arg,
380                                   const NSAPI &NS, Commit &commit) {
381  if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithBool,
382                                   Msg->getSelector())) {
383    SourceRange ArgRange = Arg->getSourceRange();
384    commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
385    commit.insert(ArgRange.getBegin(), "@");
386    return true;
387  }
388
389  return false;
390}
391
392namespace {
393
394struct LiteralInfo {
395  bool Hex, Octal;
396  StringRef U, F, L, LL;
397  CharSourceRange WithoutSuffRange;
398};
399
400}
401
402static bool getLiteralInfo(SourceRange literalRange,
403                           bool isFloat, bool isIntZero,
404                          ASTContext &Ctx, LiteralInfo &Info) {
405  if (literalRange.getBegin().isMacroID() ||
406      literalRange.getEnd().isMacroID())
407    return false;
408  StringRef text = Lexer::getSourceText(
409                                  CharSourceRange::getTokenRange(literalRange),
410                                  Ctx.getSourceManager(), Ctx.getLangOpts());
411  if (text.empty())
412    return false;
413
414  llvm::Optional<bool> UpperU, UpperL;
415  bool UpperF = false;
416
417  struct Suff {
418    static bool has(StringRef suff, StringRef &text) {
419      if (text.endswith(suff)) {
420        text = text.substr(0, text.size()-suff.size());
421        return true;
422      }
423      return false;
424    }
425  };
426
427  while (1) {
428    if (Suff::has("u", text)) {
429      UpperU = false;
430    } else if (Suff::has("U", text)) {
431      UpperU = true;
432    } else if (Suff::has("ll", text)) {
433      UpperL = false;
434    } else if (Suff::has("LL", text)) {
435      UpperL = true;
436    } else if (Suff::has("l", text)) {
437      UpperL = false;
438    } else if (Suff::has("L", text)) {
439      UpperL = true;
440    } else if (isFloat && Suff::has("f", text)) {
441      UpperF = false;
442    } else if (isFloat && Suff::has("F", text)) {
443      UpperF = true;
444    } else
445      break;
446  }
447
448  if (!UpperU.hasValue() && !UpperL.hasValue())
449    UpperU = UpperL = true;
450  else if (UpperU.hasValue() && !UpperL.hasValue())
451    UpperL = UpperU;
452  else if (UpperL.hasValue() && !UpperU.hasValue())
453    UpperU = UpperL;
454
455  Info.U = *UpperU ? "U" : "u";
456  Info.L = *UpperL ? "L" : "l";
457  Info.LL = *UpperL ? "LL" : "ll";
458  Info.F = UpperF ? "F" : "f";
459
460  Info.Hex = Info.Octal = false;
461  if (text.startswith("0x"))
462    Info.Hex = true;
463  else if (!isFloat && !isIntZero && text.startswith("0"))
464    Info.Octal = true;
465
466  SourceLocation B = literalRange.getBegin();
467  Info.WithoutSuffRange =
468      CharSourceRange::getCharRange(B, B.getLocWithOffset(text.size()));
469  return true;
470}
471
472static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
473                                   const NSAPI &NS, Commit &commit) {
474  if (Msg->getNumArgs() != 1)
475    return false;
476
477  const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts();
478  if (const CharacterLiteral *CharE = dyn_cast<CharacterLiteral>(Arg))
479    return rewriteToCharLiteral(Msg, CharE, NS, commit);
480  if (const ObjCBoolLiteralExpr *BE = dyn_cast<ObjCBoolLiteralExpr>(Arg))
481    return rewriteToBoolLiteral(Msg, BE, NS, commit);
482  if (const CXXBoolLiteralExpr *BE = dyn_cast<CXXBoolLiteralExpr>(Arg))
483    return rewriteToBoolLiteral(Msg, BE, NS, commit);
484
485  const Expr *literalE = Arg;
486  if (const UnaryOperator *UOE = dyn_cast<UnaryOperator>(literalE)) {
487    if (UOE->getOpcode() == UO_Plus || UOE->getOpcode() == UO_Minus)
488      literalE = UOE->getSubExpr();
489  }
490
491  // Only integer and floating literals; non-literals or imaginary literal
492  // cannot be rewritten.
493  if (!isa<IntegerLiteral>(literalE) && !isa<FloatingLiteral>(literalE))
494    return false;
495
496  ASTContext &Ctx = NS.getASTContext();
497  Selector Sel = Msg->getSelector();
498  llvm::Optional<NSAPI::NSNumberLiteralMethodKind>
499    MKOpt = NS.getNSNumberLiteralMethodKind(Sel);
500  if (!MKOpt)
501    return false;
502  NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;
503
504  bool CallIsUnsigned = false, CallIsLong = false, CallIsLongLong = false;
505  bool CallIsFloating = false, CallIsDouble = false;
506
507  switch (MK) {
508  // We cannot have these calls with int/float literals.
509  case NSAPI::NSNumberWithChar:
510  case NSAPI::NSNumberWithUnsignedChar:
511  case NSAPI::NSNumberWithShort:
512  case NSAPI::NSNumberWithUnsignedShort:
513  case NSAPI::NSNumberWithBool:
514    return false;
515
516  case NSAPI::NSNumberWithUnsignedInt:
517  case NSAPI::NSNumberWithUnsignedInteger:
518    CallIsUnsigned = true;
519  case NSAPI::NSNumberWithInt:
520  case NSAPI::NSNumberWithInteger:
521    break;
522
523  case NSAPI::NSNumberWithUnsignedLong:
524    CallIsUnsigned = true;
525  case NSAPI::NSNumberWithLong:
526    CallIsLong = true;
527    break;
528
529  case NSAPI::NSNumberWithUnsignedLongLong:
530    CallIsUnsigned = true;
531  case NSAPI::NSNumberWithLongLong:
532    CallIsLongLong = true;
533    break;
534
535  case NSAPI::NSNumberWithDouble:
536    CallIsDouble = true;
537  case NSAPI::NSNumberWithFloat:
538    CallIsFloating = true;
539    break;
540  }
541
542  SourceRange ArgRange = Arg->getSourceRange();
543  QualType ArgTy = Arg->getType();
544  QualType CallTy = Msg->getArg(0)->getType();
545
546  // Check for the easy case, the literal maps directly to the call.
547  if (Ctx.hasSameType(ArgTy, CallTy)) {
548    commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
549    commit.insert(ArgRange.getBegin(), "@");
550    return true;
551  }
552
553  // We will need to modify the literal suffix to get the same type as the call.
554  // Don't even try if it came from a macro.
555  if (ArgRange.getBegin().isMacroID())
556    return false;
557
558  bool LitIsFloat = ArgTy->isFloatingType();
559  // For a float passed to integer call, don't try rewriting. It is difficult
560  // and a very uncommon case anyway.
561  if (LitIsFloat && !CallIsFloating)
562    return false;
563
564  // Try to modify the literal make it the same type as the method call.
565  // -Modify the suffix, and/or
566  // -Change integer to float
567
568  LiteralInfo LitInfo;
569  bool isIntZero = false;
570  if (const IntegerLiteral *IntE = dyn_cast<IntegerLiteral>(literalE))
571    isIntZero = !IntE->getValue().getBoolValue();
572  if (!getLiteralInfo(ArgRange, LitIsFloat, isIntZero, Ctx, LitInfo))
573    return false;
574
575  // Not easy to do int -> float with hex/octal and uncommon anyway.
576  if (!LitIsFloat && CallIsFloating && (LitInfo.Hex || LitInfo.Octal))
577    return false;
578
579  SourceLocation LitB = LitInfo.WithoutSuffRange.getBegin();
580  SourceLocation LitE = LitInfo.WithoutSuffRange.getEnd();
581
582  commit.replaceWithInner(CharSourceRange::getTokenRange(Msg->getSourceRange()),
583                         LitInfo.WithoutSuffRange);
584  commit.insert(LitB, "@");
585
586  if (!LitIsFloat && CallIsFloating)
587    commit.insert(LitE, ".0");
588
589  if (CallIsFloating) {
590    if (!CallIsDouble)
591      commit.insert(LitE, LitInfo.F);
592  } else {
593    if (CallIsUnsigned)
594      commit.insert(LitE, LitInfo.U);
595
596    if (CallIsLong)
597      commit.insert(LitE, LitInfo.L);
598    else if (CallIsLongLong)
599      commit.insert(LitE, LitInfo.LL);
600  }
601  return true;
602}
603
604// FIXME: Make determination of operator precedence more general and
605// make it broadly available.
606static bool subscriptOperatorNeedsParens(const Expr *FullExpr) {
607  const Expr* Expr = FullExpr->IgnoreImpCasts();
608  if (isa<ArraySubscriptExpr>(Expr) ||
609      isa<CallExpr>(Expr) ||
610      isa<DeclRefExpr>(Expr) ||
611      isa<CXXNamedCastExpr>(Expr) ||
612      isa<CXXConstructExpr>(Expr) ||
613      isa<CXXThisExpr>(Expr) ||
614      isa<CXXTypeidExpr>(Expr) ||
615      isa<CXXUnresolvedConstructExpr>(Expr) ||
616      isa<ObjCMessageExpr>(Expr) ||
617      isa<ObjCPropertyRefExpr>(Expr) ||
618      isa<ObjCProtocolExpr>(Expr) ||
619      isa<MemberExpr>(Expr) ||
620      isa<ParenExpr>(FullExpr) ||
621      isa<ParenListExpr>(Expr) ||
622      isa<SizeOfPackExpr>(Expr))
623    return false;
624
625  return true;
626}
627static bool castOperatorNeedsParens(const Expr *FullExpr) {
628  const Expr* Expr = FullExpr->IgnoreImpCasts();
629  if (isa<ArraySubscriptExpr>(Expr) ||
630      isa<CallExpr>(Expr) ||
631      isa<DeclRefExpr>(Expr) ||
632      isa<CastExpr>(Expr) ||
633      isa<CXXNewExpr>(Expr) ||
634      isa<CXXConstructExpr>(Expr) ||
635      isa<CXXDeleteExpr>(Expr) ||
636      isa<CXXNoexceptExpr>(Expr) ||
637      isa<CXXPseudoDestructorExpr>(Expr) ||
638      isa<CXXScalarValueInitExpr>(Expr) ||
639      isa<CXXThisExpr>(Expr) ||
640      isa<CXXTypeidExpr>(Expr) ||
641      isa<CXXUnresolvedConstructExpr>(Expr) ||
642      isa<ObjCMessageExpr>(Expr) ||
643      isa<ObjCPropertyRefExpr>(Expr) ||
644      isa<ObjCProtocolExpr>(Expr) ||
645      isa<MemberExpr>(Expr) ||
646      isa<ParenExpr>(FullExpr) ||
647      isa<ParenListExpr>(Expr) ||
648      isa<SizeOfPackExpr>(Expr) ||
649      isa<UnaryOperator>(Expr))
650    return false;
651
652  return true;
653}
654
655static void objectifyExpr(const Expr *E, Commit &commit) {
656  if (!E) return;
657
658  QualType T = E->getType();
659  if (T->isObjCObjectPointerType()) {
660    if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) {
661      if (ICE->getCastKind() != CK_CPointerToObjCPointerCast)
662        return;
663    } else {
664      return;
665    }
666  } else if (!T->isPointerType()) {
667    return;
668  }
669
670  SourceRange Range = E->getSourceRange();
671  if (castOperatorNeedsParens(E))
672    commit.insertWrap("(", Range, ")");
673  commit.insertBefore(Range.getBegin(), "(id)");
674}
675