[objcmt] Rewrite a NSDictionary dictionaryWithObjects:forKeys: to a dictionary literal
if we can see the elements of the arrays. for example: NSDictionary *dict = [NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:@"1", @"2", nil] forKeys:[NSArray arrayWithObjects:@"A", @"B", nil]]; --> NSDictionary *dict = @{ @"A" : @"1", @"B" : @"2" }; rdar://12428166 llvm-svn: 172679
This commit is contained in:
parent
b306900200
commit
6b4f341ecd
|
@ -96,10 +96,11 @@ public:
|
|||
NSDict_dictionaryWithObjectsAndKeys,
|
||||
NSDict_initWithDictionary,
|
||||
NSDict_initWithObjectsAndKeys,
|
||||
NSDict_initWithObjectsForKeys,
|
||||
NSDict_objectForKey,
|
||||
NSMutableDict_setObjectForKey
|
||||
};
|
||||
static const unsigned NumNSDictionaryMethods = 10;
|
||||
static const unsigned NumNSDictionaryMethods = 11;
|
||||
|
||||
/// \brief The Objective-C NSDictionary selectors.
|
||||
Selector getNSDictionarySelector(NSDictionaryMethodKind MK) const;
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
namespace clang {
|
||||
class ObjCMessageExpr;
|
||||
class NSAPI;
|
||||
class ParentMap;
|
||||
|
||||
namespace edit {
|
||||
class Commit;
|
||||
|
@ -21,7 +22,8 @@ bool rewriteObjCRedundantCallWithLiteral(const ObjCMessageExpr *Msg,
|
|||
const NSAPI &NS, Commit &commit);
|
||||
|
||||
bool rewriteToObjCLiteralSyntax(const ObjCMessageExpr *Msg,
|
||||
const NSAPI &NS, Commit &commit);
|
||||
const NSAPI &NS, Commit &commit,
|
||||
const ParentMap *PMap);
|
||||
|
||||
bool rewriteToObjCSubscriptSyntax(const ObjCMessageExpr *Msg,
|
||||
const NSAPI &NS, Commit &commit);
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "clang/AST/ASTConsumer.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/AST/NSAPI.h"
|
||||
#include "clang/AST/ParentMap.h"
|
||||
#include "clang/AST/RecursiveASTVisitor.h"
|
||||
#include "clang/Basic/FileManager.h"
|
||||
#include "clang/Edit/Commit.h"
|
||||
|
@ -120,9 +121,11 @@ bool ObjCMigrateAction::BeginInvocation(CompilerInstance &CI) {
|
|||
namespace {
|
||||
class ObjCMigrator : public RecursiveASTVisitor<ObjCMigrator> {
|
||||
ObjCMigrateASTConsumer &Consumer;
|
||||
ParentMap &PMap;
|
||||
|
||||
public:
|
||||
ObjCMigrator(ObjCMigrateASTConsumer &consumer) : Consumer(consumer) { }
|
||||
ObjCMigrator(ObjCMigrateASTConsumer &consumer, ParentMap &PMap)
|
||||
: Consumer(consumer), PMap(PMap) { }
|
||||
|
||||
bool shouldVisitTemplateInstantiations() const { return false; }
|
||||
bool shouldWalkTypesOfTypeLocs() const { return false; }
|
||||
|
@ -130,7 +133,7 @@ public:
|
|||
bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
|
||||
if (Consumer.MigrateLiterals) {
|
||||
edit::Commit commit(*Consumer.Editor);
|
||||
edit::rewriteToObjCLiteralSyntax(E, *Consumer.NSAPIObj, commit);
|
||||
edit::rewriteToObjCLiteralSyntax(E, *Consumer.NSAPIObj, commit, &PMap);
|
||||
Consumer.Editor->commit(commit);
|
||||
}
|
||||
|
||||
|
@ -153,6 +156,23 @@ public:
|
|||
return WalkUpFromObjCMessageExpr(E);
|
||||
}
|
||||
};
|
||||
|
||||
class BodyMigrator : public RecursiveASTVisitor<BodyMigrator> {
|
||||
ObjCMigrateASTConsumer &Consumer;
|
||||
OwningPtr<ParentMap> PMap;
|
||||
|
||||
public:
|
||||
BodyMigrator(ObjCMigrateASTConsumer &consumer) : Consumer(consumer) { }
|
||||
|
||||
bool shouldVisitTemplateInstantiations() const { return false; }
|
||||
bool shouldWalkTypesOfTypeLocs() const { return false; }
|
||||
|
||||
bool TraverseStmt(Stmt *S) {
|
||||
PMap.reset(new ParentMap(S));
|
||||
ObjCMigrator(Consumer, *PMap).TraverseStmt(S);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void ObjCMigrateASTConsumer::migrateDecl(Decl *D) {
|
||||
|
@ -161,7 +181,7 @@ void ObjCMigrateASTConsumer::migrateDecl(Decl *D) {
|
|||
if (isa<ObjCMethodDecl>(D))
|
||||
return; // Wait for the ObjC container declaration.
|
||||
|
||||
ObjCMigrator(*this).TraverseDecl(D);
|
||||
BodyMigrator(*this).TraverseDecl(D);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
|
|
@ -186,6 +186,14 @@ Selector NSAPI::getNSDictionarySelector(
|
|||
Sel = Ctx.Selectors.getUnarySelector(
|
||||
&Ctx.Idents.get("initWithObjectsAndKeys"));
|
||||
break;
|
||||
case NSDict_initWithObjectsForKeys: {
|
||||
IdentifierInfo *KeyIdents[] = {
|
||||
&Ctx.Idents.get("initWithObjects"),
|
||||
&Ctx.Idents.get("forKeys")
|
||||
};
|
||||
Sel = Ctx.Selectors.getSelector(2, KeyIdents);
|
||||
break;
|
||||
}
|
||||
case NSDict_objectForKey:
|
||||
Sel = Ctx.Selectors.getUnarySelector(&Ctx.Idents.get("objectForKey"));
|
||||
break;
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "clang/AST/ExprCXX.h"
|
||||
#include "clang/AST/ExprObjC.h"
|
||||
#include "clang/AST/NSAPI.h"
|
||||
#include "clang/AST/ParentMap.h"
|
||||
#include "clang/Edit/Commit.h"
|
||||
#include "clang/Lex/Lexer.h"
|
||||
|
||||
|
@ -325,7 +326,8 @@ bool edit::rewriteToObjCSubscriptSyntax(const ObjCMessageExpr *Msg,
|
|||
//===----------------------------------------------------------------------===//
|
||||
|
||||
static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
|
||||
const NSAPI &NS, Commit &commit);
|
||||
const NSAPI &NS, Commit &commit,
|
||||
const ParentMap *PMap);
|
||||
static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
|
||||
const NSAPI &NS, Commit &commit);
|
||||
static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
|
||||
|
@ -336,13 +338,14 @@ static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
|
|||
const NSAPI &NS, Commit &commit);
|
||||
|
||||
bool edit::rewriteToObjCLiteralSyntax(const ObjCMessageExpr *Msg,
|
||||
const NSAPI &NS, Commit &commit) {
|
||||
const NSAPI &NS, Commit &commit,
|
||||
const ParentMap *PMap) {
|
||||
IdentifierInfo *II = 0;
|
||||
if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
|
||||
return false;
|
||||
|
||||
if (II == NS.getNSClassId(NSAPI::ClassId_NSArray))
|
||||
return rewriteToArrayLiteral(Msg, NS, commit);
|
||||
return rewriteToArrayLiteral(Msg, NS, commit, PMap);
|
||||
if (II == NS.getNSClassId(NSAPI::ClassId_NSDictionary))
|
||||
return rewriteToDictionaryLiteral(Msg, NS, commit);
|
||||
if (II == NS.getNSClassId(NSAPI::ClassId_NSNumber))
|
||||
|
@ -353,6 +356,19 @@ bool edit::rewriteToObjCLiteralSyntax(const ObjCMessageExpr *Msg,
|
|||
return false;
|
||||
}
|
||||
|
||||
/// \brief Returns true if the immediate message arguments of \c Msg should not
|
||||
/// be rewritten because it will interfere with the rewrite of the parent
|
||||
/// message expression. e.g.
|
||||
/// \code
|
||||
/// [NSDictionary dictionaryWithObjects:
|
||||
/// [NSArray arrayWithObjects:@"1", @"2", nil]
|
||||
/// forKeys:[NSArray arrayWithObjects:@"A", @"B", nil]];
|
||||
/// \endcode
|
||||
/// It will return true for this because we are going to rewrite this directly
|
||||
/// to a dictionary literal without any array literals.
|
||||
static bool shouldNotRewriteImmediateMessageArgs(const ObjCMessageExpr *Msg,
|
||||
const NSAPI &NS);
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// rewriteToArrayLiteral.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -361,7 +377,15 @@ bool edit::rewriteToObjCLiteralSyntax(const ObjCMessageExpr *Msg,
|
|||
static void objectifyExpr(const Expr *E, Commit &commit);
|
||||
|
||||
static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
|
||||
const NSAPI &NS, Commit &commit) {
|
||||
const NSAPI &NS, Commit &commit,
|
||||
const ParentMap *PMap) {
|
||||
if (PMap) {
|
||||
const ObjCMessageExpr *ParentMsg =
|
||||
dyn_cast_or_null<ObjCMessageExpr>(PMap->getParentIgnoreParenCasts(Msg));
|
||||
if (shouldNotRewriteImmediateMessageArgs(ParentMsg, NS))
|
||||
return false;
|
||||
}
|
||||
|
||||
Selector Sel = Msg->getSelector();
|
||||
SourceRange MsgRange = Msg->getSourceRange();
|
||||
|
||||
|
@ -411,6 +435,59 @@ static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
|
|||
// rewriteToDictionaryLiteral.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// \brief If \c Msg is an NSArray creation message or literal, this gets the
|
||||
/// objects that were used to create it.
|
||||
/// \returns true if it is an NSArray and we got objects, or false otherwise.
|
||||
static bool getNSArrayObjects(const Expr *E, const NSAPI &NS,
|
||||
SmallVectorImpl<const Expr *> &Objs) {
|
||||
if (!E)
|
||||
return false;
|
||||
|
||||
E = E->IgnoreParenCasts();
|
||||
if (!E)
|
||||
return false;
|
||||
|
||||
if (const ObjCMessageExpr *Msg = dyn_cast<ObjCMessageExpr>(E)) {
|
||||
IdentifierInfo *Cls = 0;
|
||||
if (!checkForLiteralCreation(Msg, Cls, NS.getASTContext().getLangOpts()))
|
||||
return false;
|
||||
|
||||
if (Cls != NS.getNSClassId(NSAPI::ClassId_NSArray))
|
||||
return false;
|
||||
|
||||
Selector Sel = Msg->getSelector();
|
||||
if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array))
|
||||
return true; // empty array.
|
||||
|
||||
if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) {
|
||||
if (Msg->getNumArgs() != 1)
|
||||
return false;
|
||||
Objs.push_back(Msg->getArg(0));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) ||
|
||||
Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) {
|
||||
if (Msg->getNumArgs() == 0)
|
||||
return false;
|
||||
const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1);
|
||||
if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
|
||||
return false;
|
||||
|
||||
for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i)
|
||||
Objs.push_back(Msg->getArg(i));
|
||||
return true;
|
||||
}
|
||||
|
||||
} else if (const ObjCArrayLiteral *ArrLit = dyn_cast<ObjCArrayLiteral>(E)) {
|
||||
for (unsigned i = 0, e = ArrLit->getNumElements(); i != e; ++i)
|
||||
Objs.push_back(ArrLit->getElement(i));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
|
||||
const NSAPI &NS, Commit &commit) {
|
||||
Selector Sel = Msg->getSelector();
|
||||
|
@ -481,6 +558,83 @@ static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
|
|||
return true;
|
||||
}
|
||||
|
||||
if (Sel == NS.getNSDictionarySelector(
|
||||
NSAPI::NSDict_dictionaryWithObjectsForKeys) ||
|
||||
Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsForKeys)) {
|
||||
if (Msg->getNumArgs() != 2)
|
||||
return false;
|
||||
|
||||
SmallVector<const Expr *, 8> Vals;
|
||||
if (!getNSArrayObjects(Msg->getArg(0), NS, Vals))
|
||||
return false;
|
||||
|
||||
SmallVector<const Expr *, 8> Keys;
|
||||
if (!getNSArrayObjects(Msg->getArg(1), NS, Keys))
|
||||
return false;
|
||||
|
||||
if (Vals.size() != Keys.size())
|
||||
return false;
|
||||
|
||||
if (Vals.empty()) {
|
||||
commit.replace(MsgRange, "@{}");
|
||||
return true;
|
||||
}
|
||||
|
||||
for (unsigned i = 0, n = Vals.size(); i < n; ++i) {
|
||||
objectifyExpr(Vals[i], commit);
|
||||
objectifyExpr(Keys[i], commit);
|
||||
|
||||
SourceRange ValRange = Vals[i]->getSourceRange();
|
||||
SourceRange KeyRange = Keys[i]->getSourceRange();
|
||||
// Insert value after key.
|
||||
commit.insertAfterToken(KeyRange.getEnd(), ": ");
|
||||
commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true);
|
||||
}
|
||||
// Range of arguments up until and including the last key.
|
||||
// The first value is cut off, the value will move after the key.
|
||||
SourceRange ArgRange(Keys.front()->getLocStart(),
|
||||
Keys.back()->getLocEnd());
|
||||
commit.insertWrap("@{", ArgRange, "}");
|
||||
commit.replaceWithInner(MsgRange, ArgRange);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool shouldNotRewriteImmediateMessageArgs(const ObjCMessageExpr *Msg,
|
||||
const NSAPI &NS) {
|
||||
if (!Msg)
|
||||
return false;
|
||||
|
||||
IdentifierInfo *II = 0;
|
||||
if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
|
||||
return false;
|
||||
|
||||
if (II != NS.getNSClassId(NSAPI::ClassId_NSDictionary))
|
||||
return false;
|
||||
|
||||
Selector Sel = Msg->getSelector();
|
||||
if (Sel == NS.getNSDictionarySelector(
|
||||
NSAPI::NSDict_dictionaryWithObjectsForKeys) ||
|
||||
Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsForKeys)) {
|
||||
if (Msg->getNumArgs() != 2)
|
||||
return false;
|
||||
|
||||
SmallVector<const Expr *, 8> Vals;
|
||||
if (!getNSArrayObjects(Msg->getArg(0), NS, Vals))
|
||||
return false;
|
||||
|
||||
SmallVector<const Expr *, 8> Keys;
|
||||
if (!getNSArrayObjects(Msg->getArg(1), NS, Keys))
|
||||
return false;
|
||||
|
||||
if (Vals.size() != Keys.size())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -101,6 +101,8 @@ typedef const struct __CFString * CFStringRef;
|
|||
dict = [NSDictionary dictionaryWithObjectsAndKeys: @"value1", @"key1", @"value2", @"key2", nil];
|
||||
dict = [[NSDictionary alloc] initWithObjectsAndKeys: @"value1", @"key1", @"value2", @"key2", nil];
|
||||
|
||||
dict = [[NSDictionary alloc] initWithObjects:[[NSArray alloc] initWithObjects:@"1", @"2", nil] forKeys:[NSArray arrayWithObjects:@"A", @"B", nil]];
|
||||
|
||||
NSNumber *n = [[NSNumber alloc] initWithInt:2];
|
||||
}
|
||||
@end
|
||||
|
|
|
@ -101,6 +101,8 @@ typedef const struct __CFString * CFStringRef;
|
|||
dict = @{@"key1": @"value1", @"key2": @"value2"};
|
||||
dict = @{@"key1": @"value1", @"key2": @"value2"};
|
||||
|
||||
dict = @{@"A": @"1", @"B": @"2"};
|
||||
|
||||
NSNumber *n = @2;
|
||||
}
|
||||
@end
|
||||
|
|
|
@ -153,6 +153,10 @@ typedef const struct __CFString * CFStringRef;
|
|||
void *hd;
|
||||
o = [(NSArray*)hd objectAtIndex:2];
|
||||
o = [ivarArr objectAtIndex:2];
|
||||
|
||||
dict = [NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:@"1", [NSArray array], nil] forKeys:[NSArray arrayWithObjects:@"A", [arr objectAtIndex:2], nil]];
|
||||
dict = [NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:@"1", @"2", nil] forKeys:arr];
|
||||
dict = [NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:@"1", @"2", nil] forKeys:@[@"A", @"B"]];
|
||||
}
|
||||
@end
|
||||
|
||||
|
|
|
@ -153,6 +153,10 @@ typedef const struct __CFString * CFStringRef;
|
|||
void *hd;
|
||||
o = ((NSArray*)hd)[2];
|
||||
o = ivarArr[2];
|
||||
|
||||
dict = @{@"A": @"1", arr[2]: @[]};
|
||||
dict = [NSDictionary dictionaryWithObjects:@[@"1", @"2"] forKeys:arr];
|
||||
dict = @{@"A": @"1", @"B": @"2"};
|
||||
}
|
||||
@end
|
||||
|
||||
|
|
Loading…
Reference in New Issue