Index: clang/include/clang/Basic/SourceLocation.h =================================================================== --- clang/include/clang/Basic/SourceLocation.h +++ clang/include/clang/Basic/SourceLocation.h @@ -16,6 +16,7 @@ #define LLVM_CLANG_BASIC_SOURCELOCATION_H #include "clang/Basic/LLVM.h" +#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/PointerLikeTypeTraits.h" #include @@ -194,6 +195,32 @@ return LHS.getRawEncoding() < RHS.getRawEncoding(); } +/// \brief Represents either a single location or multiple locations for +/// Objective-C selectors. +/// +/// Can be used transparently in places where SourceLocation is expected. +class MultiSourceLocation { + bool IsSingleLoc; + union { + SourceLocation Loc; + ArrayRef Locs; + }; + +public: + MultiSourceLocation() : IsSingleLoc(true), Loc(SourceLocation()) {} + MultiSourceLocation(SourceLocation Loc) : IsSingleLoc(true), Loc(Loc) {} + explicit MultiSourceLocation(ArrayRef Locs) + : IsSingleLoc(false), Locs(Locs) { + assert(!Locs.empty() && "Require at least one location"); + } + + operator SourceLocation() const { return IsSingleLoc ? Loc : Locs.front(); } + + ArrayRef asArray() const { + return IsSingleLoc ? llvm::makeArrayRef(Loc) : Locs; + } +}; + /// \brief A trivial tuple used to represent a source range. class SourceRange { SourceLocation B; Index: clang/include/clang/Sema/DelayedDiagnostic.h =================================================================== --- clang/include/clang/Sema/DelayedDiagnostic.h +++ clang/include/clang/Sema/DelayedDiagnostic.h @@ -138,7 +138,7 @@ void Destroy(); static DelayedDiagnostic makeAvailability(AvailabilityResult AR, - SourceLocation Loc, + MultiSourceLocation Locs, const NamedDecl *ReferringDecl, const NamedDecl *OffendingDecl, const ObjCInterfaceDecl *UnknownObjCClass, @@ -146,7 +146,6 @@ StringRef Msg, bool ObjCPropertyAccess); - static DelayedDiagnostic makeAccess(SourceLocation Loc, const AccessedEntity &Entity) { DelayedDiagnostic DD; @@ -194,6 +193,12 @@ return StringRef(AvailabilityData.Message, AvailabilityData.MessageLen); } + MultiSourceLocation getAvailabilitySelectorLocs() const { + assert(Kind == Availability && "Not an availability diagnostic."); + return MultiSourceLocation(llvm::makeArrayRef( + AvailabilityData.SelectorLocs, AvailabilityData.NumSelectorLocs)); + } + AvailabilityResult getAvailabilityResult() const { assert(Kind == Availability && "Not an availability diagnostic."); return AvailabilityData.AR; @@ -238,6 +243,8 @@ const ObjCPropertyDecl *ObjCProperty; const char *Message; size_t MessageLen; + SourceLocation *SelectorLocs; + size_t NumSelectorLocs; AvailabilityResult AR; bool ObjCPropertyAccess; }; Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -3947,7 +3947,7 @@ void redelayDiagnostics(sema::DelayedDiagnosticPool &pool); - void DiagnoseAvailabilityOfDecl(NamedDecl *D, SourceLocation Loc, + void DiagnoseAvailabilityOfDecl(NamedDecl *D, MultiSourceLocation Locs, const ObjCInterfaceDecl *UnknownObjCClass, bool ObjCPropertyAccess, bool AvoidPartialAvailabilityChecks = false); @@ -3962,7 +3962,7 @@ // Expression Parsing Callbacks: SemaExpr.cpp. bool CanUseDecl(NamedDecl *D, bool TreatUnavailableAsInvalid); - bool DiagnoseUseOfDecl(NamedDecl *D, SourceLocation Loc, + bool DiagnoseUseOfDecl(NamedDecl *D, MultiSourceLocation Locs, const ObjCInterfaceDecl *UnknownObjCClass = nullptr, bool ObjCPropertyAccess = false, bool AvoidPartialAvailabilityChecks = false); Index: clang/lib/Sema/DelayedDiagnostic.cpp =================================================================== --- clang/lib/Sema/DelayedDiagnostic.cpp +++ clang/lib/Sema/DelayedDiagnostic.cpp @@ -23,7 +23,7 @@ DelayedDiagnostic DelayedDiagnostic::makeAvailability(AvailabilityResult AR, - SourceLocation Loc, + MultiSourceLocation Locs, const NamedDecl *ReferringDecl, const NamedDecl *OffendingDecl, const ObjCInterfaceDecl *UnknownObjCClass, @@ -33,7 +33,7 @@ DelayedDiagnostic DD; DD.Kind = Availability; DD.Triggered = false; - DD.Loc = Loc; + DD.Loc = Locs; DD.AvailabilityData.ReferringDecl = ReferringDecl; DD.AvailabilityData.OffendingDecl = OffendingDecl; DD.AvailabilityData.UnknownObjCClass = UnknownObjCClass; @@ -43,9 +43,16 @@ MessageData = new char [Msg.size()]; memcpy(MessageData, Msg.data(), Msg.size()); } - DD.AvailabilityData.Message = MessageData; DD.AvailabilityData.MessageLen = Msg.size(); + + ArrayRef SelLocs = Locs.asArray(); + assert(!SelLocs.empty()); + DD.AvailabilityData.SelectorLocs = new SourceLocation[SelLocs.size()]; + memcpy(DD.AvailabilityData.SelectorLocs, SelLocs.data(), + sizeof(SourceLocation) * SelLocs.size()); + DD.AvailabilityData.NumSelectorLocs = SelLocs.size(); + DD.AvailabilityData.AR = AR; DD.AvailabilityData.ObjCPropertyAccess = ObjCPropertyAccess; return DD; @@ -59,6 +66,7 @@ case Availability: delete[] AvailabilityData.Message; + delete[] AvailabilityData.SelectorLocs; break; case ForbiddenType: Index: clang/lib/Sema/SemaDeclAttr.cpp =================================================================== --- clang/lib/Sema/SemaDeclAttr.cpp +++ clang/lib/Sema/SemaDeclAttr.cpp @@ -6931,6 +6931,42 @@ } // end anonymous namespace +// Returns a number of method parameters if parsing is successful. +// In case of unsuccessful parsing SlotNames can contain invalid data. +static Optional +parseObjCMethodName(StringRef Name, SmallVectorImpl &SlotNames, + const LangOptions &LangOpts) { + // Accept replacements starting with - or + as valid ObjC method names. + if (!Name.empty() && (Name.front() == '-' || Name.front() == '+')) + Name = Name.drop_front(1); + if (Name.empty()) + return None; + Name.split(SlotNames, ':'); + unsigned NumParams; + if (Name.back() == ':') { + // Remove an empty string at the end that doesn't represent any slot. + SlotNames.pop_back(); + NumParams = SlotNames.size(); + } else { + if (SlotNames.size() != 1) + // Not a valid method name, just a colon-separated string. + return None; + NumParams = 0; + } + // Verify all slot names are valid identifiers. + bool AllowDollar = LangOpts.DollarIdents; + for (StringRef S : SlotNames) { + if (S.empty()) + continue; + if (!isIdentifierHead(S[0], AllowDollar)) + return None; + for (char C : S) + if (!isIdentifierBody(C, AllowDollar)) + return None; + } + return NumParams; +} + /// Returns a source location in which it's appropriate to insert a new /// attribute for the given declaration \D. static Optional @@ -6967,7 +7003,8 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, Decl *Ctx, const NamedDecl *ReferringDecl, const NamedDecl *OffendingDecl, - StringRef Message, SourceLocation Loc, + StringRef Message, + MultiSourceLocation Locs, const ObjCInterfaceDecl *UnknownObjCClass, const ObjCPropertyDecl *ObjCProperty, bool ObjCPropertyAccess) { @@ -6989,6 +7026,8 @@ if (!ShouldDiagnoseAvailabilityInContext(S, K, DeclVersion, Ctx)) return; + SourceLocation Loc = Locs; + // The declaration can have multiple availability attributes, we are looking // at one of them. const AvailabilityAttr *A = getAttrForPlatform(S.Context, OffendingDecl); @@ -7134,37 +7173,56 @@ llvm_unreachable("Warning for availability of available declaration?"); } - CharSourceRange UseRange; - StringRef Replacement; + SmallVector FixIts; if (K == AR_Deprecated) { + StringRef Replacement; if (auto AL = OffendingDecl->getAttr()) Replacement = AL->getReplacement(); if (auto AL = getAttrForPlatform(S.Context, OffendingDecl)) Replacement = AL->getReplacement(); + CharSourceRange UseRange; if (!Replacement.empty()) UseRange = CharSourceRange::getCharRange(Loc, S.getLocForEndOfToken(Loc)); + if (UseRange.isValid()) { + if (const auto *MethodDecl = dyn_cast(ReferringDecl)) { + Selector Sel = MethodDecl->getSelector(); + SmallVector SelectorSlotNames; + Optional NumParams = parseObjCMethodName( + Replacement, SelectorSlotNames, S.getLangOpts()); + if (NumParams && NumParams.getValue() == Sel.getNumArgs()) { + ArrayRef SelectorLocs = Locs.asArray(); + assert(SelectorSlotNames.size() == SelectorLocs.size()); + for (unsigned I = 0; I < SelectorLocs.size(); ++I) { + if (!Sel.getNameForSlot(I).empty()) { + CharSourceRange NameRange = CharSourceRange::getCharRange( + SelectorLocs[I], S.getLocForEndOfToken(SelectorLocs[I])); + FixIts.push_back(FixItHint::CreateReplacement( + NameRange, SelectorSlotNames[I])); + } else + FixIts.push_back(FixItHint::CreateInsertion( + SelectorLocs[I], SelectorSlotNames[I])); + } + } else + FixIts.push_back(FixItHint::CreateReplacement(UseRange, Replacement)); + } else + FixIts.push_back(FixItHint::CreateReplacement(UseRange, Replacement)); + } } if (!Message.empty()) { - S.Diag(Loc, diag_message) << ReferringDecl << Message - << (UseRange.isValid() ? - FixItHint::CreateReplacement(UseRange, Replacement) : FixItHint()); + S.Diag(Loc, diag_message) << ReferringDecl << Message << FixIts; if (ObjCProperty) S.Diag(ObjCProperty->getLocation(), diag::note_property_attribute) << ObjCProperty->getDeclName() << property_note_select; } else if (!UnknownObjCClass) { - S.Diag(Loc, diag) << ReferringDecl - << (UseRange.isValid() ? - FixItHint::CreateReplacement(UseRange, Replacement) : FixItHint()); + S.Diag(Loc, diag) << ReferringDecl << FixIts; if (ObjCProperty) S.Diag(ObjCProperty->getLocation(), diag::note_property_attribute) << ObjCProperty->getDeclName() << property_note_select; } else { - S.Diag(Loc, diag_fwdclass_message) << ReferringDecl - << (UseRange.isValid() ? - FixItHint::CreateReplacement(UseRange, Replacement) : FixItHint()); + S.Diag(Loc, diag_fwdclass_message) << ReferringDecl << FixIts; S.Diag(UnknownObjCClass->getLocation(), diag::note_forward_class); } @@ -7180,8 +7238,9 @@ DD.Triggered = true; DoEmitAvailabilityWarning( S, DD.getAvailabilityResult(), Ctx, DD.getAvailabilityReferringDecl(), - DD.getAvailabilityOffendingDecl(), DD.getAvailabilityMessage(), DD.Loc, - DD.getUnknownObjCClass(), DD.getObjCProperty(), false); + DD.getAvailabilityOffendingDecl(), DD.getAvailabilityMessage(), + DD.getAvailabilitySelectorLocs(), DD.getUnknownObjCClass(), + DD.getObjCProperty(), false); } void Sema::PopParsingDeclaration(ParsingDeclState state, Decl *decl) { @@ -7242,7 +7301,7 @@ static void EmitAvailabilityWarning(Sema &S, AvailabilityResult AR, const NamedDecl *ReferringDecl, const NamedDecl *OffendingDecl, - StringRef Message, SourceLocation Loc, + StringRef Message, MultiSourceLocation Locs, const ObjCInterfaceDecl *UnknownObjCClass, const ObjCPropertyDecl *ObjCProperty, bool ObjCPropertyAccess) { @@ -7250,14 +7309,14 @@ if (S.DelayedDiagnostics.shouldDelayDiagnostics()) { S.DelayedDiagnostics.add( DelayedDiagnostic::makeAvailability( - AR, Loc, ReferringDecl, OffendingDecl, UnknownObjCClass, + AR, Locs, ReferringDecl, OffendingDecl, UnknownObjCClass, ObjCProperty, Message, ObjCPropertyAccess)); return; } Decl *Ctx = cast(S.getCurLexicalContext()); DoEmitAvailabilityWarning(S, AR, Ctx, ReferringDecl, OffendingDecl, - Message, Loc, UnknownObjCClass, ObjCProperty, + Message, Locs, UnknownObjCClass, ObjCProperty, ObjCPropertyAccess); } @@ -7595,7 +7654,7 @@ DiagnoseUnguardedAvailability(*this, D).IssueDiagnostics(Body); } -void Sema::DiagnoseAvailabilityOfDecl(NamedDecl *D, SourceLocation Loc, +void Sema::DiagnoseAvailabilityOfDecl(NamedDecl *D, MultiSourceLocation Locs, const ObjCInterfaceDecl *UnknownObjCClass, bool ObjCPropertyAccess, bool AvoidPartialAvailabilityChecks) { @@ -7632,6 +7691,6 @@ } } - EmitAvailabilityWarning(*this, Result, D, OffendingDecl, Message, Loc, + EmitAvailabilityWarning(*this, Result, D, OffendingDecl, Message, Locs, UnknownObjCClass, ObjCPDecl, ObjCPropertyAccess); } Index: clang/lib/Sema/SemaExpr.cpp =================================================================== --- clang/lib/Sema/SemaExpr.cpp +++ clang/lib/Sema/SemaExpr.cpp @@ -201,10 +201,11 @@ /// \returns true if there was an error (this declaration cannot be /// referenced), false otherwise. /// -bool Sema::DiagnoseUseOfDecl(NamedDecl *D, SourceLocation Loc, +bool Sema::DiagnoseUseOfDecl(NamedDecl *D, MultiSourceLocation Locs, const ObjCInterfaceDecl *UnknownObjCClass, bool ObjCPropertyAccess, bool AvoidPartialAvailabilityChecks) { + SourceLocation Loc = Locs; if (getLangOpts().CPlusPlus && isa(D)) { // If there were any diagnostics suppressed by template argument deduction, // emit them now. @@ -288,7 +289,7 @@ return true; } - DiagnoseAvailabilityOfDecl(D, Loc, UnknownObjCClass, ObjCPropertyAccess, + DiagnoseAvailabilityOfDecl(D, Locs, UnknownObjCClass, ObjCPropertyAccess, AvoidPartialAvailabilityChecks); DiagnoseUnusedOfDecl(*this, D, Loc); Index: clang/lib/Sema/SemaExprObjC.cpp =================================================================== --- clang/lib/Sema/SemaExprObjC.cpp +++ clang/lib/Sema/SemaExprObjC.cpp @@ -2408,9 +2408,9 @@ << FixItHint::CreateInsertion(Loc, "["); LBracLoc = Loc; } - SourceLocation SelLoc; + MultiSourceLocation SelLoc; if (!SelectorLocs.empty() && SelectorLocs.front().isValid()) - SelLoc = SelectorLocs.front(); + SelLoc = MultiSourceLocation(SelectorLocs); else SelLoc = Loc; @@ -2632,9 +2632,9 @@ SourceLocation Loc = SuperLoc.isValid()? SuperLoc : Receiver->getLocStart(); SourceRange RecRange = SuperLoc.isValid()? SuperLoc : Receiver->getSourceRange(); - SourceLocation SelLoc; + MultiSourceLocation SelLoc; if (!SelectorLocs.empty() && SelectorLocs.front().isValid()) - SelLoc = SelectorLocs.front(); + SelLoc = MultiSourceLocation(SelectorLocs); else SelLoc = Loc; Index: clang/test/SemaObjC/attr-deprecated-replacement-fixit.m =================================================================== --- /dev/null +++ clang/test/SemaObjC/attr-deprecated-replacement-fixit.m @@ -0,0 +1,177 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -verify -fsyntax-only %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck --implicit-check-not fix-it: %s +// RUN: cp %s %t +// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -DIGNORE_UNSUCCESSFUL_RENAMES -fixit -x objective-c %t +// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -DIGNORE_UNSUCCESSFUL_RENAMES -Werror -x objective-c %t + +#if !__has_feature(attribute_deprecated_with_replacement) +#error "Missing __has_feature" +#endif + +#if !__has_feature(attribute_availability_with_replacement) +#error "Missing __has_feature" +#endif + +#define DEPRECATED(replacement) __attribute__((deprecated("message", replacement))) + +@protocol SuccessfulMultiParameterRenames +// expected-note@+1 {{has been explicitly marked deprecated here}} +- (void)multi:(int)param1 parameter:(int)param2 replacement:(int)param3 DEPRECATED("multi_new:parameter_new:replace_new_ment:"); +- (void)multi_new:(int)param1 parameter_new:(int)param2 replace_new_ment:(int)param3; + +// expected-note@+1 {{has been explicitly marked deprecated here}} +- (void)varArgs:(int)params, ... DEPRECATED("renameVarArgs:"); +- (void)renameVarArgs:(int)params, ...; + +// expected-note@+1 {{has been explicitly marked deprecated here}} +- (void)leadingMinus:(int)param DEPRECATED("-leadingMinusRenamed:"); +- (void)leadingMinusRenamed:(int)param; + +// expected-note@+1 {{has been explicitly marked deprecated here}} +- (void)leadingPlus:(int)param DEPRECATED("+leadingPlusRenamed:"); +- (void)leadingPlusRenamed:(int)param; + +// expected-note@+1 {{has been explicitly marked deprecated here}} +- (void)sourceEmptyName:(int)param1 :(int)param2 DEPRECATED("renameEmptyName:toNonEmpty:"); +- (void)renameEmptyName:(int)param1 toNonEmpty:(int)param2; + +// expected-note@+1 {{has been explicitly marked deprecated here}} +- (void)target:(int)param1 willBecomeEmpty:(int)param2 emptyName:(int)param3 DEPRECATED("target::emptyName:"); +- (void)target:(int)param1 :(int)param2 emptyName:(int)param3; + +// expected-note@+1 {{has been explicitly marked deprecated here}} +- (void)extra:(int)param1 whiteSpace:(int)param2 DEPRECATED("renameExtra:whiteSpace:"); +- (void)renameExtra:(int)param1 whiteSpace:(int)param2; + +// Test renaming that was producing valid code earlier is still producing valid +// code. The difference is that now we detect different number of parameters. +// +// expected-note@+1 {{has been explicitly marked deprecated here}} +- (void)singleArgumentRegression:(int)param DEPRECATED("renameSingleArgument"); +- (void)renameSingleArgument:(int)param; +@end + +void successfulRenames(id object) { + [object multi:0 parameter:1 replacement:2]; // expected-warning {{is deprecated}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:11-[[@LINE-1]]:16}:"multi_new" + // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:19-[[@LINE-2]]:28}:"parameter_new" + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:31-[[@LINE-3]]:42}:"replace_new_ment" + + [object varArgs:1, 2, 3, 0]; // expected-warning {{is deprecated}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:11-[[@LINE-1]]:18}:"renameVarArgs" + + [object leadingMinus:0]; // expected-warning {{is deprecated}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:11-[[@LINE-1]]:23}:"leadingMinusRenamed" + + [object leadingPlus:0]; // expected-warning {{is deprecated}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:11-[[@LINE-1]]:22}:"leadingPlusRenamed" + + [object sourceEmptyName:0 :1]; // expected-warning {{is deprecated}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:11-[[@LINE-1]]:26}:"renameEmptyName" + // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:29-[[@LINE-2]]:29}:"toNonEmpty" + + [object target:0 willBecomeEmpty:1 emptyName:2]; // expected-warning {{is deprecated}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:11-[[@LINE-1]]:17}:"target" + // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:20-[[@LINE-2]]:35}:"" + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:38-[[@LINE-3]]:47}:"emptyName" + + [object extra: 0 whiteSpace: 1]; // expected-warning {{is deprecated}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:11-[[@LINE-1]]:16}:"renameExtra" + // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:23-[[@LINE-2]]:33}:"whiteSpace" + + [object singleArgumentRegression:0]; // expected-warning {{is deprecated}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:11-[[@LINE-1]]:35}:"renameSingleArgument" +} + +#ifndef IGNORE_UNSUCCESSFUL_RENAMES +@protocol UnsuccessfulMultiParameterRenames +// expected-note@+1 {{has been explicitly marked deprecated here}} +- (void)differentNumberOfParameters:(int)param DEPRECATED("rename:hasMoreParameters:"); + +// expected-note@+1 {{has been explicitly marked deprecated here}} +- (void)differentNumber:(int)param1 ofParameters:(int)param2 DEPRECATED("renameHasLessParameters:"); + +// expected-note@+1 {{has been explicitly marked deprecated here}} +- (void)methodLike:(int)param1 replacement:(int)param2 DEPRECATED("noColon:atTheEnd"); + +// expected-note@+1 {{has been explicitly marked deprecated here}} +- (void)freeFormText DEPRECATED("Use something else"); + +// expected-note@+1 {{has been explicitly marked deprecated here}} +- (void)freeFormTextReplacementStartsAsMethod DEPRECATED("-Use something different"); + +// expected-note@+1 {{has been explicitly marked deprecated here}} +- (void)replacementHasSingleSkipCharacter DEPRECATED("-"); + +// expected-note@+1 {{has been explicitly marked deprecated here}} +- (void)replacementHasInvalid:(int)param1 slotName:(int)param2 DEPRECATED("renameWith:1nonIdentifier:"); +@end + +void unsuccessfulRenames(id object) { + [object differentNumberOfParameters:0]; // expected-warning {{is deprecated}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:11-[[@LINE-1]]:38}:"rename:hasMoreParameters:" + + [object differentNumber:0 ofParameters:1]; // expected-warning {{is deprecated}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:11-[[@LINE-1]]:26}:"renameHasLessParameters:" + + [object methodLike:0 replacement:1]; // expected-warning {{is deprecated}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:11-[[@LINE-1]]:21}:"noColon:atTheEnd" + + [object freeFormText]; // expected-warning {{is deprecated}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:11-[[@LINE-1]]:23}:"Use something else" + + [object freeFormTextReplacementStartsAsMethod]; // expected-warning {{is deprecated}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:11-[[@LINE-1]]:48}:"-Use something different" + + [object replacementHasSingleSkipCharacter]; // expected-warning {{is deprecated}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:11-[[@LINE-1]]:44}:"-" + + [object replacementHasInvalid:0 slotName:1]; // expected-warning {{is deprecated}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:11-[[@LINE-1]]:32}:"renameWith:1nonIdentifier:" +} +#endif // IGNORE_UNSUCCESSFUL_RENAMES + +// Make sure classes are treated the same way as protocols. +__attribute__((objc_root_class)) +@interface Interface +// expected-note@+1 {{has been explicitly marked deprecated here}} ++ (void)classMethod:(int)param1 replacement:(int)param2 DEPRECATED("renameClassMethod:replace_new_ment:"); ++ (void)renameClassMethod:(int)param1 replace_new_ment:(int)param2; + +// expected-note@+1 {{has been explicitly marked deprecated here}} +- (void)multi:(int)param1 parameter:(int)param2 replacement:(int)param3 DEPRECATED("multi_new:parameter_new:replace_new_ment:"); +- (void)multi_new:(int)param1 parameter_new:(int)param2 replace_new_ment:(int)param3; +@end + +@implementation Interface ++ (void)classMethod:(int)param1 replacement:(int)param2 {} ++ (void)renameClassMethod:(int)param1 replace_new_ment:(int)param2 {} + +- (void)multi:(int)param1 parameter:(int)param2 replacement:(int)param3 {} +- (void)multi_new:(int)param1 parameter_new:(int)param2 replace_new_ment:(int)param3 {} + +- (void)usage { + [Interface classMethod:0 replacement:1]; // expected-warning {{is deprecated}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:14-[[@LINE-1]]:25}:"renameClassMethod" + // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:28-[[@LINE-2]]:39}:"replace_new_ment" + + [self multi:0 parameter:1 replacement:2]; // expected-warning {{is deprecated}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:9-[[@LINE-1]]:14}:"multi_new" + // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:17-[[@LINE-2]]:26}:"parameter_new" + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:29-[[@LINE-3]]:40}:"replace_new_ment" +} +@end + +// Make sure availability attribute is handled the same way as deprecation attribute. +@protocol AvailabilityAttributeRenames +// expected-note@+1 {{has been explicitly marked deprecated here}} +- (void)multi:(int)param1 parameter:(int)param2 replacement:(int)param3 __attribute__((availability(macosx,deprecated=9.0,replacement="multi_new:parameter_new:replace_new_ment:"))); +- (void)multi_new:(int)param1 parameter_new:(int)param2 replace_new_ment:(int)param3; +@end + +void availabilityAttributeRenames(id object) { + [object multi:0 parameter:1 replacement:2]; // expected-warning {{is deprecated}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:11-[[@LINE-1]]:16}:"multi_new" + // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:19-[[@LINE-2]]:28}:"parameter_new" + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:31-[[@LINE-3]]:42}:"replace_new_ment" +}