From d0c687aaf82c4343b63d5f97cd79a15bec3f6b26 Mon Sep 17 00:00:00 2001 From: Volodymyr Sapsai Date: Thu, 29 Mar 2018 17:34:09 +0000 Subject: [PATCH] [Sema] Make deprecation fix-it replace all multi-parameter ObjC method slots. Deprecation replacement can be any text but if it looks like a name of ObjC method and has the same number of arguments as original method, replace all slot names so after applying a fix-it you have valid code. rdar://problem/36660853 Reviewers: aaron.ballman, erik.pilkington, rsmith Reviewed By: erik.pilkington Subscribers: cfe-commits, jkorous-apple Differential Revision: https://reviews.llvm.org/D44589 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@328807 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/CharInfo.h | 11 +- include/clang/Sema/DelayedDiagnostic.h | 12 +- include/clang/Sema/Sema.h | 4 +- lib/Sema/DelayedDiagnostic.cpp | 13 +- lib/Sema/SemaDeclAttr.cpp | 101 ++++++++-- lib/Sema/SemaExpr.cpp | 5 +- lib/Sema/SemaExprObjC.cpp | 26 +-- .../attr-deprecated-replacement-fixit.m | 177 ++++++++++++++++++ unittests/Basic/CharInfoTest.cpp | 5 + 9 files changed, 309 insertions(+), 45 deletions(-) create mode 100644 test/SemaObjC/attr-deprecated-replacement-fixit.m diff --git a/include/clang/Basic/CharInfo.h b/include/clang/Basic/CharInfo.h index cc27bbb48e..be49f360d6 100644 --- a/include/clang/Basic/CharInfo.h +++ b/include/clang/Basic/CharInfo.h @@ -180,14 +180,15 @@ LLVM_READONLY inline char toUppercase(char c) { /// Return true if this is a valid ASCII identifier. /// -/// Note that this is a very simple check; it does not accept '$' or UCNs as -/// valid identifier characters. -LLVM_READONLY inline bool isValidIdentifier(StringRef S) { - if (S.empty() || !isIdentifierHead(S[0])) +/// Note that this is a very simple check; it does not accept UCNs as valid +/// identifier characters. +LLVM_READONLY inline bool isValidIdentifier(StringRef S, + bool AllowDollar = false) { + if (S.empty() || !isIdentifierHead(S[0], AllowDollar)) return false; for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) - if (!isIdentifierBody(*I)) + if (!isIdentifierBody(*I, AllowDollar)) return false; return true; diff --git a/include/clang/Sema/DelayedDiagnostic.h b/include/clang/Sema/DelayedDiagnostic.h index c4db8e20aa..ce2a95073d 100644 --- a/include/clang/Sema/DelayedDiagnostic.h +++ b/include/clang/Sema/DelayedDiagnostic.h @@ -31,6 +31,7 @@ #include "clang/Basic/SourceLocation.h" #include "clang/Basic/Specifiers.h" #include "clang/Sema/Sema.h" +#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Casting.h" @@ -138,7 +139,7 @@ public: void Destroy(); static DelayedDiagnostic makeAvailability(AvailabilityResult AR, - SourceLocation Loc, + ArrayRef Locs, const NamedDecl *ReferringDecl, const NamedDecl *OffendingDecl, const ObjCInterfaceDecl *UnknownObjCClass, @@ -146,7 +147,6 @@ public: StringRef Msg, bool ObjCPropertyAccess); - static DelayedDiagnostic makeAccess(SourceLocation Loc, const AccessedEntity &Entity) { DelayedDiagnostic DD; @@ -194,6 +194,12 @@ public: return StringRef(AvailabilityData.Message, AvailabilityData.MessageLen); } + ArrayRef getAvailabilitySelectorLocs() const { + assert(Kind == Availability && "Not an availability diagnostic."); + return llvm::makeArrayRef(AvailabilityData.SelectorLocs, + AvailabilityData.NumSelectorLocs); + } + AvailabilityResult getAvailabilityResult() const { assert(Kind == Availability && "Not an availability diagnostic."); return AvailabilityData.AR; @@ -238,6 +244,8 @@ private: const ObjCPropertyDecl *ObjCProperty; const char *Message; size_t MessageLen; + SourceLocation *SelectorLocs; + size_t NumSelectorLocs; AvailabilityResult AR; bool ObjCPropertyAccess; }; diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 3f8ff6bb92..a6682537aa 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -3947,7 +3947,7 @@ public: void redelayDiagnostics(sema::DelayedDiagnosticPool &pool); - void DiagnoseAvailabilityOfDecl(NamedDecl *D, SourceLocation Loc, + void DiagnoseAvailabilityOfDecl(NamedDecl *D, ArrayRef Locs, const ObjCInterfaceDecl *UnknownObjCClass, bool ObjCPropertyAccess, bool AvoidPartialAvailabilityChecks = false); @@ -3962,7 +3962,7 @@ public: // Expression Parsing Callbacks: SemaExpr.cpp. bool CanUseDecl(NamedDecl *D, bool TreatUnavailableAsInvalid); - bool DiagnoseUseOfDecl(NamedDecl *D, SourceLocation Loc, + bool DiagnoseUseOfDecl(NamedDecl *D, ArrayRef Locs, const ObjCInterfaceDecl *UnknownObjCClass = nullptr, bool ObjCPropertyAccess = false, bool AvoidPartialAvailabilityChecks = false); diff --git a/lib/Sema/DelayedDiagnostic.cpp b/lib/Sema/DelayedDiagnostic.cpp index 2555206340..122b477d55 100644 --- a/lib/Sema/DelayedDiagnostic.cpp +++ b/lib/Sema/DelayedDiagnostic.cpp @@ -23,17 +23,18 @@ using namespace sema; DelayedDiagnostic DelayedDiagnostic::makeAvailability(AvailabilityResult AR, - SourceLocation Loc, + ArrayRef Locs, const NamedDecl *ReferringDecl, const NamedDecl *OffendingDecl, const ObjCInterfaceDecl *UnknownObjCClass, const ObjCPropertyDecl *ObjCProperty, StringRef Msg, bool ObjCPropertyAccess) { + assert(!Locs.empty()); DelayedDiagnostic DD; DD.Kind = Availability; DD.Triggered = false; - DD.Loc = Loc; + DD.Loc = Locs.front(); DD.AvailabilityData.ReferringDecl = ReferringDecl; DD.AvailabilityData.OffendingDecl = OffendingDecl; DD.AvailabilityData.UnknownObjCClass = UnknownObjCClass; @@ -43,9 +44,14 @@ DelayedDiagnostic::makeAvailability(AvailabilityResult AR, MessageData = new char [Msg.size()]; memcpy(MessageData, Msg.data(), Msg.size()); } - DD.AvailabilityData.Message = MessageData; DD.AvailabilityData.MessageLen = Msg.size(); + + DD.AvailabilityData.SelectorLocs = new SourceLocation[Locs.size()]; + memcpy(DD.AvailabilityData.SelectorLocs, Locs.data(), + sizeof(SourceLocation) * Locs.size()); + DD.AvailabilityData.NumSelectorLocs = Locs.size(); + DD.AvailabilityData.AR = AR; DD.AvailabilityData.ObjCPropertyAccess = ObjCPropertyAccess; return DD; @@ -59,6 +65,7 @@ void DelayedDiagnostic::Destroy() { case Availability: delete[] AvailabilityData.Message; + delete[] AvailabilityData.SelectorLocs; break; case ForbiddenType: diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index 512a3df074..1d2c3b5534 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -6931,6 +6931,45 @@ struct AttributeInsertion { } // end anonymous namespace +/// Tries to parse a string as ObjC method name. +/// +/// \param Name The string to parse. Expected to originate from availability +/// attribute argument. +/// \param SlotNames The vector that will be populated with slot names. In case +/// of unsuccessful parsing can contain invalid data. +/// \returns A number of method parameters if parsing was successful, None +/// otherwise. +static Optional +tryParseObjCMethodName(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. + bool AllowDollar = LangOpts.DollarIdents; + for (StringRef S : SlotNames) { + if (S.empty()) + continue; + if (!isValidIdentifier(S, 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 +7006,8 @@ createAttributeInsertion(const NamedDecl *D, const SourceManager &SM, static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, Decl *Ctx, const NamedDecl *ReferringDecl, const NamedDecl *OffendingDecl, - StringRef Message, SourceLocation Loc, + StringRef Message, + ArrayRef Locs, const ObjCInterfaceDecl *UnknownObjCClass, const ObjCPropertyDecl *ObjCProperty, bool ObjCPropertyAccess) { @@ -6989,6 +7029,8 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, if (!ShouldDiagnoseAvailabilityInContext(S, K, DeclVersion, Ctx)) return; + SourceLocation Loc = Locs.front(); + // The declaration can have multiple availability attributes, we are looking // at one of them. const AvailabilityAttr *A = getAttrForPlatform(S.Context, OffendingDecl); @@ -7134,37 +7176,55 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, 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 = tryParseObjCMethodName( + Replacement, SelectorSlotNames, S.getLangOpts()); + if (NumParams && NumParams.getValue() == Sel.getNumArgs()) { + assert(SelectorSlotNames.size() == Locs.size()); + for (unsigned I = 0; I < Locs.size(); ++I) { + if (!Sel.getNameForSlot(I).empty()) { + CharSourceRange NameRange = CharSourceRange::getCharRange( + Locs[I], S.getLocForEndOfToken(Locs[I])); + FixIts.push_back(FixItHint::CreateReplacement( + NameRange, SelectorSlotNames[I])); + } else + FixIts.push_back( + FixItHint::CreateInsertion(Locs[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 +7240,9 @@ static void handleDelayedAvailabilityCheck(Sema &S, DelayedDiagnostic &DD, 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 +7303,8 @@ void Sema::redelayDiagnostics(DelayedDiagnosticPool &pool) { static void EmitAvailabilityWarning(Sema &S, AvailabilityResult AR, const NamedDecl *ReferringDecl, const NamedDecl *OffendingDecl, - StringRef Message, SourceLocation Loc, + StringRef Message, + ArrayRef Locs, const ObjCInterfaceDecl *UnknownObjCClass, const ObjCPropertyDecl *ObjCProperty, bool ObjCPropertyAccess) { @@ -7250,14 +7312,14 @@ static void EmitAvailabilityWarning(Sema &S, AvailabilityResult AR, 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 +7657,8 @@ void Sema::DiagnoseUnguardedAvailabilityViolations(Decl *D) { DiagnoseUnguardedAvailability(*this, D).IssueDiagnostics(Body); } -void Sema::DiagnoseAvailabilityOfDecl(NamedDecl *D, SourceLocation Loc, +void Sema::DiagnoseAvailabilityOfDecl(NamedDecl *D, + ArrayRef Locs, const ObjCInterfaceDecl *UnknownObjCClass, bool ObjCPropertyAccess, bool AvoidPartialAvailabilityChecks) { @@ -7632,6 +7695,6 @@ void Sema::DiagnoseAvailabilityOfDecl(NamedDecl *D, SourceLocation Loc, } } - EmitAvailabilityWarning(*this, Result, D, OffendingDecl, Message, Loc, + EmitAvailabilityWarning(*this, Result, D, OffendingDecl, Message, Locs, UnknownObjCClass, ObjCPDecl, ObjCPropertyAccess); } diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index d87c21c0b1..c5f581f423 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -202,10 +202,11 @@ void Sema::MaybeSuggestAddingStaticToDecl(const FunctionDecl *Cur) { /// \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, ArrayRef Locs, const ObjCInterfaceDecl *UnknownObjCClass, bool ObjCPropertyAccess, bool AvoidPartialAvailabilityChecks) { + SourceLocation Loc = Locs.front(); if (getLangOpts().CPlusPlus && isa(D)) { // If there were any diagnostics suppressed by template argument deduction, // emit them now. @@ -289,7 +290,7 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, SourceLocation Loc, return true; } - DiagnoseAvailabilityOfDecl(D, Loc, UnknownObjCClass, ObjCPropertyAccess, + DiagnoseAvailabilityOfDecl(D, Locs, UnknownObjCClass, ObjCPropertyAccess, AvoidPartialAvailabilityChecks); DiagnoseUnusedOfDecl(*this, D, Loc); diff --git a/lib/Sema/SemaExprObjC.cpp b/lib/Sema/SemaExprObjC.cpp index 8d017af991..14db4145ed 100644 --- a/lib/Sema/SemaExprObjC.cpp +++ b/lib/Sema/SemaExprObjC.cpp @@ -2408,11 +2408,12 @@ ExprResult Sema::BuildClassMessage(TypeSourceInfo *ReceiverTypeInfo, << FixItHint::CreateInsertion(Loc, "["); LBracLoc = Loc; } - SourceLocation SelLoc; + ArrayRef SelectorSlotLocs; if (!SelectorLocs.empty() && SelectorLocs.front().isValid()) - SelLoc = SelectorLocs.front(); + SelectorSlotLocs = SelectorLocs; else - SelLoc = Loc; + SelectorSlotLocs = Loc; + SourceLocation SelLoc = SelectorSlotLocs.front(); if (ReceiverType->isDependentType()) { // If the receiver type is dependent, we can't type-check anything @@ -2437,7 +2438,7 @@ ExprResult Sema::BuildClassMessage(TypeSourceInfo *ReceiverTypeInfo, assert(Class && "We don't know which class we're messaging?"); // objc++ diagnoses during typename annotation. if (!getLangOpts().CPlusPlus) - (void)DiagnoseUseOfDecl(Class, SelLoc); + (void)DiagnoseUseOfDecl(Class, SelectorSlotLocs); // Find the method we are messaging. if (!Method) { SourceRange TypeRange @@ -2462,7 +2463,7 @@ ExprResult Sema::BuildClassMessage(TypeSourceInfo *ReceiverTypeInfo, if (!Method) Method = Class->lookupPrivateClassMethod(Sel); - if (Method && DiagnoseUseOfDecl(Method, SelLoc)) + if (Method && DiagnoseUseOfDecl(Method, SelectorSlotLocs)) return ExprError(); } @@ -2632,11 +2633,12 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, SourceLocation Loc = SuperLoc.isValid()? SuperLoc : Receiver->getLocStart(); SourceRange RecRange = SuperLoc.isValid()? SuperLoc : Receiver->getSourceRange(); - SourceLocation SelLoc; + ArrayRef SelectorSlotLocs; if (!SelectorLocs.empty() && SelectorLocs.front().isValid()) - SelLoc = SelectorLocs.front(); + SelectorSlotLocs = SelectorLocs; else - SelLoc = Loc; + SelectorSlotLocs = Loc; + SourceLocation SelLoc = SelectorSlotLocs.front(); if (LBracLoc.isInvalid()) { Diag(Loc, diag::err_missing_open_square_message_send) @@ -2748,7 +2750,7 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, if (!AreMultipleMethodsInGlobalPool(Sel, Method, SourceRange(LBracLoc, RBracLoc), receiverIsIdLike, Methods)) - DiagnoseUseOfDecl(Method, SelLoc); + DiagnoseUseOfDecl(Method, SelectorSlotLocs); } } else if (ReceiverType->isObjCClassOrClassKindOfType() || ReceiverType->isObjCQualifiedClassType()) { @@ -2780,7 +2782,7 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, if (!Method) Method = ClassDecl->lookupPrivateClassMethod(Sel); } - if (Method && DiagnoseUseOfDecl(Method, SelLoc)) + if (Method && DiagnoseUseOfDecl(Method, SelectorSlotLocs)) return ExprError(); } if (!Method) { @@ -2827,7 +2829,7 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, Method = LookupMethodInQualifiedType(Sel, QIdTy, true); if (!Method) Method = LookupMethodInQualifiedType(Sel, QIdTy, false); - if (Method && DiagnoseUseOfDecl(Method, SelLoc)) + if (Method && DiagnoseUseOfDecl(Method, SelectorSlotLocs)) return ExprError(); } else if (const ObjCObjectPointerType *OCIType = ReceiverType->getAsObjCInterfacePointerType()) { @@ -2902,7 +2904,7 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, } } } - if (Method && DiagnoseUseOfDecl(Method, SelLoc, forwardClass)) + if (Method && DiagnoseUseOfDecl(Method, SelectorSlotLocs, forwardClass)) return ExprError(); } else { // Reject other random receiver types (e.g. structs). diff --git a/test/SemaObjC/attr-deprecated-replacement-fixit.m b/test/SemaObjC/attr-deprecated-replacement-fixit.m new file mode 100644 index 0000000000..9d0fcf91a2 --- /dev/null +++ b/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" +} diff --git a/unittests/Basic/CharInfoTest.cpp b/unittests/Basic/CharInfoTest.cpp index 348e6ffe2b..7a9d17fce6 100644 --- a/unittests/Basic/CharInfoTest.cpp +++ b/unittests/Basic/CharInfoTest.cpp @@ -432,6 +432,7 @@ TEST(CharInfoTest, isValidIdentifier) { EXPECT_TRUE(isValidIdentifier("z")); EXPECT_TRUE(isValidIdentifier("A")); EXPECT_TRUE(isValidIdentifier("Z")); + EXPECT_TRUE(isValidIdentifier("$", /*AllowDollar=*/true)); // 2 characters, '_' suffix EXPECT_FALSE(isValidIdentifier("._")); @@ -448,6 +449,7 @@ TEST(CharInfoTest, isValidIdentifier) { EXPECT_TRUE(isValidIdentifier("z_")); EXPECT_TRUE(isValidIdentifier("A_")); EXPECT_TRUE(isValidIdentifier("Z_")); + EXPECT_TRUE(isValidIdentifier("$_", /*AllowDollar=*/true)); // 2 characters, '_' prefix EXPECT_FALSE(isValidIdentifier("_.")); @@ -464,6 +466,7 @@ TEST(CharInfoTest, isValidIdentifier) { EXPECT_TRUE(isValidIdentifier("_z")); EXPECT_TRUE(isValidIdentifier("_A")); EXPECT_TRUE(isValidIdentifier("_Z")); + EXPECT_TRUE(isValidIdentifier("_$", /*AllowDollar=*/true)); // 3 characters, '__' prefix EXPECT_FALSE(isValidIdentifier("__.")); @@ -480,6 +483,7 @@ TEST(CharInfoTest, isValidIdentifier) { EXPECT_TRUE(isValidIdentifier("__z")); EXPECT_TRUE(isValidIdentifier("__A")); EXPECT_TRUE(isValidIdentifier("__Z")); + EXPECT_TRUE(isValidIdentifier("__$", /*AllowDollar=*/true)); // 3 characters, '_' prefix and suffix EXPECT_FALSE(isValidIdentifier("_._")); @@ -496,4 +500,5 @@ TEST(CharInfoTest, isValidIdentifier) { EXPECT_TRUE(isValidIdentifier("_z_")); EXPECT_TRUE(isValidIdentifier("_A_")); EXPECT_TRUE(isValidIdentifier("_Z_")); + EXPECT_TRUE(isValidIdentifier("_$_", /*AllowDollar=*/true)); } -- 2.40.0