xref: /freebsd/contrib/llvm-project/clang/lib/Edit/RewriteObjCFoundationAPI.cpp (revision 0b57cec536236d46e3dba9bd041533462f33dbb7)
1*0b57cec5SDimitry Andric //===--- RewriteObjCFoundationAPI.cpp - Foundation API Rewriter -----------===//
2*0b57cec5SDimitry Andric //
3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*0b57cec5SDimitry Andric //
7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
8*0b57cec5SDimitry Andric //
9*0b57cec5SDimitry Andric // Rewrites legacy method calls to modern syntax.
10*0b57cec5SDimitry Andric //
11*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
12*0b57cec5SDimitry Andric 
13*0b57cec5SDimitry Andric #include "clang/Edit/Rewriters.h"
14*0b57cec5SDimitry Andric #include "clang/AST/ASTContext.h"
15*0b57cec5SDimitry Andric #include "clang/AST/ExprCXX.h"
16*0b57cec5SDimitry Andric #include "clang/AST/ExprObjC.h"
17*0b57cec5SDimitry Andric #include "clang/AST/NSAPI.h"
18*0b57cec5SDimitry Andric #include "clang/AST/ParentMap.h"
19*0b57cec5SDimitry Andric #include "clang/Edit/Commit.h"
20*0b57cec5SDimitry Andric #include "clang/Lex/Lexer.h"
21*0b57cec5SDimitry Andric 
22*0b57cec5SDimitry Andric using namespace clang;
23*0b57cec5SDimitry Andric using namespace edit;
24*0b57cec5SDimitry Andric 
25*0b57cec5SDimitry Andric static bool checkForLiteralCreation(const ObjCMessageExpr *Msg,
26*0b57cec5SDimitry Andric                                     IdentifierInfo *&ClassId,
27*0b57cec5SDimitry Andric                                     const LangOptions &LangOpts) {
28*0b57cec5SDimitry Andric   if (!Msg || Msg->isImplicit() || !Msg->getMethodDecl())
29*0b57cec5SDimitry Andric     return false;
30*0b57cec5SDimitry Andric 
31*0b57cec5SDimitry Andric   const ObjCInterfaceDecl *Receiver = Msg->getReceiverInterface();
32*0b57cec5SDimitry Andric   if (!Receiver)
33*0b57cec5SDimitry Andric     return false;
34*0b57cec5SDimitry Andric   ClassId = Receiver->getIdentifier();
35*0b57cec5SDimitry Andric 
36*0b57cec5SDimitry Andric   if (Msg->getReceiverKind() == ObjCMessageExpr::Class)
37*0b57cec5SDimitry Andric     return true;
38*0b57cec5SDimitry Andric 
39*0b57cec5SDimitry Andric   // When in ARC mode we also convert "[[.. alloc] init]" messages to literals,
40*0b57cec5SDimitry Andric   // since the change from +1 to +0 will be handled fine by ARC.
41*0b57cec5SDimitry Andric   if (LangOpts.ObjCAutoRefCount) {
42*0b57cec5SDimitry Andric     if (Msg->getReceiverKind() == ObjCMessageExpr::Instance) {
43*0b57cec5SDimitry Andric       if (const ObjCMessageExpr *Rec = dyn_cast<ObjCMessageExpr>(
44*0b57cec5SDimitry Andric                            Msg->getInstanceReceiver()->IgnoreParenImpCasts())) {
45*0b57cec5SDimitry Andric         if (Rec->getMethodFamily() == OMF_alloc)
46*0b57cec5SDimitry Andric           return true;
47*0b57cec5SDimitry Andric       }
48*0b57cec5SDimitry Andric     }
49*0b57cec5SDimitry Andric   }
50*0b57cec5SDimitry Andric 
51*0b57cec5SDimitry Andric   return false;
52*0b57cec5SDimitry Andric }
53*0b57cec5SDimitry Andric 
54*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
55*0b57cec5SDimitry Andric // rewriteObjCRedundantCallWithLiteral.
56*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
57*0b57cec5SDimitry Andric 
58*0b57cec5SDimitry Andric bool edit::rewriteObjCRedundantCallWithLiteral(const ObjCMessageExpr *Msg,
59*0b57cec5SDimitry Andric                                               const NSAPI &NS, Commit &commit) {
60*0b57cec5SDimitry Andric   IdentifierInfo *II = nullptr;
61*0b57cec5SDimitry Andric   if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
62*0b57cec5SDimitry Andric     return false;
63*0b57cec5SDimitry Andric   if (Msg->getNumArgs() != 1)
64*0b57cec5SDimitry Andric     return false;
65*0b57cec5SDimitry Andric 
66*0b57cec5SDimitry Andric   const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts();
67*0b57cec5SDimitry Andric   Selector Sel = Msg->getSelector();
68*0b57cec5SDimitry Andric 
69*0b57cec5SDimitry Andric   if ((isa<ObjCStringLiteral>(Arg) &&
70*0b57cec5SDimitry Andric        NS.getNSClassId(NSAPI::ClassId_NSString) == II &&
71*0b57cec5SDimitry Andric        (NS.getNSStringSelector(NSAPI::NSStr_stringWithString) == Sel ||
72*0b57cec5SDimitry Andric         NS.getNSStringSelector(NSAPI::NSStr_initWithString) == Sel))   ||
73*0b57cec5SDimitry Andric 
74*0b57cec5SDimitry Andric       (isa<ObjCArrayLiteral>(Arg) &&
75*0b57cec5SDimitry Andric        NS.getNSClassId(NSAPI::ClassId_NSArray) == II &&
76*0b57cec5SDimitry Andric        (NS.getNSArraySelector(NSAPI::NSArr_arrayWithArray) == Sel ||
77*0b57cec5SDimitry Andric         NS.getNSArraySelector(NSAPI::NSArr_initWithArray) == Sel))     ||
78*0b57cec5SDimitry Andric 
79*0b57cec5SDimitry Andric       (isa<ObjCDictionaryLiteral>(Arg) &&
80*0b57cec5SDimitry Andric        NS.getNSClassId(NSAPI::ClassId_NSDictionary) == II &&
81*0b57cec5SDimitry Andric        (NS.getNSDictionarySelector(
82*0b57cec5SDimitry Andric                               NSAPI::NSDict_dictionaryWithDictionary) == Sel ||
83*0b57cec5SDimitry Andric         NS.getNSDictionarySelector(NSAPI::NSDict_initWithDictionary) == Sel))) {
84*0b57cec5SDimitry Andric 
85*0b57cec5SDimitry Andric     commit.replaceWithInner(Msg->getSourceRange(),
86*0b57cec5SDimitry Andric                            Msg->getArg(0)->getSourceRange());
87*0b57cec5SDimitry Andric     return true;
88*0b57cec5SDimitry Andric   }
89*0b57cec5SDimitry Andric 
90*0b57cec5SDimitry Andric   return false;
91*0b57cec5SDimitry Andric }
92*0b57cec5SDimitry Andric 
93*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
94*0b57cec5SDimitry Andric // rewriteToObjCSubscriptSyntax.
95*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
96*0b57cec5SDimitry Andric 
97*0b57cec5SDimitry Andric /// Check for classes that accept 'objectForKey:' (or the other selectors
98*0b57cec5SDimitry Andric /// that the migrator handles) but return their instances as 'id', resulting
99*0b57cec5SDimitry Andric /// in the compiler resolving 'objectForKey:' as the method from NSDictionary.
100*0b57cec5SDimitry Andric ///
101*0b57cec5SDimitry Andric /// When checking if we can convert to subscripting syntax, check whether
102*0b57cec5SDimitry Andric /// the receiver is a result of a class method from a hardcoded list of
103*0b57cec5SDimitry Andric /// such classes. In such a case return the specific class as the interface
104*0b57cec5SDimitry Andric /// of the receiver.
105*0b57cec5SDimitry Andric ///
106*0b57cec5SDimitry Andric /// FIXME: Remove this when these classes start using 'instancetype'.
107*0b57cec5SDimitry Andric static const ObjCInterfaceDecl *
108*0b57cec5SDimitry Andric maybeAdjustInterfaceForSubscriptingCheck(const ObjCInterfaceDecl *IFace,
109*0b57cec5SDimitry Andric                                          const Expr *Receiver,
110*0b57cec5SDimitry Andric                                          ASTContext &Ctx) {
111*0b57cec5SDimitry Andric   assert(IFace && Receiver);
112*0b57cec5SDimitry Andric 
113*0b57cec5SDimitry Andric   // If the receiver has type 'id'...
114*0b57cec5SDimitry Andric   if (!Ctx.isObjCIdType(Receiver->getType().getUnqualifiedType()))
115*0b57cec5SDimitry Andric     return IFace;
116*0b57cec5SDimitry Andric 
117*0b57cec5SDimitry Andric   const ObjCMessageExpr *
118*0b57cec5SDimitry Andric     InnerMsg = dyn_cast<ObjCMessageExpr>(Receiver->IgnoreParenCasts());
119*0b57cec5SDimitry Andric   if (!InnerMsg)
120*0b57cec5SDimitry Andric     return IFace;
121*0b57cec5SDimitry Andric 
122*0b57cec5SDimitry Andric   QualType ClassRec;
123*0b57cec5SDimitry Andric   switch (InnerMsg->getReceiverKind()) {
124*0b57cec5SDimitry Andric   case ObjCMessageExpr::Instance:
125*0b57cec5SDimitry Andric   case ObjCMessageExpr::SuperInstance:
126*0b57cec5SDimitry Andric     return IFace;
127*0b57cec5SDimitry Andric 
128*0b57cec5SDimitry Andric   case ObjCMessageExpr::Class:
129*0b57cec5SDimitry Andric     ClassRec = InnerMsg->getClassReceiver();
130*0b57cec5SDimitry Andric     break;
131*0b57cec5SDimitry Andric   case ObjCMessageExpr::SuperClass:
132*0b57cec5SDimitry Andric     ClassRec = InnerMsg->getSuperType();
133*0b57cec5SDimitry Andric     break;
134*0b57cec5SDimitry Andric   }
135*0b57cec5SDimitry Andric 
136*0b57cec5SDimitry Andric   if (ClassRec.isNull())
137*0b57cec5SDimitry Andric     return IFace;
138*0b57cec5SDimitry Andric 
139*0b57cec5SDimitry Andric   // ...and it is the result of a class message...
140*0b57cec5SDimitry Andric 
141*0b57cec5SDimitry Andric   const ObjCObjectType *ObjTy = ClassRec->getAs<ObjCObjectType>();
142*0b57cec5SDimitry Andric   if (!ObjTy)
143*0b57cec5SDimitry Andric     return IFace;
144*0b57cec5SDimitry Andric   const ObjCInterfaceDecl *OID = ObjTy->getInterface();
145*0b57cec5SDimitry Andric 
146*0b57cec5SDimitry Andric   // ...and the receiving class is NSMapTable or NSLocale, return that
147*0b57cec5SDimitry Andric   // class as the receiving interface.
148*0b57cec5SDimitry Andric   if (OID->getName() == "NSMapTable" ||
149*0b57cec5SDimitry Andric       OID->getName() == "NSLocale")
150*0b57cec5SDimitry Andric     return OID;
151*0b57cec5SDimitry Andric 
152*0b57cec5SDimitry Andric   return IFace;
153*0b57cec5SDimitry Andric }
154*0b57cec5SDimitry Andric 
155*0b57cec5SDimitry Andric static bool canRewriteToSubscriptSyntax(const ObjCInterfaceDecl *&IFace,
156*0b57cec5SDimitry Andric                                         const ObjCMessageExpr *Msg,
157*0b57cec5SDimitry Andric                                         ASTContext &Ctx,
158*0b57cec5SDimitry Andric                                         Selector subscriptSel) {
159*0b57cec5SDimitry Andric   const Expr *Rec = Msg->getInstanceReceiver();
160*0b57cec5SDimitry Andric   if (!Rec)
161*0b57cec5SDimitry Andric     return false;
162*0b57cec5SDimitry Andric   IFace = maybeAdjustInterfaceForSubscriptingCheck(IFace, Rec, Ctx);
163*0b57cec5SDimitry Andric 
164*0b57cec5SDimitry Andric   if (const ObjCMethodDecl *MD = IFace->lookupInstanceMethod(subscriptSel)) {
165*0b57cec5SDimitry Andric     if (!MD->isUnavailable())
166*0b57cec5SDimitry Andric       return true;
167*0b57cec5SDimitry Andric   }
168*0b57cec5SDimitry Andric   return false;
169*0b57cec5SDimitry Andric }
170*0b57cec5SDimitry Andric 
171*0b57cec5SDimitry Andric static bool subscriptOperatorNeedsParens(const Expr *FullExpr);
172*0b57cec5SDimitry Andric 
173*0b57cec5SDimitry Andric static void maybePutParensOnReceiver(const Expr *Receiver, Commit &commit) {
174*0b57cec5SDimitry Andric   if (subscriptOperatorNeedsParens(Receiver)) {
175*0b57cec5SDimitry Andric     SourceRange RecRange = Receiver->getSourceRange();
176*0b57cec5SDimitry Andric     commit.insertWrap("(", RecRange, ")");
177*0b57cec5SDimitry Andric   }
178*0b57cec5SDimitry Andric }
179*0b57cec5SDimitry Andric 
180*0b57cec5SDimitry Andric static bool rewriteToSubscriptGetCommon(const ObjCMessageExpr *Msg,
181*0b57cec5SDimitry Andric                                         Commit &commit) {
182*0b57cec5SDimitry Andric   if (Msg->getNumArgs() != 1)
183*0b57cec5SDimitry Andric     return false;
184*0b57cec5SDimitry Andric   const Expr *Rec = Msg->getInstanceReceiver();
185*0b57cec5SDimitry Andric   if (!Rec)
186*0b57cec5SDimitry Andric     return false;
187*0b57cec5SDimitry Andric 
188*0b57cec5SDimitry Andric   SourceRange MsgRange = Msg->getSourceRange();
189*0b57cec5SDimitry Andric   SourceRange RecRange = Rec->getSourceRange();
190*0b57cec5SDimitry Andric   SourceRange ArgRange = Msg->getArg(0)->getSourceRange();
191*0b57cec5SDimitry Andric 
192*0b57cec5SDimitry Andric   commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
193*0b57cec5SDimitry Andric                                                        ArgRange.getBegin()),
194*0b57cec5SDimitry Andric                          CharSourceRange::getTokenRange(RecRange));
195*0b57cec5SDimitry Andric   commit.replaceWithInner(SourceRange(ArgRange.getBegin(), MsgRange.getEnd()),
196*0b57cec5SDimitry Andric                          ArgRange);
197*0b57cec5SDimitry Andric   commit.insertWrap("[", ArgRange, "]");
198*0b57cec5SDimitry Andric   maybePutParensOnReceiver(Rec, commit);
199*0b57cec5SDimitry Andric   return true;
200*0b57cec5SDimitry Andric }
201*0b57cec5SDimitry Andric 
202*0b57cec5SDimitry Andric static bool rewriteToArraySubscriptGet(const ObjCInterfaceDecl *IFace,
203*0b57cec5SDimitry Andric                                        const ObjCMessageExpr *Msg,
204*0b57cec5SDimitry Andric                                        const NSAPI &NS,
205*0b57cec5SDimitry Andric                                        Commit &commit) {
206*0b57cec5SDimitry Andric   if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
207*0b57cec5SDimitry Andric                                    NS.getObjectAtIndexedSubscriptSelector()))
208*0b57cec5SDimitry Andric     return false;
209*0b57cec5SDimitry Andric   return rewriteToSubscriptGetCommon(Msg, commit);
210*0b57cec5SDimitry Andric }
211*0b57cec5SDimitry Andric 
212*0b57cec5SDimitry Andric static bool rewriteToDictionarySubscriptGet(const ObjCInterfaceDecl *IFace,
213*0b57cec5SDimitry Andric                                             const ObjCMessageExpr *Msg,
214*0b57cec5SDimitry Andric                                             const NSAPI &NS,
215*0b57cec5SDimitry Andric                                             Commit &commit) {
216*0b57cec5SDimitry Andric   if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
217*0b57cec5SDimitry Andric                                   NS.getObjectForKeyedSubscriptSelector()))
218*0b57cec5SDimitry Andric     return false;
219*0b57cec5SDimitry Andric   return rewriteToSubscriptGetCommon(Msg, commit);
220*0b57cec5SDimitry Andric }
221*0b57cec5SDimitry Andric 
222*0b57cec5SDimitry Andric static bool rewriteToArraySubscriptSet(const ObjCInterfaceDecl *IFace,
223*0b57cec5SDimitry Andric                                        const ObjCMessageExpr *Msg,
224*0b57cec5SDimitry Andric                                        const NSAPI &NS,
225*0b57cec5SDimitry Andric                                        Commit &commit) {
226*0b57cec5SDimitry Andric   if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
227*0b57cec5SDimitry Andric                                    NS.getSetObjectAtIndexedSubscriptSelector()))
228*0b57cec5SDimitry Andric     return false;
229*0b57cec5SDimitry Andric 
230*0b57cec5SDimitry Andric   if (Msg->getNumArgs() != 2)
231*0b57cec5SDimitry Andric     return false;
232*0b57cec5SDimitry Andric   const Expr *Rec = Msg->getInstanceReceiver();
233*0b57cec5SDimitry Andric   if (!Rec)
234*0b57cec5SDimitry Andric     return false;
235*0b57cec5SDimitry Andric 
236*0b57cec5SDimitry Andric   SourceRange MsgRange = Msg->getSourceRange();
237*0b57cec5SDimitry Andric   SourceRange RecRange = Rec->getSourceRange();
238*0b57cec5SDimitry Andric   SourceRange Arg0Range = Msg->getArg(0)->getSourceRange();
239*0b57cec5SDimitry Andric   SourceRange Arg1Range = Msg->getArg(1)->getSourceRange();
240*0b57cec5SDimitry Andric 
241*0b57cec5SDimitry Andric   commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
242*0b57cec5SDimitry Andric                                                        Arg0Range.getBegin()),
243*0b57cec5SDimitry Andric                          CharSourceRange::getTokenRange(RecRange));
244*0b57cec5SDimitry Andric   commit.replaceWithInner(CharSourceRange::getCharRange(Arg0Range.getBegin(),
245*0b57cec5SDimitry Andric                                                        Arg1Range.getBegin()),
246*0b57cec5SDimitry Andric                          CharSourceRange::getTokenRange(Arg0Range));
247*0b57cec5SDimitry Andric   commit.replaceWithInner(SourceRange(Arg1Range.getBegin(), MsgRange.getEnd()),
248*0b57cec5SDimitry Andric                          Arg1Range);
249*0b57cec5SDimitry Andric   commit.insertWrap("[", CharSourceRange::getCharRange(Arg0Range.getBegin(),
250*0b57cec5SDimitry Andric                                                        Arg1Range.getBegin()),
251*0b57cec5SDimitry Andric                     "] = ");
252*0b57cec5SDimitry Andric   maybePutParensOnReceiver(Rec, commit);
253*0b57cec5SDimitry Andric   return true;
254*0b57cec5SDimitry Andric }
255*0b57cec5SDimitry Andric 
256*0b57cec5SDimitry Andric static bool rewriteToDictionarySubscriptSet(const ObjCInterfaceDecl *IFace,
257*0b57cec5SDimitry Andric                                             const ObjCMessageExpr *Msg,
258*0b57cec5SDimitry Andric                                             const NSAPI &NS,
259*0b57cec5SDimitry Andric                                             Commit &commit) {
260*0b57cec5SDimitry Andric   if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
261*0b57cec5SDimitry Andric                                    NS.getSetObjectForKeyedSubscriptSelector()))
262*0b57cec5SDimitry Andric     return false;
263*0b57cec5SDimitry Andric 
264*0b57cec5SDimitry Andric   if (Msg->getNumArgs() != 2)
265*0b57cec5SDimitry Andric     return false;
266*0b57cec5SDimitry Andric   const Expr *Rec = Msg->getInstanceReceiver();
267*0b57cec5SDimitry Andric   if (!Rec)
268*0b57cec5SDimitry Andric     return false;
269*0b57cec5SDimitry Andric 
270*0b57cec5SDimitry Andric   SourceRange MsgRange = Msg->getSourceRange();
271*0b57cec5SDimitry Andric   SourceRange RecRange = Rec->getSourceRange();
272*0b57cec5SDimitry Andric   SourceRange Arg0Range = Msg->getArg(0)->getSourceRange();
273*0b57cec5SDimitry Andric   SourceRange Arg1Range = Msg->getArg(1)->getSourceRange();
274*0b57cec5SDimitry Andric 
275*0b57cec5SDimitry Andric   SourceLocation LocBeforeVal = Arg0Range.getBegin();
276*0b57cec5SDimitry Andric   commit.insertBefore(LocBeforeVal, "] = ");
277*0b57cec5SDimitry Andric   commit.insertFromRange(LocBeforeVal, Arg1Range, /*afterToken=*/false,
278*0b57cec5SDimitry Andric                          /*beforePreviousInsertions=*/true);
279*0b57cec5SDimitry Andric   commit.insertBefore(LocBeforeVal, "[");
280*0b57cec5SDimitry Andric   commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
281*0b57cec5SDimitry Andric                                                        Arg0Range.getBegin()),
282*0b57cec5SDimitry Andric                          CharSourceRange::getTokenRange(RecRange));
283*0b57cec5SDimitry Andric   commit.replaceWithInner(SourceRange(Arg0Range.getBegin(), MsgRange.getEnd()),
284*0b57cec5SDimitry Andric                          Arg0Range);
285*0b57cec5SDimitry Andric   maybePutParensOnReceiver(Rec, commit);
286*0b57cec5SDimitry Andric   return true;
287*0b57cec5SDimitry Andric }
288*0b57cec5SDimitry Andric 
289*0b57cec5SDimitry Andric bool edit::rewriteToObjCSubscriptSyntax(const ObjCMessageExpr *Msg,
290*0b57cec5SDimitry Andric                                         const NSAPI &NS, Commit &commit) {
291*0b57cec5SDimitry Andric   if (!Msg || Msg->isImplicit() ||
292*0b57cec5SDimitry Andric       Msg->getReceiverKind() != ObjCMessageExpr::Instance)
293*0b57cec5SDimitry Andric     return false;
294*0b57cec5SDimitry Andric   const ObjCMethodDecl *Method = Msg->getMethodDecl();
295*0b57cec5SDimitry Andric   if (!Method)
296*0b57cec5SDimitry Andric     return false;
297*0b57cec5SDimitry Andric 
298*0b57cec5SDimitry Andric   const ObjCInterfaceDecl *IFace =
299*0b57cec5SDimitry Andric       NS.getASTContext().getObjContainingInterface(Method);
300*0b57cec5SDimitry Andric   if (!IFace)
301*0b57cec5SDimitry Andric     return false;
302*0b57cec5SDimitry Andric   Selector Sel = Msg->getSelector();
303*0b57cec5SDimitry Andric 
304*0b57cec5SDimitry Andric   if (Sel == NS.getNSArraySelector(NSAPI::NSArr_objectAtIndex))
305*0b57cec5SDimitry Andric     return rewriteToArraySubscriptGet(IFace, Msg, NS, commit);
306*0b57cec5SDimitry Andric 
307*0b57cec5SDimitry Andric   if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_objectForKey))
308*0b57cec5SDimitry Andric     return rewriteToDictionarySubscriptGet(IFace, Msg, NS, commit);
309*0b57cec5SDimitry Andric 
310*0b57cec5SDimitry Andric   if (Msg->getNumArgs() != 2)
311*0b57cec5SDimitry Andric     return false;
312*0b57cec5SDimitry Andric 
313*0b57cec5SDimitry Andric   if (Sel == NS.getNSArraySelector(NSAPI::NSMutableArr_replaceObjectAtIndex))
314*0b57cec5SDimitry Andric     return rewriteToArraySubscriptSet(IFace, Msg, NS, commit);
315*0b57cec5SDimitry Andric 
316*0b57cec5SDimitry Andric   if (Sel == NS.getNSDictionarySelector(NSAPI::NSMutableDict_setObjectForKey))
317*0b57cec5SDimitry Andric     return rewriteToDictionarySubscriptSet(IFace, Msg, NS, commit);
318*0b57cec5SDimitry Andric 
319*0b57cec5SDimitry Andric   return false;
320*0b57cec5SDimitry Andric }
321*0b57cec5SDimitry Andric 
322*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
323*0b57cec5SDimitry Andric // rewriteToObjCLiteralSyntax.
324*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
325*0b57cec5SDimitry Andric 
326*0b57cec5SDimitry Andric static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
327*0b57cec5SDimitry Andric                                   const NSAPI &NS, Commit &commit,
328*0b57cec5SDimitry Andric                                   const ParentMap *PMap);
329*0b57cec5SDimitry Andric static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
330*0b57cec5SDimitry Andric                                   const NSAPI &NS, Commit &commit);
331*0b57cec5SDimitry Andric static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
332*0b57cec5SDimitry Andric                                   const NSAPI &NS, Commit &commit);
333*0b57cec5SDimitry Andric static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
334*0b57cec5SDimitry Andric                                             const NSAPI &NS, Commit &commit);
335*0b57cec5SDimitry Andric static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
336*0b57cec5SDimitry Andric                                            const NSAPI &NS, Commit &commit);
337*0b57cec5SDimitry Andric 
338*0b57cec5SDimitry Andric bool edit::rewriteToObjCLiteralSyntax(const ObjCMessageExpr *Msg,
339*0b57cec5SDimitry Andric                                       const NSAPI &NS, Commit &commit,
340*0b57cec5SDimitry Andric                                       const ParentMap *PMap) {
341*0b57cec5SDimitry Andric   IdentifierInfo *II = nullptr;
342*0b57cec5SDimitry Andric   if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
343*0b57cec5SDimitry Andric     return false;
344*0b57cec5SDimitry Andric 
345*0b57cec5SDimitry Andric   if (II == NS.getNSClassId(NSAPI::ClassId_NSArray))
346*0b57cec5SDimitry Andric     return rewriteToArrayLiteral(Msg, NS, commit, PMap);
347*0b57cec5SDimitry Andric   if (II == NS.getNSClassId(NSAPI::ClassId_NSDictionary))
348*0b57cec5SDimitry Andric     return rewriteToDictionaryLiteral(Msg, NS, commit);
349*0b57cec5SDimitry Andric   if (II == NS.getNSClassId(NSAPI::ClassId_NSNumber))
350*0b57cec5SDimitry Andric     return rewriteToNumberLiteral(Msg, NS, commit);
351*0b57cec5SDimitry Andric   if (II == NS.getNSClassId(NSAPI::ClassId_NSString))
352*0b57cec5SDimitry Andric     return rewriteToStringBoxedExpression(Msg, NS, commit);
353*0b57cec5SDimitry Andric 
354*0b57cec5SDimitry Andric   return false;
355*0b57cec5SDimitry Andric }
356*0b57cec5SDimitry Andric 
357*0b57cec5SDimitry Andric /// Returns true if the immediate message arguments of \c Msg should not
358*0b57cec5SDimitry Andric /// be rewritten because it will interfere with the rewrite of the parent
359*0b57cec5SDimitry Andric /// message expression. e.g.
360*0b57cec5SDimitry Andric /// \code
361*0b57cec5SDimitry Andric ///   [NSDictionary dictionaryWithObjects:
362*0b57cec5SDimitry Andric ///                                 [NSArray arrayWithObjects:@"1", @"2", nil]
363*0b57cec5SDimitry Andric ///                         forKeys:[NSArray arrayWithObjects:@"A", @"B", nil]];
364*0b57cec5SDimitry Andric /// \endcode
365*0b57cec5SDimitry Andric /// It will return true for this because we are going to rewrite this directly
366*0b57cec5SDimitry Andric /// to a dictionary literal without any array literals.
367*0b57cec5SDimitry Andric static bool shouldNotRewriteImmediateMessageArgs(const ObjCMessageExpr *Msg,
368*0b57cec5SDimitry Andric                                                  const NSAPI &NS);
369*0b57cec5SDimitry Andric 
370*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
371*0b57cec5SDimitry Andric // rewriteToArrayLiteral.
372*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
373*0b57cec5SDimitry Andric 
374*0b57cec5SDimitry Andric /// Adds an explicit cast to 'id' if the type is not objc object.
375*0b57cec5SDimitry Andric static void objectifyExpr(const Expr *E, Commit &commit);
376*0b57cec5SDimitry Andric 
377*0b57cec5SDimitry Andric static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
378*0b57cec5SDimitry Andric                                   const NSAPI &NS, Commit &commit,
379*0b57cec5SDimitry Andric                                   const ParentMap *PMap) {
380*0b57cec5SDimitry Andric   if (PMap) {
381*0b57cec5SDimitry Andric     const ObjCMessageExpr *ParentMsg =
382*0b57cec5SDimitry Andric         dyn_cast_or_null<ObjCMessageExpr>(PMap->getParentIgnoreParenCasts(Msg));
383*0b57cec5SDimitry Andric     if (shouldNotRewriteImmediateMessageArgs(ParentMsg, NS))
384*0b57cec5SDimitry Andric       return false;
385*0b57cec5SDimitry Andric   }
386*0b57cec5SDimitry Andric 
387*0b57cec5SDimitry Andric   Selector Sel = Msg->getSelector();
388*0b57cec5SDimitry Andric   SourceRange MsgRange = Msg->getSourceRange();
389*0b57cec5SDimitry Andric 
390*0b57cec5SDimitry Andric   if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array)) {
391*0b57cec5SDimitry Andric     if (Msg->getNumArgs() != 0)
392*0b57cec5SDimitry Andric       return false;
393*0b57cec5SDimitry Andric     commit.replace(MsgRange, "@[]");
394*0b57cec5SDimitry Andric     return true;
395*0b57cec5SDimitry Andric   }
396*0b57cec5SDimitry Andric 
397*0b57cec5SDimitry Andric   if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) {
398*0b57cec5SDimitry Andric     if (Msg->getNumArgs() != 1)
399*0b57cec5SDimitry Andric       return false;
400*0b57cec5SDimitry Andric     objectifyExpr(Msg->getArg(0), commit);
401*0b57cec5SDimitry Andric     SourceRange ArgRange = Msg->getArg(0)->getSourceRange();
402*0b57cec5SDimitry Andric     commit.replaceWithInner(MsgRange, ArgRange);
403*0b57cec5SDimitry Andric     commit.insertWrap("@[", ArgRange, "]");
404*0b57cec5SDimitry Andric     return true;
405*0b57cec5SDimitry Andric   }
406*0b57cec5SDimitry Andric 
407*0b57cec5SDimitry Andric   if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) ||
408*0b57cec5SDimitry Andric       Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) {
409*0b57cec5SDimitry Andric     if (Msg->getNumArgs() == 0)
410*0b57cec5SDimitry Andric       return false;
411*0b57cec5SDimitry Andric     const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1);
412*0b57cec5SDimitry Andric     if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
413*0b57cec5SDimitry Andric       return false;
414*0b57cec5SDimitry Andric 
415*0b57cec5SDimitry Andric     for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i)
416*0b57cec5SDimitry Andric       objectifyExpr(Msg->getArg(i), commit);
417*0b57cec5SDimitry Andric 
418*0b57cec5SDimitry Andric     if (Msg->getNumArgs() == 1) {
419*0b57cec5SDimitry Andric       commit.replace(MsgRange, "@[]");
420*0b57cec5SDimitry Andric       return true;
421*0b57cec5SDimitry Andric     }
422*0b57cec5SDimitry Andric     SourceRange ArgRange(Msg->getArg(0)->getBeginLoc(),
423*0b57cec5SDimitry Andric                          Msg->getArg(Msg->getNumArgs() - 2)->getEndLoc());
424*0b57cec5SDimitry Andric     commit.replaceWithInner(MsgRange, ArgRange);
425*0b57cec5SDimitry Andric     commit.insertWrap("@[", ArgRange, "]");
426*0b57cec5SDimitry Andric     return true;
427*0b57cec5SDimitry Andric   }
428*0b57cec5SDimitry Andric 
429*0b57cec5SDimitry Andric   return false;
430*0b57cec5SDimitry Andric }
431*0b57cec5SDimitry Andric 
432*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
433*0b57cec5SDimitry Andric // rewriteToDictionaryLiteral.
434*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
435*0b57cec5SDimitry Andric 
436*0b57cec5SDimitry Andric /// If \c Msg is an NSArray creation message or literal, this gets the
437*0b57cec5SDimitry Andric /// objects that were used to create it.
438*0b57cec5SDimitry Andric /// \returns true if it is an NSArray and we got objects, or false otherwise.
439*0b57cec5SDimitry Andric static bool getNSArrayObjects(const Expr *E, const NSAPI &NS,
440*0b57cec5SDimitry Andric                               SmallVectorImpl<const Expr *> &Objs) {
441*0b57cec5SDimitry Andric   if (!E)
442*0b57cec5SDimitry Andric     return false;
443*0b57cec5SDimitry Andric 
444*0b57cec5SDimitry Andric   E = E->IgnoreParenCasts();
445*0b57cec5SDimitry Andric   if (!E)
446*0b57cec5SDimitry Andric     return false;
447*0b57cec5SDimitry Andric 
448*0b57cec5SDimitry Andric   if (const ObjCMessageExpr *Msg = dyn_cast<ObjCMessageExpr>(E)) {
449*0b57cec5SDimitry Andric     IdentifierInfo *Cls = nullptr;
450*0b57cec5SDimitry Andric     if (!checkForLiteralCreation(Msg, Cls, NS.getASTContext().getLangOpts()))
451*0b57cec5SDimitry Andric       return false;
452*0b57cec5SDimitry Andric 
453*0b57cec5SDimitry Andric     if (Cls != NS.getNSClassId(NSAPI::ClassId_NSArray))
454*0b57cec5SDimitry Andric       return false;
455*0b57cec5SDimitry Andric 
456*0b57cec5SDimitry Andric     Selector Sel = Msg->getSelector();
457*0b57cec5SDimitry Andric     if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array))
458*0b57cec5SDimitry Andric       return true; // empty array.
459*0b57cec5SDimitry Andric 
460*0b57cec5SDimitry Andric     if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) {
461*0b57cec5SDimitry Andric       if (Msg->getNumArgs() != 1)
462*0b57cec5SDimitry Andric         return false;
463*0b57cec5SDimitry Andric       Objs.push_back(Msg->getArg(0));
464*0b57cec5SDimitry Andric       return true;
465*0b57cec5SDimitry Andric     }
466*0b57cec5SDimitry Andric 
467*0b57cec5SDimitry Andric     if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) ||
468*0b57cec5SDimitry Andric         Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) {
469*0b57cec5SDimitry Andric       if (Msg->getNumArgs() == 0)
470*0b57cec5SDimitry Andric         return false;
471*0b57cec5SDimitry Andric       const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1);
472*0b57cec5SDimitry Andric       if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
473*0b57cec5SDimitry Andric         return false;
474*0b57cec5SDimitry Andric 
475*0b57cec5SDimitry Andric       for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i)
476*0b57cec5SDimitry Andric         Objs.push_back(Msg->getArg(i));
477*0b57cec5SDimitry Andric       return true;
478*0b57cec5SDimitry Andric     }
479*0b57cec5SDimitry Andric 
480*0b57cec5SDimitry Andric   } else if (const ObjCArrayLiteral *ArrLit = dyn_cast<ObjCArrayLiteral>(E)) {
481*0b57cec5SDimitry Andric     for (unsigned i = 0, e = ArrLit->getNumElements(); i != e; ++i)
482*0b57cec5SDimitry Andric       Objs.push_back(ArrLit->getElement(i));
483*0b57cec5SDimitry Andric     return true;
484*0b57cec5SDimitry Andric   }
485*0b57cec5SDimitry Andric 
486*0b57cec5SDimitry Andric   return false;
487*0b57cec5SDimitry Andric }
488*0b57cec5SDimitry Andric 
489*0b57cec5SDimitry Andric static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
490*0b57cec5SDimitry Andric                                        const NSAPI &NS, Commit &commit) {
491*0b57cec5SDimitry Andric   Selector Sel = Msg->getSelector();
492*0b57cec5SDimitry Andric   SourceRange MsgRange = Msg->getSourceRange();
493*0b57cec5SDimitry Andric 
494*0b57cec5SDimitry Andric   if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_dictionary)) {
495*0b57cec5SDimitry Andric     if (Msg->getNumArgs() != 0)
496*0b57cec5SDimitry Andric       return false;
497*0b57cec5SDimitry Andric     commit.replace(MsgRange, "@{}");
498*0b57cec5SDimitry Andric     return true;
499*0b57cec5SDimitry Andric   }
500*0b57cec5SDimitry Andric 
501*0b57cec5SDimitry Andric   if (Sel == NS.getNSDictionarySelector(
502*0b57cec5SDimitry Andric                                     NSAPI::NSDict_dictionaryWithObjectForKey)) {
503*0b57cec5SDimitry Andric     if (Msg->getNumArgs() != 2)
504*0b57cec5SDimitry Andric       return false;
505*0b57cec5SDimitry Andric 
506*0b57cec5SDimitry Andric     objectifyExpr(Msg->getArg(0), commit);
507*0b57cec5SDimitry Andric     objectifyExpr(Msg->getArg(1), commit);
508*0b57cec5SDimitry Andric 
509*0b57cec5SDimitry Andric     SourceRange ValRange = Msg->getArg(0)->getSourceRange();
510*0b57cec5SDimitry Andric     SourceRange KeyRange = Msg->getArg(1)->getSourceRange();
511*0b57cec5SDimitry Andric     // Insert key before the value.
512*0b57cec5SDimitry Andric     commit.insertBefore(ValRange.getBegin(), ": ");
513*0b57cec5SDimitry Andric     commit.insertFromRange(ValRange.getBegin(),
514*0b57cec5SDimitry Andric                            CharSourceRange::getTokenRange(KeyRange),
515*0b57cec5SDimitry Andric                        /*afterToken=*/false, /*beforePreviousInsertions=*/true);
516*0b57cec5SDimitry Andric     commit.insertBefore(ValRange.getBegin(), "@{");
517*0b57cec5SDimitry Andric     commit.insertAfterToken(ValRange.getEnd(), "}");
518*0b57cec5SDimitry Andric     commit.replaceWithInner(MsgRange, ValRange);
519*0b57cec5SDimitry Andric     return true;
520*0b57cec5SDimitry Andric   }
521*0b57cec5SDimitry Andric 
522*0b57cec5SDimitry Andric   if (Sel == NS.getNSDictionarySelector(
523*0b57cec5SDimitry Andric                                   NSAPI::NSDict_dictionaryWithObjectsAndKeys) ||
524*0b57cec5SDimitry Andric       Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsAndKeys)) {
525*0b57cec5SDimitry Andric     if (Msg->getNumArgs() % 2 != 1)
526*0b57cec5SDimitry Andric       return false;
527*0b57cec5SDimitry Andric     unsigned SentinelIdx = Msg->getNumArgs() - 1;
528*0b57cec5SDimitry Andric     const Expr *SentinelExpr = Msg->getArg(SentinelIdx);
529*0b57cec5SDimitry Andric     if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
530*0b57cec5SDimitry Andric       return false;
531*0b57cec5SDimitry Andric 
532*0b57cec5SDimitry Andric     if (Msg->getNumArgs() == 1) {
533*0b57cec5SDimitry Andric       commit.replace(MsgRange, "@{}");
534*0b57cec5SDimitry Andric       return true;
535*0b57cec5SDimitry Andric     }
536*0b57cec5SDimitry Andric 
537*0b57cec5SDimitry Andric     for (unsigned i = 0; i < SentinelIdx; i += 2) {
538*0b57cec5SDimitry Andric       objectifyExpr(Msg->getArg(i), commit);
539*0b57cec5SDimitry Andric       objectifyExpr(Msg->getArg(i+1), commit);
540*0b57cec5SDimitry Andric 
541*0b57cec5SDimitry Andric       SourceRange ValRange = Msg->getArg(i)->getSourceRange();
542*0b57cec5SDimitry Andric       SourceRange KeyRange = Msg->getArg(i+1)->getSourceRange();
543*0b57cec5SDimitry Andric       // Insert value after key.
544*0b57cec5SDimitry Andric       commit.insertAfterToken(KeyRange.getEnd(), ": ");
545*0b57cec5SDimitry Andric       commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true);
546*0b57cec5SDimitry Andric       commit.remove(CharSourceRange::getCharRange(ValRange.getBegin(),
547*0b57cec5SDimitry Andric                                                   KeyRange.getBegin()));
548*0b57cec5SDimitry Andric     }
549*0b57cec5SDimitry Andric     // Range of arguments up until and including the last key.
550*0b57cec5SDimitry Andric     // The sentinel and first value are cut off, the value will move after the
551*0b57cec5SDimitry Andric     // key.
552*0b57cec5SDimitry Andric     SourceRange ArgRange(Msg->getArg(1)->getBeginLoc(),
553*0b57cec5SDimitry Andric                          Msg->getArg(SentinelIdx - 1)->getEndLoc());
554*0b57cec5SDimitry Andric     commit.insertWrap("@{", ArgRange, "}");
555*0b57cec5SDimitry Andric     commit.replaceWithInner(MsgRange, ArgRange);
556*0b57cec5SDimitry Andric     return true;
557*0b57cec5SDimitry Andric   }
558*0b57cec5SDimitry Andric 
559*0b57cec5SDimitry Andric   if (Sel == NS.getNSDictionarySelector(
560*0b57cec5SDimitry Andric                                   NSAPI::NSDict_dictionaryWithObjectsForKeys) ||
561*0b57cec5SDimitry Andric       Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsForKeys)) {
562*0b57cec5SDimitry Andric     if (Msg->getNumArgs() != 2)
563*0b57cec5SDimitry Andric       return false;
564*0b57cec5SDimitry Andric 
565*0b57cec5SDimitry Andric     SmallVector<const Expr *, 8> Vals;
566*0b57cec5SDimitry Andric     if (!getNSArrayObjects(Msg->getArg(0), NS, Vals))
567*0b57cec5SDimitry Andric       return false;
568*0b57cec5SDimitry Andric 
569*0b57cec5SDimitry Andric     SmallVector<const Expr *, 8> Keys;
570*0b57cec5SDimitry Andric     if (!getNSArrayObjects(Msg->getArg(1), NS, Keys))
571*0b57cec5SDimitry Andric       return false;
572*0b57cec5SDimitry Andric 
573*0b57cec5SDimitry Andric     if (Vals.size() != Keys.size())
574*0b57cec5SDimitry Andric       return false;
575*0b57cec5SDimitry Andric 
576*0b57cec5SDimitry Andric     if (Vals.empty()) {
577*0b57cec5SDimitry Andric       commit.replace(MsgRange, "@{}");
578*0b57cec5SDimitry Andric       return true;
579*0b57cec5SDimitry Andric     }
580*0b57cec5SDimitry Andric 
581*0b57cec5SDimitry Andric     for (unsigned i = 0, n = Vals.size(); i < n; ++i) {
582*0b57cec5SDimitry Andric       objectifyExpr(Vals[i], commit);
583*0b57cec5SDimitry Andric       objectifyExpr(Keys[i], commit);
584*0b57cec5SDimitry Andric 
585*0b57cec5SDimitry Andric       SourceRange ValRange = Vals[i]->getSourceRange();
586*0b57cec5SDimitry Andric       SourceRange KeyRange = Keys[i]->getSourceRange();
587*0b57cec5SDimitry Andric       // Insert value after key.
588*0b57cec5SDimitry Andric       commit.insertAfterToken(KeyRange.getEnd(), ": ");
589*0b57cec5SDimitry Andric       commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true);
590*0b57cec5SDimitry Andric     }
591*0b57cec5SDimitry Andric     // Range of arguments up until and including the last key.
592*0b57cec5SDimitry Andric     // The first value is cut off, the value will move after the key.
593*0b57cec5SDimitry Andric     SourceRange ArgRange(Keys.front()->getBeginLoc(), Keys.back()->getEndLoc());
594*0b57cec5SDimitry Andric     commit.insertWrap("@{", ArgRange, "}");
595*0b57cec5SDimitry Andric     commit.replaceWithInner(MsgRange, ArgRange);
596*0b57cec5SDimitry Andric     return true;
597*0b57cec5SDimitry Andric   }
598*0b57cec5SDimitry Andric 
599*0b57cec5SDimitry Andric   return false;
600*0b57cec5SDimitry Andric }
601*0b57cec5SDimitry Andric 
602*0b57cec5SDimitry Andric static bool shouldNotRewriteImmediateMessageArgs(const ObjCMessageExpr *Msg,
603*0b57cec5SDimitry Andric                                                  const NSAPI &NS) {
604*0b57cec5SDimitry Andric   if (!Msg)
605*0b57cec5SDimitry Andric     return false;
606*0b57cec5SDimitry Andric 
607*0b57cec5SDimitry Andric   IdentifierInfo *II = nullptr;
608*0b57cec5SDimitry Andric   if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
609*0b57cec5SDimitry Andric     return false;
610*0b57cec5SDimitry Andric 
611*0b57cec5SDimitry Andric   if (II != NS.getNSClassId(NSAPI::ClassId_NSDictionary))
612*0b57cec5SDimitry Andric     return false;
613*0b57cec5SDimitry Andric 
614*0b57cec5SDimitry Andric   Selector Sel = Msg->getSelector();
615*0b57cec5SDimitry Andric   if (Sel == NS.getNSDictionarySelector(
616*0b57cec5SDimitry Andric                                   NSAPI::NSDict_dictionaryWithObjectsForKeys) ||
617*0b57cec5SDimitry Andric       Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsForKeys)) {
618*0b57cec5SDimitry Andric     if (Msg->getNumArgs() != 2)
619*0b57cec5SDimitry Andric       return false;
620*0b57cec5SDimitry Andric 
621*0b57cec5SDimitry Andric     SmallVector<const Expr *, 8> Vals;
622*0b57cec5SDimitry Andric     if (!getNSArrayObjects(Msg->getArg(0), NS, Vals))
623*0b57cec5SDimitry Andric       return false;
624*0b57cec5SDimitry Andric 
625*0b57cec5SDimitry Andric     SmallVector<const Expr *, 8> Keys;
626*0b57cec5SDimitry Andric     if (!getNSArrayObjects(Msg->getArg(1), NS, Keys))
627*0b57cec5SDimitry Andric       return false;
628*0b57cec5SDimitry Andric 
629*0b57cec5SDimitry Andric     if (Vals.size() != Keys.size())
630*0b57cec5SDimitry Andric       return false;
631*0b57cec5SDimitry Andric 
632*0b57cec5SDimitry Andric     return true;
633*0b57cec5SDimitry Andric   }
634*0b57cec5SDimitry Andric 
635*0b57cec5SDimitry Andric   return false;
636*0b57cec5SDimitry Andric }
637*0b57cec5SDimitry Andric 
638*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
639*0b57cec5SDimitry Andric // rewriteToNumberLiteral.
640*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
641*0b57cec5SDimitry Andric 
642*0b57cec5SDimitry Andric static bool rewriteToCharLiteral(const ObjCMessageExpr *Msg,
643*0b57cec5SDimitry Andric                                    const CharacterLiteral *Arg,
644*0b57cec5SDimitry Andric                                    const NSAPI &NS, Commit &commit) {
645*0b57cec5SDimitry Andric   if (Arg->getKind() != CharacterLiteral::Ascii)
646*0b57cec5SDimitry Andric     return false;
647*0b57cec5SDimitry Andric   if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithChar,
648*0b57cec5SDimitry Andric                                    Msg->getSelector())) {
649*0b57cec5SDimitry Andric     SourceRange ArgRange = Arg->getSourceRange();
650*0b57cec5SDimitry Andric     commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
651*0b57cec5SDimitry Andric     commit.insert(ArgRange.getBegin(), "@");
652*0b57cec5SDimitry Andric     return true;
653*0b57cec5SDimitry Andric   }
654*0b57cec5SDimitry Andric 
655*0b57cec5SDimitry Andric   return rewriteToNumericBoxedExpression(Msg, NS, commit);
656*0b57cec5SDimitry Andric }
657*0b57cec5SDimitry Andric 
658*0b57cec5SDimitry Andric static bool rewriteToBoolLiteral(const ObjCMessageExpr *Msg,
659*0b57cec5SDimitry Andric                                    const Expr *Arg,
660*0b57cec5SDimitry Andric                                    const NSAPI &NS, Commit &commit) {
661*0b57cec5SDimitry Andric   if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithBool,
662*0b57cec5SDimitry Andric                                    Msg->getSelector())) {
663*0b57cec5SDimitry Andric     SourceRange ArgRange = Arg->getSourceRange();
664*0b57cec5SDimitry Andric     commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
665*0b57cec5SDimitry Andric     commit.insert(ArgRange.getBegin(), "@");
666*0b57cec5SDimitry Andric     return true;
667*0b57cec5SDimitry Andric   }
668*0b57cec5SDimitry Andric 
669*0b57cec5SDimitry Andric   return rewriteToNumericBoxedExpression(Msg, NS, commit);
670*0b57cec5SDimitry Andric }
671*0b57cec5SDimitry Andric 
672*0b57cec5SDimitry Andric namespace {
673*0b57cec5SDimitry Andric 
674*0b57cec5SDimitry Andric struct LiteralInfo {
675*0b57cec5SDimitry Andric   bool Hex, Octal;
676*0b57cec5SDimitry Andric   StringRef U, F, L, LL;
677*0b57cec5SDimitry Andric   CharSourceRange WithoutSuffRange;
678*0b57cec5SDimitry Andric };
679*0b57cec5SDimitry Andric 
680*0b57cec5SDimitry Andric }
681*0b57cec5SDimitry Andric 
682*0b57cec5SDimitry Andric static bool getLiteralInfo(SourceRange literalRange,
683*0b57cec5SDimitry Andric                            bool isFloat, bool isIntZero,
684*0b57cec5SDimitry Andric                           ASTContext &Ctx, LiteralInfo &Info) {
685*0b57cec5SDimitry Andric   if (literalRange.getBegin().isMacroID() ||
686*0b57cec5SDimitry Andric       literalRange.getEnd().isMacroID())
687*0b57cec5SDimitry Andric     return false;
688*0b57cec5SDimitry Andric   StringRef text = Lexer::getSourceText(
689*0b57cec5SDimitry Andric                                   CharSourceRange::getTokenRange(literalRange),
690*0b57cec5SDimitry Andric                                   Ctx.getSourceManager(), Ctx.getLangOpts());
691*0b57cec5SDimitry Andric   if (text.empty())
692*0b57cec5SDimitry Andric     return false;
693*0b57cec5SDimitry Andric 
694*0b57cec5SDimitry Andric   Optional<bool> UpperU, UpperL;
695*0b57cec5SDimitry Andric   bool UpperF = false;
696*0b57cec5SDimitry Andric 
697*0b57cec5SDimitry Andric   struct Suff {
698*0b57cec5SDimitry Andric     static bool has(StringRef suff, StringRef &text) {
699*0b57cec5SDimitry Andric       if (text.endswith(suff)) {
700*0b57cec5SDimitry Andric         text = text.substr(0, text.size()-suff.size());
701*0b57cec5SDimitry Andric         return true;
702*0b57cec5SDimitry Andric       }
703*0b57cec5SDimitry Andric       return false;
704*0b57cec5SDimitry Andric     }
705*0b57cec5SDimitry Andric   };
706*0b57cec5SDimitry Andric 
707*0b57cec5SDimitry Andric   while (1) {
708*0b57cec5SDimitry Andric     if (Suff::has("u", text)) {
709*0b57cec5SDimitry Andric       UpperU = false;
710*0b57cec5SDimitry Andric     } else if (Suff::has("U", text)) {
711*0b57cec5SDimitry Andric       UpperU = true;
712*0b57cec5SDimitry Andric     } else if (Suff::has("ll", text)) {
713*0b57cec5SDimitry Andric       UpperL = false;
714*0b57cec5SDimitry Andric     } else if (Suff::has("LL", text)) {
715*0b57cec5SDimitry Andric       UpperL = true;
716*0b57cec5SDimitry Andric     } else if (Suff::has("l", text)) {
717*0b57cec5SDimitry Andric       UpperL = false;
718*0b57cec5SDimitry Andric     } else if (Suff::has("L", text)) {
719*0b57cec5SDimitry Andric       UpperL = true;
720*0b57cec5SDimitry Andric     } else if (isFloat && Suff::has("f", text)) {
721*0b57cec5SDimitry Andric       UpperF = false;
722*0b57cec5SDimitry Andric     } else if (isFloat && Suff::has("F", text)) {
723*0b57cec5SDimitry Andric       UpperF = true;
724*0b57cec5SDimitry Andric     } else
725*0b57cec5SDimitry Andric       break;
726*0b57cec5SDimitry Andric   }
727*0b57cec5SDimitry Andric 
728*0b57cec5SDimitry Andric   if (!UpperU.hasValue() && !UpperL.hasValue())
729*0b57cec5SDimitry Andric     UpperU = UpperL = true;
730*0b57cec5SDimitry Andric   else if (UpperU.hasValue() && !UpperL.hasValue())
731*0b57cec5SDimitry Andric     UpperL = UpperU;
732*0b57cec5SDimitry Andric   else if (UpperL.hasValue() && !UpperU.hasValue())
733*0b57cec5SDimitry Andric     UpperU = UpperL;
734*0b57cec5SDimitry Andric 
735*0b57cec5SDimitry Andric   Info.U = *UpperU ? "U" : "u";
736*0b57cec5SDimitry Andric   Info.L = *UpperL ? "L" : "l";
737*0b57cec5SDimitry Andric   Info.LL = *UpperL ? "LL" : "ll";
738*0b57cec5SDimitry Andric   Info.F = UpperF ? "F" : "f";
739*0b57cec5SDimitry Andric 
740*0b57cec5SDimitry Andric   Info.Hex = Info.Octal = false;
741*0b57cec5SDimitry Andric   if (text.startswith("0x"))
742*0b57cec5SDimitry Andric     Info.Hex = true;
743*0b57cec5SDimitry Andric   else if (!isFloat && !isIntZero && text.startswith("0"))
744*0b57cec5SDimitry Andric     Info.Octal = true;
745*0b57cec5SDimitry Andric 
746*0b57cec5SDimitry Andric   SourceLocation B = literalRange.getBegin();
747*0b57cec5SDimitry Andric   Info.WithoutSuffRange =
748*0b57cec5SDimitry Andric       CharSourceRange::getCharRange(B, B.getLocWithOffset(text.size()));
749*0b57cec5SDimitry Andric   return true;
750*0b57cec5SDimitry Andric }
751*0b57cec5SDimitry Andric 
752*0b57cec5SDimitry Andric static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
753*0b57cec5SDimitry Andric                                    const NSAPI &NS, Commit &commit) {
754*0b57cec5SDimitry Andric   if (Msg->getNumArgs() != 1)
755*0b57cec5SDimitry Andric     return false;
756*0b57cec5SDimitry Andric 
757*0b57cec5SDimitry Andric   const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts();
758*0b57cec5SDimitry Andric   if (const CharacterLiteral *CharE = dyn_cast<CharacterLiteral>(Arg))
759*0b57cec5SDimitry Andric     return rewriteToCharLiteral(Msg, CharE, NS, commit);
760*0b57cec5SDimitry Andric   if (const ObjCBoolLiteralExpr *BE = dyn_cast<ObjCBoolLiteralExpr>(Arg))
761*0b57cec5SDimitry Andric     return rewriteToBoolLiteral(Msg, BE, NS, commit);
762*0b57cec5SDimitry Andric   if (const CXXBoolLiteralExpr *BE = dyn_cast<CXXBoolLiteralExpr>(Arg))
763*0b57cec5SDimitry Andric     return rewriteToBoolLiteral(Msg, BE, NS, commit);
764*0b57cec5SDimitry Andric 
765*0b57cec5SDimitry Andric   const Expr *literalE = Arg;
766*0b57cec5SDimitry Andric   if (const UnaryOperator *UOE = dyn_cast<UnaryOperator>(literalE)) {
767*0b57cec5SDimitry Andric     if (UOE->getOpcode() == UO_Plus || UOE->getOpcode() == UO_Minus)
768*0b57cec5SDimitry Andric       literalE = UOE->getSubExpr();
769*0b57cec5SDimitry Andric   }
770*0b57cec5SDimitry Andric 
771*0b57cec5SDimitry Andric   // Only integer and floating literals, otherwise try to rewrite to boxed
772*0b57cec5SDimitry Andric   // expression.
773*0b57cec5SDimitry Andric   if (!isa<IntegerLiteral>(literalE) && !isa<FloatingLiteral>(literalE))
774*0b57cec5SDimitry Andric     return rewriteToNumericBoxedExpression(Msg, NS, commit);
775*0b57cec5SDimitry Andric 
776*0b57cec5SDimitry Andric   ASTContext &Ctx = NS.getASTContext();
777*0b57cec5SDimitry Andric   Selector Sel = Msg->getSelector();
778*0b57cec5SDimitry Andric   Optional<NSAPI::NSNumberLiteralMethodKind>
779*0b57cec5SDimitry Andric     MKOpt = NS.getNSNumberLiteralMethodKind(Sel);
780*0b57cec5SDimitry Andric   if (!MKOpt)
781*0b57cec5SDimitry Andric     return false;
782*0b57cec5SDimitry Andric   NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;
783*0b57cec5SDimitry Andric 
784*0b57cec5SDimitry Andric   bool CallIsUnsigned = false, CallIsLong = false, CallIsLongLong = false;
785*0b57cec5SDimitry Andric   bool CallIsFloating = false, CallIsDouble = false;
786*0b57cec5SDimitry Andric 
787*0b57cec5SDimitry Andric   switch (MK) {
788*0b57cec5SDimitry Andric   // We cannot have these calls with int/float literals.
789*0b57cec5SDimitry Andric   case NSAPI::NSNumberWithChar:
790*0b57cec5SDimitry Andric   case NSAPI::NSNumberWithUnsignedChar:
791*0b57cec5SDimitry Andric   case NSAPI::NSNumberWithShort:
792*0b57cec5SDimitry Andric   case NSAPI::NSNumberWithUnsignedShort:
793*0b57cec5SDimitry Andric   case NSAPI::NSNumberWithBool:
794*0b57cec5SDimitry Andric     return rewriteToNumericBoxedExpression(Msg, NS, commit);
795*0b57cec5SDimitry Andric 
796*0b57cec5SDimitry Andric   case NSAPI::NSNumberWithUnsignedInt:
797*0b57cec5SDimitry Andric   case NSAPI::NSNumberWithUnsignedInteger:
798*0b57cec5SDimitry Andric     CallIsUnsigned = true;
799*0b57cec5SDimitry Andric     LLVM_FALLTHROUGH;
800*0b57cec5SDimitry Andric   case NSAPI::NSNumberWithInt:
801*0b57cec5SDimitry Andric   case NSAPI::NSNumberWithInteger:
802*0b57cec5SDimitry Andric     break;
803*0b57cec5SDimitry Andric 
804*0b57cec5SDimitry Andric   case NSAPI::NSNumberWithUnsignedLong:
805*0b57cec5SDimitry Andric     CallIsUnsigned = true;
806*0b57cec5SDimitry Andric     LLVM_FALLTHROUGH;
807*0b57cec5SDimitry Andric   case NSAPI::NSNumberWithLong:
808*0b57cec5SDimitry Andric     CallIsLong = true;
809*0b57cec5SDimitry Andric     break;
810*0b57cec5SDimitry Andric 
811*0b57cec5SDimitry Andric   case NSAPI::NSNumberWithUnsignedLongLong:
812*0b57cec5SDimitry Andric     CallIsUnsigned = true;
813*0b57cec5SDimitry Andric     LLVM_FALLTHROUGH;
814*0b57cec5SDimitry Andric   case NSAPI::NSNumberWithLongLong:
815*0b57cec5SDimitry Andric     CallIsLongLong = true;
816*0b57cec5SDimitry Andric     break;
817*0b57cec5SDimitry Andric 
818*0b57cec5SDimitry Andric   case NSAPI::NSNumberWithDouble:
819*0b57cec5SDimitry Andric     CallIsDouble = true;
820*0b57cec5SDimitry Andric     LLVM_FALLTHROUGH;
821*0b57cec5SDimitry Andric   case NSAPI::NSNumberWithFloat:
822*0b57cec5SDimitry Andric     CallIsFloating = true;
823*0b57cec5SDimitry Andric     break;
824*0b57cec5SDimitry Andric   }
825*0b57cec5SDimitry Andric 
826*0b57cec5SDimitry Andric   SourceRange ArgRange = Arg->getSourceRange();
827*0b57cec5SDimitry Andric   QualType ArgTy = Arg->getType();
828*0b57cec5SDimitry Andric   QualType CallTy = Msg->getArg(0)->getType();
829*0b57cec5SDimitry Andric 
830*0b57cec5SDimitry Andric   // Check for the easy case, the literal maps directly to the call.
831*0b57cec5SDimitry Andric   if (Ctx.hasSameType(ArgTy, CallTy)) {
832*0b57cec5SDimitry Andric     commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
833*0b57cec5SDimitry Andric     commit.insert(ArgRange.getBegin(), "@");
834*0b57cec5SDimitry Andric     return true;
835*0b57cec5SDimitry Andric   }
836*0b57cec5SDimitry Andric 
837*0b57cec5SDimitry Andric   // We will need to modify the literal suffix to get the same type as the call.
838*0b57cec5SDimitry Andric   // Try with boxed expression if it came from a macro.
839*0b57cec5SDimitry Andric   if (ArgRange.getBegin().isMacroID())
840*0b57cec5SDimitry Andric     return rewriteToNumericBoxedExpression(Msg, NS, commit);
841*0b57cec5SDimitry Andric 
842*0b57cec5SDimitry Andric   bool LitIsFloat = ArgTy->isFloatingType();
843*0b57cec5SDimitry Andric   // For a float passed to integer call, don't try rewriting to objc literal.
844*0b57cec5SDimitry Andric   // It is difficult and a very uncommon case anyway.
845*0b57cec5SDimitry Andric   // But try with boxed expression.
846*0b57cec5SDimitry Andric   if (LitIsFloat && !CallIsFloating)
847*0b57cec5SDimitry Andric     return rewriteToNumericBoxedExpression(Msg, NS, commit);
848*0b57cec5SDimitry Andric 
849*0b57cec5SDimitry Andric   // Try to modify the literal make it the same type as the method call.
850*0b57cec5SDimitry Andric   // -Modify the suffix, and/or
851*0b57cec5SDimitry Andric   // -Change integer to float
852*0b57cec5SDimitry Andric 
853*0b57cec5SDimitry Andric   LiteralInfo LitInfo;
854*0b57cec5SDimitry Andric   bool isIntZero = false;
855*0b57cec5SDimitry Andric   if (const IntegerLiteral *IntE = dyn_cast<IntegerLiteral>(literalE))
856*0b57cec5SDimitry Andric     isIntZero = !IntE->getValue().getBoolValue();
857*0b57cec5SDimitry Andric   if (!getLiteralInfo(ArgRange, LitIsFloat, isIntZero, Ctx, LitInfo))
858*0b57cec5SDimitry Andric     return rewriteToNumericBoxedExpression(Msg, NS, commit);
859*0b57cec5SDimitry Andric 
860*0b57cec5SDimitry Andric   // Not easy to do int -> float with hex/octal and uncommon anyway.
861*0b57cec5SDimitry Andric   if (!LitIsFloat && CallIsFloating && (LitInfo.Hex || LitInfo.Octal))
862*0b57cec5SDimitry Andric     return rewriteToNumericBoxedExpression(Msg, NS, commit);
863*0b57cec5SDimitry Andric 
864*0b57cec5SDimitry Andric   SourceLocation LitB = LitInfo.WithoutSuffRange.getBegin();
865*0b57cec5SDimitry Andric   SourceLocation LitE = LitInfo.WithoutSuffRange.getEnd();
866*0b57cec5SDimitry Andric 
867*0b57cec5SDimitry Andric   commit.replaceWithInner(CharSourceRange::getTokenRange(Msg->getSourceRange()),
868*0b57cec5SDimitry Andric                          LitInfo.WithoutSuffRange);
869*0b57cec5SDimitry Andric   commit.insert(LitB, "@");
870*0b57cec5SDimitry Andric 
871*0b57cec5SDimitry Andric   if (!LitIsFloat && CallIsFloating)
872*0b57cec5SDimitry Andric     commit.insert(LitE, ".0");
873*0b57cec5SDimitry Andric 
874*0b57cec5SDimitry Andric   if (CallIsFloating) {
875*0b57cec5SDimitry Andric     if (!CallIsDouble)
876*0b57cec5SDimitry Andric       commit.insert(LitE, LitInfo.F);
877*0b57cec5SDimitry Andric   } else {
878*0b57cec5SDimitry Andric     if (CallIsUnsigned)
879*0b57cec5SDimitry Andric       commit.insert(LitE, LitInfo.U);
880*0b57cec5SDimitry Andric 
881*0b57cec5SDimitry Andric     if (CallIsLong)
882*0b57cec5SDimitry Andric       commit.insert(LitE, LitInfo.L);
883*0b57cec5SDimitry Andric     else if (CallIsLongLong)
884*0b57cec5SDimitry Andric       commit.insert(LitE, LitInfo.LL);
885*0b57cec5SDimitry Andric   }
886*0b57cec5SDimitry Andric   return true;
887*0b57cec5SDimitry Andric }
888*0b57cec5SDimitry Andric 
889*0b57cec5SDimitry Andric // FIXME: Make determination of operator precedence more general and
890*0b57cec5SDimitry Andric // make it broadly available.
891*0b57cec5SDimitry Andric static bool subscriptOperatorNeedsParens(const Expr *FullExpr) {
892*0b57cec5SDimitry Andric   const Expr* Expr = FullExpr->IgnoreImpCasts();
893*0b57cec5SDimitry Andric   if (isa<ArraySubscriptExpr>(Expr) ||
894*0b57cec5SDimitry Andric       isa<CallExpr>(Expr) ||
895*0b57cec5SDimitry Andric       isa<DeclRefExpr>(Expr) ||
896*0b57cec5SDimitry Andric       isa<CXXNamedCastExpr>(Expr) ||
897*0b57cec5SDimitry Andric       isa<CXXConstructExpr>(Expr) ||
898*0b57cec5SDimitry Andric       isa<CXXThisExpr>(Expr) ||
899*0b57cec5SDimitry Andric       isa<CXXTypeidExpr>(Expr) ||
900*0b57cec5SDimitry Andric       isa<CXXUnresolvedConstructExpr>(Expr) ||
901*0b57cec5SDimitry Andric       isa<ObjCMessageExpr>(Expr) ||
902*0b57cec5SDimitry Andric       isa<ObjCPropertyRefExpr>(Expr) ||
903*0b57cec5SDimitry Andric       isa<ObjCProtocolExpr>(Expr) ||
904*0b57cec5SDimitry Andric       isa<MemberExpr>(Expr) ||
905*0b57cec5SDimitry Andric       isa<ObjCIvarRefExpr>(Expr) ||
906*0b57cec5SDimitry Andric       isa<ParenExpr>(FullExpr) ||
907*0b57cec5SDimitry Andric       isa<ParenListExpr>(Expr) ||
908*0b57cec5SDimitry Andric       isa<SizeOfPackExpr>(Expr))
909*0b57cec5SDimitry Andric     return false;
910*0b57cec5SDimitry Andric 
911*0b57cec5SDimitry Andric   return true;
912*0b57cec5SDimitry Andric }
913*0b57cec5SDimitry Andric static bool castOperatorNeedsParens(const Expr *FullExpr) {
914*0b57cec5SDimitry Andric   const Expr* Expr = FullExpr->IgnoreImpCasts();
915*0b57cec5SDimitry Andric   if (isa<ArraySubscriptExpr>(Expr) ||
916*0b57cec5SDimitry Andric       isa<CallExpr>(Expr) ||
917*0b57cec5SDimitry Andric       isa<DeclRefExpr>(Expr) ||
918*0b57cec5SDimitry Andric       isa<CastExpr>(Expr) ||
919*0b57cec5SDimitry Andric       isa<CXXNewExpr>(Expr) ||
920*0b57cec5SDimitry Andric       isa<CXXConstructExpr>(Expr) ||
921*0b57cec5SDimitry Andric       isa<CXXDeleteExpr>(Expr) ||
922*0b57cec5SDimitry Andric       isa<CXXNoexceptExpr>(Expr) ||
923*0b57cec5SDimitry Andric       isa<CXXPseudoDestructorExpr>(Expr) ||
924*0b57cec5SDimitry Andric       isa<CXXScalarValueInitExpr>(Expr) ||
925*0b57cec5SDimitry Andric       isa<CXXThisExpr>(Expr) ||
926*0b57cec5SDimitry Andric       isa<CXXTypeidExpr>(Expr) ||
927*0b57cec5SDimitry Andric       isa<CXXUnresolvedConstructExpr>(Expr) ||
928*0b57cec5SDimitry Andric       isa<ObjCMessageExpr>(Expr) ||
929*0b57cec5SDimitry Andric       isa<ObjCPropertyRefExpr>(Expr) ||
930*0b57cec5SDimitry Andric       isa<ObjCProtocolExpr>(Expr) ||
931*0b57cec5SDimitry Andric       isa<MemberExpr>(Expr) ||
932*0b57cec5SDimitry Andric       isa<ObjCIvarRefExpr>(Expr) ||
933*0b57cec5SDimitry Andric       isa<ParenExpr>(FullExpr) ||
934*0b57cec5SDimitry Andric       isa<ParenListExpr>(Expr) ||
935*0b57cec5SDimitry Andric       isa<SizeOfPackExpr>(Expr) ||
936*0b57cec5SDimitry Andric       isa<UnaryOperator>(Expr))
937*0b57cec5SDimitry Andric     return false;
938*0b57cec5SDimitry Andric 
939*0b57cec5SDimitry Andric   return true;
940*0b57cec5SDimitry Andric }
941*0b57cec5SDimitry Andric 
942*0b57cec5SDimitry Andric static void objectifyExpr(const Expr *E, Commit &commit) {
943*0b57cec5SDimitry Andric   if (!E) return;
944*0b57cec5SDimitry Andric 
945*0b57cec5SDimitry Andric   QualType T = E->getType();
946*0b57cec5SDimitry Andric   if (T->isObjCObjectPointerType()) {
947*0b57cec5SDimitry Andric     if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) {
948*0b57cec5SDimitry Andric       if (ICE->getCastKind() != CK_CPointerToObjCPointerCast)
949*0b57cec5SDimitry Andric         return;
950*0b57cec5SDimitry Andric     } else {
951*0b57cec5SDimitry Andric       return;
952*0b57cec5SDimitry Andric     }
953*0b57cec5SDimitry Andric   } else if (!T->isPointerType()) {
954*0b57cec5SDimitry Andric     return;
955*0b57cec5SDimitry Andric   }
956*0b57cec5SDimitry Andric 
957*0b57cec5SDimitry Andric   SourceRange Range = E->getSourceRange();
958*0b57cec5SDimitry Andric   if (castOperatorNeedsParens(E))
959*0b57cec5SDimitry Andric     commit.insertWrap("(", Range, ")");
960*0b57cec5SDimitry Andric   commit.insertBefore(Range.getBegin(), "(id)");
961*0b57cec5SDimitry Andric }
962*0b57cec5SDimitry Andric 
963*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
964*0b57cec5SDimitry Andric // rewriteToNumericBoxedExpression.
965*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
966*0b57cec5SDimitry Andric 
967*0b57cec5SDimitry Andric static bool isEnumConstant(const Expr *E) {
968*0b57cec5SDimitry Andric   if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts()))
969*0b57cec5SDimitry Andric     if (const ValueDecl *VD = DRE->getDecl())
970*0b57cec5SDimitry Andric       return isa<EnumConstantDecl>(VD);
971*0b57cec5SDimitry Andric 
972*0b57cec5SDimitry Andric   return false;
973*0b57cec5SDimitry Andric }
974*0b57cec5SDimitry Andric 
975*0b57cec5SDimitry Andric static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
976*0b57cec5SDimitry Andric                                             const NSAPI &NS, Commit &commit) {
977*0b57cec5SDimitry Andric   if (Msg->getNumArgs() != 1)
978*0b57cec5SDimitry Andric     return false;
979*0b57cec5SDimitry Andric 
980*0b57cec5SDimitry Andric   const Expr *Arg = Msg->getArg(0);
981*0b57cec5SDimitry Andric   if (Arg->isTypeDependent())
982*0b57cec5SDimitry Andric     return false;
983*0b57cec5SDimitry Andric 
984*0b57cec5SDimitry Andric   ASTContext &Ctx = NS.getASTContext();
985*0b57cec5SDimitry Andric   Selector Sel = Msg->getSelector();
986*0b57cec5SDimitry Andric   Optional<NSAPI::NSNumberLiteralMethodKind>
987*0b57cec5SDimitry Andric     MKOpt = NS.getNSNumberLiteralMethodKind(Sel);
988*0b57cec5SDimitry Andric   if (!MKOpt)
989*0b57cec5SDimitry Andric     return false;
990*0b57cec5SDimitry Andric   NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;
991*0b57cec5SDimitry Andric 
992*0b57cec5SDimitry Andric   const Expr *OrigArg = Arg->IgnoreImpCasts();
993*0b57cec5SDimitry Andric   QualType FinalTy = Arg->getType();
994*0b57cec5SDimitry Andric   QualType OrigTy = OrigArg->getType();
995*0b57cec5SDimitry Andric   uint64_t FinalTySize = Ctx.getTypeSize(FinalTy);
996*0b57cec5SDimitry Andric   uint64_t OrigTySize = Ctx.getTypeSize(OrigTy);
997*0b57cec5SDimitry Andric 
998*0b57cec5SDimitry Andric   bool isTruncated = FinalTySize < OrigTySize;
999*0b57cec5SDimitry Andric   bool needsCast = false;
1000*0b57cec5SDimitry Andric 
1001*0b57cec5SDimitry Andric   if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) {
1002*0b57cec5SDimitry Andric     switch (ICE->getCastKind()) {
1003*0b57cec5SDimitry Andric     case CK_LValueToRValue:
1004*0b57cec5SDimitry Andric     case CK_NoOp:
1005*0b57cec5SDimitry Andric     case CK_UserDefinedConversion:
1006*0b57cec5SDimitry Andric       break;
1007*0b57cec5SDimitry Andric 
1008*0b57cec5SDimitry Andric     case CK_IntegralCast: {
1009*0b57cec5SDimitry Andric       if (MK == NSAPI::NSNumberWithBool && OrigTy->isBooleanType())
1010*0b57cec5SDimitry Andric         break;
1011*0b57cec5SDimitry Andric       // Be more liberal with Integer/UnsignedInteger which are very commonly
1012*0b57cec5SDimitry Andric       // used.
1013*0b57cec5SDimitry Andric       if ((MK == NSAPI::NSNumberWithInteger ||
1014*0b57cec5SDimitry Andric            MK == NSAPI::NSNumberWithUnsignedInteger) &&
1015*0b57cec5SDimitry Andric           !isTruncated) {
1016*0b57cec5SDimitry Andric         if (OrigTy->getAs<EnumType>() || isEnumConstant(OrigArg))
1017*0b57cec5SDimitry Andric           break;
1018*0b57cec5SDimitry Andric         if ((MK==NSAPI::NSNumberWithInteger) == OrigTy->isSignedIntegerType() &&
1019*0b57cec5SDimitry Andric             OrigTySize >= Ctx.getTypeSize(Ctx.IntTy))
1020*0b57cec5SDimitry Andric           break;
1021*0b57cec5SDimitry Andric       }
1022*0b57cec5SDimitry Andric 
1023*0b57cec5SDimitry Andric       needsCast = true;
1024*0b57cec5SDimitry Andric       break;
1025*0b57cec5SDimitry Andric     }
1026*0b57cec5SDimitry Andric 
1027*0b57cec5SDimitry Andric     case CK_PointerToBoolean:
1028*0b57cec5SDimitry Andric     case CK_IntegralToBoolean:
1029*0b57cec5SDimitry Andric     case CK_IntegralToFloating:
1030*0b57cec5SDimitry Andric     case CK_FloatingToIntegral:
1031*0b57cec5SDimitry Andric     case CK_FloatingToBoolean:
1032*0b57cec5SDimitry Andric     case CK_FloatingCast:
1033*0b57cec5SDimitry Andric     case CK_FloatingComplexToReal:
1034*0b57cec5SDimitry Andric     case CK_FloatingComplexToBoolean:
1035*0b57cec5SDimitry Andric     case CK_IntegralComplexToReal:
1036*0b57cec5SDimitry Andric     case CK_IntegralComplexToBoolean:
1037*0b57cec5SDimitry Andric     case CK_AtomicToNonAtomic:
1038*0b57cec5SDimitry Andric     case CK_AddressSpaceConversion:
1039*0b57cec5SDimitry Andric       needsCast = true;
1040*0b57cec5SDimitry Andric       break;
1041*0b57cec5SDimitry Andric 
1042*0b57cec5SDimitry Andric     case CK_Dependent:
1043*0b57cec5SDimitry Andric     case CK_BitCast:
1044*0b57cec5SDimitry Andric     case CK_LValueBitCast:
1045*0b57cec5SDimitry Andric     case CK_LValueToRValueBitCast:
1046*0b57cec5SDimitry Andric     case CK_BaseToDerived:
1047*0b57cec5SDimitry Andric     case CK_DerivedToBase:
1048*0b57cec5SDimitry Andric     case CK_UncheckedDerivedToBase:
1049*0b57cec5SDimitry Andric     case CK_Dynamic:
1050*0b57cec5SDimitry Andric     case CK_ToUnion:
1051*0b57cec5SDimitry Andric     case CK_ArrayToPointerDecay:
1052*0b57cec5SDimitry Andric     case CK_FunctionToPointerDecay:
1053*0b57cec5SDimitry Andric     case CK_NullToPointer:
1054*0b57cec5SDimitry Andric     case CK_NullToMemberPointer:
1055*0b57cec5SDimitry Andric     case CK_BaseToDerivedMemberPointer:
1056*0b57cec5SDimitry Andric     case CK_DerivedToBaseMemberPointer:
1057*0b57cec5SDimitry Andric     case CK_MemberPointerToBoolean:
1058*0b57cec5SDimitry Andric     case CK_ReinterpretMemberPointer:
1059*0b57cec5SDimitry Andric     case CK_ConstructorConversion:
1060*0b57cec5SDimitry Andric     case CK_IntegralToPointer:
1061*0b57cec5SDimitry Andric     case CK_PointerToIntegral:
1062*0b57cec5SDimitry Andric     case CK_ToVoid:
1063*0b57cec5SDimitry Andric     case CK_VectorSplat:
1064*0b57cec5SDimitry Andric     case CK_CPointerToObjCPointerCast:
1065*0b57cec5SDimitry Andric     case CK_BlockPointerToObjCPointerCast:
1066*0b57cec5SDimitry Andric     case CK_AnyPointerToBlockPointerCast:
1067*0b57cec5SDimitry Andric     case CK_ObjCObjectLValueCast:
1068*0b57cec5SDimitry Andric     case CK_FloatingRealToComplex:
1069*0b57cec5SDimitry Andric     case CK_FloatingComplexCast:
1070*0b57cec5SDimitry Andric     case CK_FloatingComplexToIntegralComplex:
1071*0b57cec5SDimitry Andric     case CK_IntegralRealToComplex:
1072*0b57cec5SDimitry Andric     case CK_IntegralComplexCast:
1073*0b57cec5SDimitry Andric     case CK_IntegralComplexToFloatingComplex:
1074*0b57cec5SDimitry Andric     case CK_ARCProduceObject:
1075*0b57cec5SDimitry Andric     case CK_ARCConsumeObject:
1076*0b57cec5SDimitry Andric     case CK_ARCReclaimReturnedObject:
1077*0b57cec5SDimitry Andric     case CK_ARCExtendBlockObject:
1078*0b57cec5SDimitry Andric     case CK_NonAtomicToAtomic:
1079*0b57cec5SDimitry Andric     case CK_CopyAndAutoreleaseBlockObject:
1080*0b57cec5SDimitry Andric     case CK_BuiltinFnToFnPtr:
1081*0b57cec5SDimitry Andric     case CK_ZeroToOCLOpaqueType:
1082*0b57cec5SDimitry Andric     case CK_IntToOCLSampler:
1083*0b57cec5SDimitry Andric       return false;
1084*0b57cec5SDimitry Andric 
1085*0b57cec5SDimitry Andric     case CK_BooleanToSignedIntegral:
1086*0b57cec5SDimitry Andric       llvm_unreachable("OpenCL-specific cast in Objective-C?");
1087*0b57cec5SDimitry Andric 
1088*0b57cec5SDimitry Andric     case CK_FixedPointCast:
1089*0b57cec5SDimitry Andric     case CK_FixedPointToBoolean:
1090*0b57cec5SDimitry Andric     case CK_FixedPointToIntegral:
1091*0b57cec5SDimitry Andric     case CK_IntegralToFixedPoint:
1092*0b57cec5SDimitry Andric       llvm_unreachable("Fixed point types are disabled for Objective-C");
1093*0b57cec5SDimitry Andric     }
1094*0b57cec5SDimitry Andric   }
1095*0b57cec5SDimitry Andric 
1096*0b57cec5SDimitry Andric   if (needsCast) {
1097*0b57cec5SDimitry Andric     DiagnosticsEngine &Diags = Ctx.getDiagnostics();
1098*0b57cec5SDimitry Andric     // FIXME: Use a custom category name to distinguish migration diagnostics.
1099*0b57cec5SDimitry Andric     unsigned diagID = Diags.getCustomDiagID(DiagnosticsEngine::Warning,
1100*0b57cec5SDimitry Andric                        "converting to boxing syntax requires casting %0 to %1");
1101*0b57cec5SDimitry Andric     Diags.Report(Msg->getExprLoc(), diagID) << OrigTy << FinalTy
1102*0b57cec5SDimitry Andric         << Msg->getSourceRange();
1103*0b57cec5SDimitry Andric     return false;
1104*0b57cec5SDimitry Andric   }
1105*0b57cec5SDimitry Andric 
1106*0b57cec5SDimitry Andric   SourceRange ArgRange = OrigArg->getSourceRange();
1107*0b57cec5SDimitry Andric   commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
1108*0b57cec5SDimitry Andric 
1109*0b57cec5SDimitry Andric   if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
1110*0b57cec5SDimitry Andric     commit.insertBefore(ArgRange.getBegin(), "@");
1111*0b57cec5SDimitry Andric   else
1112*0b57cec5SDimitry Andric     commit.insertWrap("@(", ArgRange, ")");
1113*0b57cec5SDimitry Andric 
1114*0b57cec5SDimitry Andric   return true;
1115*0b57cec5SDimitry Andric }
1116*0b57cec5SDimitry Andric 
1117*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
1118*0b57cec5SDimitry Andric // rewriteToStringBoxedExpression.
1119*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
1120*0b57cec5SDimitry Andric 
1121*0b57cec5SDimitry Andric static bool doRewriteToUTF8StringBoxedExpressionHelper(
1122*0b57cec5SDimitry Andric                                               const ObjCMessageExpr *Msg,
1123*0b57cec5SDimitry Andric                                               const NSAPI &NS, Commit &commit) {
1124*0b57cec5SDimitry Andric   const Expr *Arg = Msg->getArg(0);
1125*0b57cec5SDimitry Andric   if (Arg->isTypeDependent())
1126*0b57cec5SDimitry Andric     return false;
1127*0b57cec5SDimitry Andric 
1128*0b57cec5SDimitry Andric   ASTContext &Ctx = NS.getASTContext();
1129*0b57cec5SDimitry Andric 
1130*0b57cec5SDimitry Andric   const Expr *OrigArg = Arg->IgnoreImpCasts();
1131*0b57cec5SDimitry Andric   QualType OrigTy = OrigArg->getType();
1132*0b57cec5SDimitry Andric   if (OrigTy->isArrayType())
1133*0b57cec5SDimitry Andric     OrigTy = Ctx.getArrayDecayedType(OrigTy);
1134*0b57cec5SDimitry Andric 
1135*0b57cec5SDimitry Andric   if (const StringLiteral *
1136*0b57cec5SDimitry Andric         StrE = dyn_cast<StringLiteral>(OrigArg->IgnoreParens())) {
1137*0b57cec5SDimitry Andric     commit.replaceWithInner(Msg->getSourceRange(), StrE->getSourceRange());
1138*0b57cec5SDimitry Andric     commit.insert(StrE->getBeginLoc(), "@");
1139*0b57cec5SDimitry Andric     return true;
1140*0b57cec5SDimitry Andric   }
1141*0b57cec5SDimitry Andric 
1142*0b57cec5SDimitry Andric   if (const PointerType *PT = OrigTy->getAs<PointerType>()) {
1143*0b57cec5SDimitry Andric     QualType PointeeType = PT->getPointeeType();
1144*0b57cec5SDimitry Andric     if (Ctx.hasSameUnqualifiedType(PointeeType, Ctx.CharTy)) {
1145*0b57cec5SDimitry Andric       SourceRange ArgRange = OrigArg->getSourceRange();
1146*0b57cec5SDimitry Andric       commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
1147*0b57cec5SDimitry Andric 
1148*0b57cec5SDimitry Andric       if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
1149*0b57cec5SDimitry Andric         commit.insertBefore(ArgRange.getBegin(), "@");
1150*0b57cec5SDimitry Andric       else
1151*0b57cec5SDimitry Andric         commit.insertWrap("@(", ArgRange, ")");
1152*0b57cec5SDimitry Andric 
1153*0b57cec5SDimitry Andric       return true;
1154*0b57cec5SDimitry Andric     }
1155*0b57cec5SDimitry Andric   }
1156*0b57cec5SDimitry Andric 
1157*0b57cec5SDimitry Andric   return false;
1158*0b57cec5SDimitry Andric }
1159*0b57cec5SDimitry Andric 
1160*0b57cec5SDimitry Andric static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
1161*0b57cec5SDimitry Andric                                            const NSAPI &NS, Commit &commit) {
1162*0b57cec5SDimitry Andric   Selector Sel = Msg->getSelector();
1163*0b57cec5SDimitry Andric 
1164*0b57cec5SDimitry Andric   if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithUTF8String) ||
1165*0b57cec5SDimitry Andric       Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCString) ||
1166*0b57cec5SDimitry Andric       Sel == NS.getNSStringSelector(NSAPI::NSStr_initWithUTF8String)) {
1167*0b57cec5SDimitry Andric     if (Msg->getNumArgs() != 1)
1168*0b57cec5SDimitry Andric       return false;
1169*0b57cec5SDimitry Andric     return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);
1170*0b57cec5SDimitry Andric   }
1171*0b57cec5SDimitry Andric 
1172*0b57cec5SDimitry Andric   if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCStringEncoding)) {
1173*0b57cec5SDimitry Andric     if (Msg->getNumArgs() != 2)
1174*0b57cec5SDimitry Andric       return false;
1175*0b57cec5SDimitry Andric 
1176*0b57cec5SDimitry Andric     const Expr *encodingArg = Msg->getArg(1);
1177*0b57cec5SDimitry Andric     if (NS.isNSUTF8StringEncodingConstant(encodingArg) ||
1178*0b57cec5SDimitry Andric         NS.isNSASCIIStringEncodingConstant(encodingArg))
1179*0b57cec5SDimitry Andric       return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);
1180*0b57cec5SDimitry Andric   }
1181*0b57cec5SDimitry Andric 
1182*0b57cec5SDimitry Andric   return false;
1183*0b57cec5SDimitry Andric }
1184