[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

llvm-svn: 328807
This commit is contained in:
Volodymyr Sapsai 2018-03-29 17:34:09 +00:00
parent 2fa1436206
commit 7d89ce97ec
9 changed files with 309 additions and 45 deletions

View File

@ -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;

View File

@ -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<SourceLocation> 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<SourceLocation> 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;
};

View File

@ -3947,7 +3947,7 @@ public:
void redelayDiagnostics(sema::DelayedDiagnosticPool &pool);
void DiagnoseAvailabilityOfDecl(NamedDecl *D, SourceLocation Loc,
void DiagnoseAvailabilityOfDecl(NamedDecl *D, ArrayRef<SourceLocation> 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<SourceLocation> Locs,
const ObjCInterfaceDecl *UnknownObjCClass = nullptr,
bool ObjCPropertyAccess = false,
bool AvoidPartialAvailabilityChecks = false);

View File

@ -23,17 +23,18 @@ using namespace sema;
DelayedDiagnostic
DelayedDiagnostic::makeAvailability(AvailabilityResult AR,
SourceLocation Loc,
ArrayRef<SourceLocation> 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:

View File

@ -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<unsigned>
tryParseObjCMethodName(StringRef Name, SmallVectorImpl<StringRef> &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<AttributeInsertion>
@ -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<SourceLocation> 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<FixItHint, 12> FixIts;
if (K == AR_Deprecated) {
StringRef Replacement;
if (auto AL = OffendingDecl->getAttr<DeprecatedAttr>())
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<ObjCMethodDecl>(ReferringDecl)) {
Selector Sel = MethodDecl->getSelector();
SmallVector<StringRef, 12> SelectorSlotNames;
Optional<unsigned> 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<SourceLocation> 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<Decl>(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<SourceLocation> 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);
}

View File

@ -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<SourceLocation> Locs,
const ObjCInterfaceDecl *UnknownObjCClass,
bool ObjCPropertyAccess,
bool AvoidPartialAvailabilityChecks) {
SourceLocation Loc = Locs.front();
if (getLangOpts().CPlusPlus && isa<FunctionDecl>(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);

View File

@ -2408,11 +2408,12 @@ ExprResult Sema::BuildClassMessage(TypeSourceInfo *ReceiverTypeInfo,
<< FixItHint::CreateInsertion(Loc, "[");
LBracLoc = Loc;
}
SourceLocation SelLoc;
ArrayRef<SourceLocation> 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<SourceLocation> 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).

View File

@ -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<SuccessfulMultiParameterRenames> 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<UnsuccessfulMultiParameterRenames> 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<AvailabilityAttributeRenames> 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"
}

View File

@ -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));
}