| //===--- RewriteObjCFoundationAPI.cpp - Foundation API Rewriter -----------===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // Rewrites legacy method calls to modern syntax. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "clang/Edit/Rewriters.h" |
| #include "clang/Edit/Commit.h" |
| #include "clang/Lex/Lexer.h" |
| #include "clang/AST/ExprObjC.h" |
| #include "clang/AST/ExprCXX.h" |
| #include "clang/AST/NSAPI.h" |
| |
| using namespace clang; |
| using namespace edit; |
| |
| static bool checkForLiteralCreation(const ObjCMessageExpr *Msg, |
| IdentifierInfo *&ClassId) { |
| if (!Msg || Msg->isImplicit() || !Msg->getMethodDecl()) |
| return false; |
| |
| const ObjCInterfaceDecl *Receiver = Msg->getReceiverInterface(); |
| if (!Receiver) |
| return false; |
| ClassId = Receiver->getIdentifier(); |
| |
| if (Msg->getReceiverKind() == ObjCMessageExpr::Class) |
| return true; |
| |
| return false; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // rewriteObjCRedundantCallWithLiteral. |
| //===----------------------------------------------------------------------===// |
| |
| bool edit::rewriteObjCRedundantCallWithLiteral(const ObjCMessageExpr *Msg, |
| const NSAPI &NS, Commit &commit) { |
| IdentifierInfo *II = 0; |
| if (!checkForLiteralCreation(Msg, II)) |
| return false; |
| if (Msg->getNumArgs() != 1) |
| return false; |
| |
| const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts(); |
| Selector Sel = Msg->getSelector(); |
| |
| if ((isa<ObjCStringLiteral>(Arg) && |
| NS.getNSClassId(NSAPI::ClassId_NSString) == II && |
| NS.getNSStringSelector(NSAPI::NSStr_stringWithString) == Sel) || |
| |
| (isa<ObjCArrayLiteral>(Arg) && |
| NS.getNSClassId(NSAPI::ClassId_NSArray) == II && |
| NS.getNSArraySelector(NSAPI::NSArr_arrayWithArray) == Sel) || |
| |
| (isa<ObjCDictionaryLiteral>(Arg) && |
| NS.getNSClassId(NSAPI::ClassId_NSDictionary) == II && |
| NS.getNSDictionarySelector( |
| NSAPI::NSDict_dictionaryWithDictionary) == Sel)) { |
| |
| commit.replaceWithInner(Msg->getSourceRange(), |
| Msg->getArg(0)->getSourceRange()); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // rewriteToObjCSubscriptSyntax. |
| //===----------------------------------------------------------------------===// |
| |
| static void maybePutParensOnReceiver(const Expr *Receiver, Commit &commit) { |
| Receiver = Receiver->IgnoreImpCasts(); |
| if (isa<BinaryOperator>(Receiver) || isa<UnaryOperator>(Receiver)) { |
| SourceRange RecRange = Receiver->getSourceRange(); |
| commit.insertWrap("(", RecRange, ")"); |
| } |
| } |
| |
| static bool rewriteToSubscriptGet(const ObjCMessageExpr *Msg, Commit &commit) { |
| if (Msg->getNumArgs() != 1) |
| return false; |
| const Expr *Rec = Msg->getInstanceReceiver(); |
| if (!Rec) |
| return false; |
| |
| SourceRange MsgRange = Msg->getSourceRange(); |
| SourceRange RecRange = Rec->getSourceRange(); |
| SourceRange ArgRange = Msg->getArg(0)->getSourceRange(); |
| |
| commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(), |
| ArgRange.getBegin()), |
| CharSourceRange::getTokenRange(RecRange)); |
| commit.replaceWithInner(SourceRange(ArgRange.getBegin(), MsgRange.getEnd()), |
| ArgRange); |
| commit.insertWrap("[", ArgRange, "]"); |
| maybePutParensOnReceiver(Rec, commit); |
| return true; |
| } |
| |
| static bool rewriteToArraySubscriptSet(const ObjCMessageExpr *Msg, |
| Commit &commit) { |
| if (Msg->getNumArgs() != 2) |
| return false; |
| const Expr *Rec = Msg->getInstanceReceiver(); |
| if (!Rec) |
| return false; |
| |
| SourceRange MsgRange = Msg->getSourceRange(); |
| SourceRange RecRange = Rec->getSourceRange(); |
| SourceRange Arg0Range = Msg->getArg(0)->getSourceRange(); |
| SourceRange Arg1Range = Msg->getArg(1)->getSourceRange(); |
| |
| commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(), |
| Arg0Range.getBegin()), |
| CharSourceRange::getTokenRange(RecRange)); |
| commit.replaceWithInner(CharSourceRange::getCharRange(Arg0Range.getBegin(), |
| Arg1Range.getBegin()), |
| CharSourceRange::getTokenRange(Arg0Range)); |
| commit.replaceWithInner(SourceRange(Arg1Range.getBegin(), MsgRange.getEnd()), |
| Arg1Range); |
| commit.insertWrap("[", CharSourceRange::getCharRange(Arg0Range.getBegin(), |
| Arg1Range.getBegin()), |
| "] = "); |
| maybePutParensOnReceiver(Rec, commit); |
| return true; |
| } |
| |
| static bool rewriteToDictionarySubscriptSet(const ObjCMessageExpr *Msg, |
| Commit &commit) { |
| if (Msg->getNumArgs() != 2) |
| return false; |
| const Expr *Rec = Msg->getInstanceReceiver(); |
| if (!Rec) |
| return false; |
| |
| SourceRange MsgRange = Msg->getSourceRange(); |
| SourceRange RecRange = Rec->getSourceRange(); |
| SourceRange Arg0Range = Msg->getArg(0)->getSourceRange(); |
| SourceRange Arg1Range = Msg->getArg(1)->getSourceRange(); |
| |
| SourceLocation LocBeforeVal = Arg0Range.getBegin(); |
| commit.insertBefore(LocBeforeVal, "] = "); |
| commit.insertFromRange(LocBeforeVal, Arg1Range, /*afterToken=*/false, |
| /*beforePreviousInsertions=*/true); |
| commit.insertBefore(LocBeforeVal, "["); |
| commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(), |
| Arg0Range.getBegin()), |
| CharSourceRange::getTokenRange(RecRange)); |
| commit.replaceWithInner(SourceRange(Arg0Range.getBegin(), MsgRange.getEnd()), |
| Arg0Range); |
| maybePutParensOnReceiver(Rec, commit); |
| return true; |
| } |
| |
| bool edit::rewriteToObjCSubscriptSyntax(const ObjCMessageExpr *Msg, |
| const NSAPI &NS, Commit &commit) { |
| if (!Msg || Msg->isImplicit() || |
| Msg->getReceiverKind() != ObjCMessageExpr::Instance) |
| return false; |
| const ObjCMethodDecl *Method = Msg->getMethodDecl(); |
| if (!Method) |
| return false; |
| |
| const ObjCInterfaceDecl * |
| IFace = NS.getASTContext().getObjContainingInterface( |
| const_cast<ObjCMethodDecl *>(Method)); |
| if (!IFace) |
| return false; |
| IdentifierInfo *II = IFace->getIdentifier(); |
| Selector Sel = Msg->getSelector(); |
| |
| if ((II == NS.getNSClassId(NSAPI::ClassId_NSArray) && |
| Sel == NS.getNSArraySelector(NSAPI::NSArr_objectAtIndex)) || |
| (II == NS.getNSClassId(NSAPI::ClassId_NSDictionary) && |
| Sel == NS.getNSDictionarySelector(NSAPI::NSDict_objectForKey))) |
| return rewriteToSubscriptGet(Msg, commit); |
| |
| if (Msg->getNumArgs() != 2) |
| return false; |
| |
| if (II == NS.getNSClassId(NSAPI::ClassId_NSMutableArray) && |
| Sel == NS.getNSArraySelector(NSAPI::NSMutableArr_replaceObjectAtIndex)) |
| return rewriteToArraySubscriptSet(Msg, commit); |
| |
| if (II == NS.getNSClassId(NSAPI::ClassId_NSMutableDictionary) && |
| Sel == NS.getNSDictionarySelector(NSAPI::NSMutableDict_setObjectForKey)) |
| return rewriteToDictionarySubscriptSet(Msg, commit); |
| |
| return false; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // rewriteToObjCLiteralSyntax. |
| //===----------------------------------------------------------------------===// |
| |
| static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg, |
| const NSAPI &NS, Commit &commit); |
| static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg, |
| const NSAPI &NS, Commit &commit); |
| static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg, |
| const NSAPI &NS, Commit &commit); |
| |
| bool edit::rewriteToObjCLiteralSyntax(const ObjCMessageExpr *Msg, |
| const NSAPI &NS, Commit &commit) { |
| IdentifierInfo *II = 0; |
| if (!checkForLiteralCreation(Msg, II)) |
| return false; |
| |
| if (II == NS.getNSClassId(NSAPI::ClassId_NSArray)) |
| return rewriteToArrayLiteral(Msg, NS, commit); |
| if (II == NS.getNSClassId(NSAPI::ClassId_NSDictionary)) |
| return rewriteToDictionaryLiteral(Msg, NS, commit); |
| if (II == NS.getNSClassId(NSAPI::ClassId_NSNumber)) |
| return rewriteToNumberLiteral(Msg, NS, commit); |
| |
| return false; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // rewriteToArrayLiteral. |
| //===----------------------------------------------------------------------===// |
| |
| /// \brief Adds an explicit cast to 'id' if the type is not objc object. |
| static void objectifyExpr(const Expr *E, Commit &commit); |
| |
| static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg, |
| const NSAPI &NS, Commit &commit) { |
| Selector Sel = Msg->getSelector(); |
| SourceRange MsgRange = Msg->getSourceRange(); |
| |
| if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array)) { |
| if (Msg->getNumArgs() != 0) |
| return false; |
| commit.replace(MsgRange, "@[]"); |
| return true; |
| } |
| |
| if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) { |
| if (Msg->getNumArgs() != 1) |
| return false; |
| objectifyExpr(Msg->getArg(0), commit); |
| SourceRange ArgRange = Msg->getArg(0)->getSourceRange(); |
| commit.replaceWithInner(MsgRange, ArgRange); |
| commit.insertWrap("@[", ArgRange, "]"); |
| return true; |
| } |
| |
| if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects)) { |
| 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) |
| objectifyExpr(Msg->getArg(i), commit); |
| |
| if (Msg->getNumArgs() == 1) { |
| commit.replace(MsgRange, "@[]"); |
| return true; |
| } |
| SourceRange ArgRange(Msg->getArg(0)->getLocStart(), |
| Msg->getArg(Msg->getNumArgs()-2)->getLocEnd()); |
| commit.replaceWithInner(MsgRange, ArgRange); |
| commit.insertWrap("@[", ArgRange, "]"); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // rewriteToDictionaryLiteral. |
| //===----------------------------------------------------------------------===// |
| |
| static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg, |
| const NSAPI &NS, Commit &commit) { |
| Selector Sel = Msg->getSelector(); |
| SourceRange MsgRange = Msg->getSourceRange(); |
| |
| if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_dictionary)) { |
| if (Msg->getNumArgs() != 0) |
| return false; |
| commit.replace(MsgRange, "@{}"); |
| return true; |
| } |
| |
| if (Sel == NS.getNSDictionarySelector( |
| NSAPI::NSDict_dictionaryWithObjectForKey)) { |
| if (Msg->getNumArgs() != 2) |
| return false; |
| |
| objectifyExpr(Msg->getArg(0), commit); |
| objectifyExpr(Msg->getArg(1), commit); |
| |
| SourceRange ValRange = Msg->getArg(0)->getSourceRange(); |
| SourceRange KeyRange = Msg->getArg(1)->getSourceRange(); |
| // Insert key before the value. |
| commit.insertBefore(ValRange.getBegin(), ": "); |
| commit.insertFromRange(ValRange.getBegin(), |
| CharSourceRange::getTokenRange(KeyRange), |
| /*afterToken=*/false, /*beforePreviousInsertions=*/true); |
| commit.insertBefore(ValRange.getBegin(), "@{"); |
| commit.insertAfterToken(ValRange.getEnd(), "}"); |
| commit.replaceWithInner(MsgRange, ValRange); |
| return true; |
| } |
| |
| if (Sel == NS.getNSDictionarySelector( |
| NSAPI::NSDict_dictionaryWithObjectsAndKeys)) { |
| if (Msg->getNumArgs() % 2 != 1) |
| return false; |
| unsigned SentinelIdx = Msg->getNumArgs() - 1; |
| const Expr *SentinelExpr = Msg->getArg(SentinelIdx); |
| if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr)) |
| return false; |
| |
| if (Msg->getNumArgs() == 1) { |
| commit.replace(MsgRange, "@{}"); |
| return true; |
| } |
| |
| for (unsigned i = 0; i < SentinelIdx; i += 2) { |
| objectifyExpr(Msg->getArg(i), commit); |
| objectifyExpr(Msg->getArg(i+1), commit); |
| |
| SourceRange ValRange = Msg->getArg(i)->getSourceRange(); |
| SourceRange KeyRange = Msg->getArg(i+1)->getSourceRange(); |
| // Insert value after key. |
| commit.insertAfterToken(KeyRange.getEnd(), ": "); |
| commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true); |
| commit.remove(CharSourceRange::getCharRange(ValRange.getBegin(), |
| KeyRange.getBegin())); |
| } |
| // Range of arguments up until and including the last key. |
| // The sentinel and first value are cut off, the value will move after the |
| // key. |
| SourceRange ArgRange(Msg->getArg(1)->getLocStart(), |
| Msg->getArg(SentinelIdx-1)->getLocEnd()); |
| commit.insertWrap("@{", ArgRange, "}"); |
| commit.replaceWithInner(MsgRange, ArgRange); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // rewriteToNumberLiteral. |
| //===----------------------------------------------------------------------===// |
| |
| static bool rewriteToCharLiteral(const ObjCMessageExpr *Msg, |
| const CharacterLiteral *Arg, |
| const NSAPI &NS, Commit &commit) { |
| if (Arg->getKind() != CharacterLiteral::Ascii) |
| return false; |
| if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithChar, |
| Msg->getSelector())) { |
| SourceRange ArgRange = Arg->getSourceRange(); |
| commit.replaceWithInner(Msg->getSourceRange(), ArgRange); |
| commit.insert(ArgRange.getBegin(), "@"); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| static bool rewriteToBoolLiteral(const ObjCMessageExpr *Msg, |
| const Expr *Arg, |
| const NSAPI &NS, Commit &commit) { |
| if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithBool, |
| Msg->getSelector())) { |
| SourceRange ArgRange = Arg->getSourceRange(); |
| commit.replaceWithInner(Msg->getSourceRange(), ArgRange); |
| commit.insert(ArgRange.getBegin(), "@"); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| namespace { |
| |
| struct LiteralInfo { |
| bool Hex, Octal; |
| StringRef U, F, L, LL; |
| CharSourceRange WithoutSuffRange; |
| }; |
| |
| } |
| |
| static bool getLiteralInfo(SourceRange literalRange, |
| bool isFloat, bool isIntZero, |
| ASTContext &Ctx, LiteralInfo &Info) { |
| if (literalRange.getBegin().isMacroID() || |
| literalRange.getEnd().isMacroID()) |
| return false; |
| StringRef text = Lexer::getSourceText( |
| CharSourceRange::getTokenRange(literalRange), |
| Ctx.getSourceManager(), Ctx.getLangOpts()); |
| if (text.empty()) |
| return false; |
| |
| llvm::Optional<bool> UpperU, UpperL; |
| bool UpperF = false; |
| |
| struct Suff { |
| static bool has(StringRef suff, StringRef &text) { |
| if (text.endswith(suff)) { |
| text = text.substr(0, text.size()-suff.size()); |
| return true; |
| } |
| return false; |
| } |
| }; |
| |
| while (1) { |
| if (Suff::has("u", text)) { |
| UpperU = false; |
| } else if (Suff::has("U", text)) { |
| UpperU = true; |
| } else if (Suff::has("ll", text)) { |
| UpperL = false; |
| } else if (Suff::has("LL", text)) { |
| UpperL = true; |
| } else if (Suff::has("l", text)) { |
| UpperL = false; |
| } else if (Suff::has("L", text)) { |
| UpperL = true; |
| } else if (isFloat && Suff::has("f", text)) { |
| UpperF = false; |
| } else if (isFloat && Suff::has("F", text)) { |
| UpperF = true; |
| } else |
| break; |
| } |
| |
| if (!UpperU.hasValue() && !UpperL.hasValue()) |
| UpperU = UpperL = true; |
| else if (UpperU.hasValue() && !UpperL.hasValue()) |
| UpperL = UpperU; |
| else if (UpperL.hasValue() && !UpperU.hasValue()) |
| UpperU = UpperL; |
| |
| Info.U = *UpperU ? "U" : "u"; |
| Info.L = *UpperL ? "L" : "l"; |
| Info.LL = *UpperL ? "LL" : "ll"; |
| Info.F = UpperF ? "F" : "f"; |
| |
| Info.Hex = Info.Octal = false; |
| if (text.startswith("0x")) |
| Info.Hex = true; |
| else if (!isFloat && !isIntZero && text.startswith("0")) |
| Info.Octal = true; |
| |
| SourceLocation B = literalRange.getBegin(); |
| Info.WithoutSuffRange = |
| CharSourceRange::getCharRange(B, B.getLocWithOffset(text.size())); |
| return true; |
| } |
| |
| static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg, |
| const NSAPI &NS, Commit &commit) { |
| if (Msg->getNumArgs() != 1) |
| return false; |
| |
| const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts(); |
| if (const CharacterLiteral *CharE = dyn_cast<CharacterLiteral>(Arg)) |
| return rewriteToCharLiteral(Msg, CharE, NS, commit); |
| if (const ObjCBoolLiteralExpr *BE = dyn_cast<ObjCBoolLiteralExpr>(Arg)) |
| return rewriteToBoolLiteral(Msg, BE, NS, commit); |
| if (const CXXBoolLiteralExpr *BE = dyn_cast<CXXBoolLiteralExpr>(Arg)) |
| return rewriteToBoolLiteral(Msg, BE, NS, commit); |
| |
| const Expr *literalE = Arg; |
| if (const UnaryOperator *UOE = dyn_cast<UnaryOperator>(literalE)) { |
| if (UOE->getOpcode() == UO_Plus || UOE->getOpcode() == UO_Minus) |
| literalE = UOE->getSubExpr(); |
| } |
| |
| // Only integer and floating literals; non-literals or imaginary literal |
| // cannot be rewritten. |
| if (!isa<IntegerLiteral>(literalE) && !isa<FloatingLiteral>(literalE)) |
| return false; |
| |
| ASTContext &Ctx = NS.getASTContext(); |
| Selector Sel = Msg->getSelector(); |
| llvm::Optional<NSAPI::NSNumberLiteralMethodKind> |
| MKOpt = NS.getNSNumberLiteralMethodKind(Sel); |
| if (!MKOpt) |
| return false; |
| NSAPI::NSNumberLiteralMethodKind MK = *MKOpt; |
| |
| bool CallIsUnsigned = false, CallIsLong = false, CallIsLongLong = false; |
| bool CallIsFloating = false, CallIsDouble = false; |
| |
| switch (MK) { |
| // We cannot have these calls with int/float literals. |
| case NSAPI::NSNumberWithChar: |
| case NSAPI::NSNumberWithUnsignedChar: |
| case NSAPI::NSNumberWithShort: |
| case NSAPI::NSNumberWithUnsignedShort: |
| case NSAPI::NSNumberWithBool: |
| return false; |
| |
| case NSAPI::NSNumberWithUnsignedInt: |
| case NSAPI::NSNumberWithUnsignedInteger: |
| CallIsUnsigned = true; |
| case NSAPI::NSNumberWithInt: |
| case NSAPI::NSNumberWithInteger: |
| break; |
| |
| case NSAPI::NSNumberWithUnsignedLong: |
| CallIsUnsigned = true; |
| case NSAPI::NSNumberWithLong: |
| CallIsLong = true; |
| break; |
| |
| case NSAPI::NSNumberWithUnsignedLongLong: |
| CallIsUnsigned = true; |
| case NSAPI::NSNumberWithLongLong: |
| CallIsLongLong = true; |
| break; |
| |
| case NSAPI::NSNumberWithDouble: |
| CallIsDouble = true; |
| case NSAPI::NSNumberWithFloat: |
| CallIsFloating = true; |
| break; |
| } |
| |
| SourceRange ArgRange = Arg->getSourceRange(); |
| QualType ArgTy = Arg->getType(); |
| QualType CallTy = Msg->getArg(0)->getType(); |
| |
| // Check for the easy case, the literal maps directly to the call. |
| if (Ctx.hasSameType(ArgTy, CallTy)) { |
| commit.replaceWithInner(Msg->getSourceRange(), ArgRange); |
| commit.insert(ArgRange.getBegin(), "@"); |
| return true; |
| } |
| |
| // We will need to modify the literal suffix to get the same type as the call. |
| // Don't even try if it came from a macro. |
| if (ArgRange.getBegin().isMacroID()) |
| return false; |
| |
| bool LitIsFloat = ArgTy->isFloatingType(); |
| // For a float passed to integer call, don't try rewriting. It is difficult |
| // and a very uncommon case anyway. |
| if (LitIsFloat && !CallIsFloating) |
| return false; |
| |
| // Try to modify the literal make it the same type as the method call. |
| // -Modify the suffix, and/or |
| // -Change integer to float |
| |
| LiteralInfo LitInfo; |
| bool isIntZero = false; |
| if (const IntegerLiteral *IntE = dyn_cast<IntegerLiteral>(literalE)) |
| isIntZero = !IntE->getValue().getBoolValue(); |
| if (!getLiteralInfo(ArgRange, LitIsFloat, isIntZero, Ctx, LitInfo)) |
| return false; |
| |
| // Not easy to do int -> float with hex/octal and uncommon anyway. |
| if (!LitIsFloat && CallIsFloating && (LitInfo.Hex || LitInfo.Octal)) |
| return false; |
| |
| SourceLocation LitB = LitInfo.WithoutSuffRange.getBegin(); |
| SourceLocation LitE = LitInfo.WithoutSuffRange.getEnd(); |
| |
| commit.replaceWithInner(CharSourceRange::getTokenRange(Msg->getSourceRange()), |
| LitInfo.WithoutSuffRange); |
| commit.insert(LitB, "@"); |
| |
| if (!LitIsFloat && CallIsFloating) |
| commit.insert(LitE, ".0"); |
| |
| if (CallIsFloating) { |
| if (!CallIsDouble) |
| commit.insert(LitE, LitInfo.F); |
| } else { |
| if (CallIsUnsigned) |
| commit.insert(LitE, LitInfo.U); |
| |
| if (CallIsLong) |
| commit.insert(LitE, LitInfo.L); |
| else if (CallIsLongLong) |
| commit.insert(LitE, LitInfo.LL); |
| } |
| return true; |
| } |
| |
| static bool castOperatorNeedsParens(const Expr *FullExpr) { |
| const Expr* Expr = FullExpr->IgnoreImpCasts(); |
| if (isa<ArraySubscriptExpr>(Expr) || |
| isa<CallExpr>(Expr) || |
| isa<DeclRefExpr>(Expr) || |
| isa<CastExpr>(Expr) || |
| isa<CXXNewExpr>(Expr) || |
| isa<CXXConstructExpr>(Expr) || |
| isa<CXXDeleteExpr>(Expr) || |
| isa<CXXNoexceptExpr>(Expr) || |
| isa<CXXPseudoDestructorExpr>(Expr) || |
| isa<CXXScalarValueInitExpr>(Expr) || |
| isa<CXXThisExpr>(Expr) || |
| isa<CXXTypeidExpr>(Expr) || |
| isa<CXXUnresolvedConstructExpr>(Expr) || |
| isa<ObjCMessageExpr>(Expr) || |
| isa<ObjCPropertyRefExpr>(Expr) || |
| isa<ObjCProtocolExpr>(Expr) || |
| isa<MemberExpr>(Expr) || |
| isa<ParenExpr>(FullExpr) || |
| isa<ParenListExpr>(Expr) || |
| isa<SizeOfPackExpr>(Expr) || |
| isa<UnaryOperator>(Expr)) |
| return false; |
| |
| return true; |
| } |
| |
| static void objectifyExpr(const Expr *E, Commit &commit) { |
| if (!E) return; |
| |
| QualType T = E->getType(); |
| if (T->isObjCObjectPointerType()) { |
| if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) { |
| if (ICE->getCastKind() != CK_CPointerToObjCPointerCast) |
| return; |
| } else { |
| return; |
| } |
| } else if (!T->isPointerType()) { |
| return; |
| } |
| |
| SourceRange Range = E->getSourceRange(); |
| if (castOperatorNeedsParens(E)) |
| commit.insertWrap("(", Range, ")"); |
| commit.insertBefore(Range.getBegin(), "(id)"); |
| } |