summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVolodymyr Sapsai <vsapsai@apple.com>2018-03-29 17:34:09 +0000
committerVolodymyr Sapsai <vsapsai@apple.com>2018-03-29 17:34:09 +0000
commitd0c687aaf82c4343b63d5f97cd79a15bec3f6b26 (patch)
tree0fc167aff13ad6cde785e6099eb168b039b50f1e
parentf7f26e44292421968b3266fa0af9e43117c33eea (diff)
downloadclang-d0c687aaf82c4343b63d5f97cd79a15bec3f6b26.tar.gz
[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
-rw-r--r--include/clang/Basic/CharInfo.h11
-rw-r--r--include/clang/Sema/DelayedDiagnostic.h12
-rw-r--r--include/clang/Sema/Sema.h4
-rw-r--r--lib/Sema/DelayedDiagnostic.cpp13
-rw-r--r--lib/Sema/SemaDeclAttr.cpp101
-rw-r--r--lib/Sema/SemaExpr.cpp5
-rw-r--r--lib/Sema/SemaExprObjC.cpp26
-rw-r--r--test/SemaObjC/attr-deprecated-replacement-fixit.m177
-rw-r--r--unittests/Basic/CharInfoTest.cpp5
9 files changed, 309 insertions, 45 deletions
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<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;
};
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<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);
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<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:
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<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);
}
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<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);
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<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).
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<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"
+}
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));
}