]> granicus.if.org Git - clang/commitdiff
[Sema] Make deprecation fix-it replace all multi-parameter ObjC method slots.
authorVolodymyr Sapsai <vsapsai@apple.com>
Thu, 29 Mar 2018 17:34:09 +0000 (17:34 +0000)
committerVolodymyr Sapsai <vsapsai@apple.com>
Thu, 29 Mar 2018 17:34:09 +0000 (17:34 +0000)
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
include/clang/Sema/DelayedDiagnostic.h
include/clang/Sema/Sema.h
lib/Sema/DelayedDiagnostic.cpp
lib/Sema/SemaDeclAttr.cpp
lib/Sema/SemaExpr.cpp
lib/Sema/SemaExprObjC.cpp
test/SemaObjC/attr-deprecated-replacement-fixit.m [new file with mode: 0644]
unittests/Basic/CharInfoTest.cpp

index cc27bbb48e0d1b3b98e0012c72a5b2ffae5a0299..be49f360d60f926c880d9fa670e927412e1f5a43 100644 (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;
index c4db8e20aa60ae38062345c9871822a5deb96783..ce2a95073dcec44b43c5ddd54ec57746262a303a 100644 (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;
   };
index 3f8ff6bb923e0b14832c8e52d421224fbb19b67e..a6682537aac1ad953976fc7160e00559875c4db0 100644 (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);
index 255520634006ba542c23af501f28684f79ff2897..122b477d552282ced82ab02362661d8e9f8db3b1 100644 (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:
index 512a3df0744f011c965bb08f70cbff1d60587dfd..1d2c3b55342cdede32c50e9ea8a1a659a0fe68c0 100644 (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);
 }
index d87c21c0b177764fa9fcff0ada4f5222de3c3ef1..c5f581f423242a530b09a3ea95c9629a7c14839c 100644 (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);
index 8d017af9912b46edb2ef618c232b2a25ace53d88..14db4145ed92852cf0c49e508f208d39b959a0b9 100644 (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).
diff --git a/test/SemaObjC/attr-deprecated-replacement-fixit.m b/test/SemaObjC/attr-deprecated-replacement-fixit.m
new file mode 100644 (file)
index 0000000..9d0fcf9
--- /dev/null
@@ -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"
+}
index 348e6ffe2b7d1b84d725cb8d30c3f8b8811e14c6..7a9d17fce65dd090bc033adfb26756c6bdd4fb08 100644 (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));
 }