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