Merge pull request #15864 from jrose-apple/get-set-stop

Diagnose unavailable getters and setters
diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def
index 6b4f720..e2db36e 100644
--- a/include/swift/AST/DiagnosticsSema.def
+++ b/include/swift/AST/DiagnosticsSema.def
@@ -3622,38 +3622,45 @@
 //------------------------------------------------------------------------------
 
 ERROR(availability_decl_unavailable, none,
-  "%0 is unavailable", (DeclName))
+      "%select{getter for |setter for |}0%1 is unavailable",
+      (unsigned, DeclName))
 
 #define REPLACEMENT_DECL_KIND_SELECT "select{| instance method| property}"
 ERROR(availability_decl_unavailable_rename, none,
-      "%0 has been %select{renamed to|replaced by}1"
-      "%" REPLACEMENT_DECL_KIND_SELECT "2 '%3'",
-      (DeclName, bool, unsigned, StringRef))
+      "%select{getter for |setter for |}0%1 has been "
+      "%select{renamed to|replaced by}2%" REPLACEMENT_DECL_KIND_SELECT "3 "
+      "'%4'",
+      (unsigned, DeclName, bool, unsigned, StringRef))
 
 ERROR(availability_decl_unavailable_rename_msg, none,
-      "%0 has been %select{renamed to|replaced by}1"
-      "%" REPLACEMENT_DECL_KIND_SELECT "2 '%3': %4",
-      (DeclName, bool, unsigned, StringRef, StringRef))
+      "%select{getter for |setter for |}0%1 has been "
+      "%select{renamed to|replaced by}2%" REPLACEMENT_DECL_KIND_SELECT "3 "
+      "'%4': %5",
+      (unsigned, DeclName, bool, unsigned, StringRef, StringRef))
 
 ERROR(availability_decl_unavailable_msg, none,
-  "%0 is unavailable: %1", (DeclName, StringRef))
+      "%select{getter for |setter for |}0%1 is unavailable: %2",
+      (unsigned, DeclName, StringRef))
 
 ERROR(availability_decl_unavailable_in_swift, none,
-  "%0 is unavailable in Swift", (DeclName))
+      "%select{getter for |setter for |}0%1 is unavailable in Swift",
+      (unsigned, DeclName))
 
 ERROR(availability_decl_unavailable_in_swift_msg, none,
-  "%0 is unavailable in Swift: %1", (DeclName, StringRef))
+      "%select{getter for |setter for |}0%1 is unavailable in Swift: %2",
+      (unsigned, DeclName, StringRef))
 
 NOTE(availability_marked_unavailable, none,
-  "%0 has been explicitly marked unavailable here", (DeclName))
+     "%select{getter for |setter for |}0%1 has been explicitly marked "
+     "unavailable here", (unsigned, DeclName))
 
 NOTE(availability_introduced_in_swift, none,
-     "%0 was introduced in Swift %1",
-    (DeclName, clang::VersionTuple))
+     "%select{getter for |setter for |}0%1 was introduced in Swift %2",
+     (unsigned, DeclName, clang::VersionTuple))
 
 NOTE(availability_obsoleted, none,
-     "%0 was obsoleted in %1 %2",
-    (DeclName, StringRef, clang::VersionTuple))
+     "%select{getter for |setter for |}0%1 was obsoleted in %2 %3",
+     (unsigned, DeclName, StringRef, clang::VersionTuple))
 
 WARNING(availability_deprecated, none,
         "%select{getter for |setter for |}0%1 %select{is|%select{is|was}4}2 "
diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp
index e5253a8..07f653a 100644
--- a/lib/Sema/TypeCheckAvailability.cpp
+++ b/lib/Sema/TypeCheckAvailability.cpp
@@ -1534,6 +1534,9 @@
                                      const ValueDecl *renamedDecl,
                                      const AvailableAttr *attr,
                                      const ApplyExpr *call) {
+  if (isa<AccessorDecl>(renamedDecl))
+    return;
+
   ParsedDeclName parsed = swift::parseDeclName(attr->Rename);
   if (!parsed)
     return;
@@ -1906,6 +1909,26 @@
   return ReplacementDeclKind::None;
 }
 
+/// Returns a value that can be used to select between accessor kinds in
+/// diagnostics.
+///
+/// This is correlated with diag::availability_deprecated and others.
+static std::pair<unsigned, DeclName>
+getAccessorKindAndNameForDiagnostics(const ValueDecl *D) {
+  // This should always be one more than the last AccessorKind supported in
+  // the diagnostics. If you need to change it, change the assertion below as
+  // well.
+  static const unsigned NOT_ACCESSOR_INDEX = 2;
+
+  if (auto *accessor = dyn_cast<AccessorDecl>(D)) {
+    DeclName Name = accessor->getStorage()->getFullName();
+    assert(accessor->isGetterOrSetter());
+    return {static_cast<unsigned>(accessor->getAccessorKind()), Name};
+  }
+
+  return {NOT_ACCESSOR_INDEX, D->getFullName()};
+}
+
 void TypeChecker::diagnoseIfDeprecated(SourceRange ReferenceRange,
                                        const DeclContext *ReferenceDC,
                                        const ValueDecl *DeprecatedDecl,
@@ -1933,26 +1956,19 @@
   }
 
   DeclName Name;
-  Optional<unsigned> rawAccessorKind;
-  if (auto *accessor = dyn_cast<AccessorDecl>(DeprecatedDecl)) {
-    Name = accessor->getStorage()->getFullName();
-    assert(accessor->isGetterOrSetter());
-    rawAccessorKind = static_cast<unsigned>(accessor->getAccessorKind());
-  } else {
-    Name = DeprecatedDecl->getFullName();
-  }
+  unsigned RawAccessorKind;
+  std::tie(RawAccessorKind, Name) =
+      getAccessorKindAndNameForDiagnostics(DeprecatedDecl);
 
   StringRef Platform = Attr->prettyPlatformString();
   clang::VersionTuple DeprecatedVersion;
   if (Attr->Deprecated)
     DeprecatedVersion = Attr->Deprecated.getValue();
 
-  static const unsigned NOT_ACCESSOR_INDEX = 2;
   if (Attr->Message.empty() && Attr->Rename.empty()) {
     diagnose(ReferenceRange.Start, diag::availability_deprecated,
-             rawAccessorKind.getValueOr(NOT_ACCESSOR_INDEX), Name,
-             Attr->hasPlatform(), Platform, Attr->Deprecated.hasValue(),
-             DeprecatedVersion)
+             RawAccessorKind, Name, Attr->hasPlatform(), Platform,
+             Attr->Deprecated.hasValue(), DeprecatedVersion)
       .highlight(Attr->getRange());
     return;
   }
@@ -1965,22 +1981,21 @@
   if (!Attr->Message.empty()) {
     EncodedDiagnosticMessage EncodedMessage(Attr->Message);
     diagnose(ReferenceRange.Start, diag::availability_deprecated_msg,
-             rawAccessorKind.getValueOr(NOT_ACCESSOR_INDEX), Name,
-             Attr->hasPlatform(), Platform, Attr->Deprecated.hasValue(),
-             DeprecatedVersion, EncodedMessage.Message)
+             RawAccessorKind, Name, Attr->hasPlatform(), Platform,
+             Attr->Deprecated.hasValue(), DeprecatedVersion,
+             EncodedMessage.Message)
       .highlight(Attr->getRange());
   } else {
     unsigned rawReplaceKind = static_cast<unsigned>(
         replacementDeclKind.getValueOr(ReplacementDeclKind::None));
     diagnose(ReferenceRange.Start, diag::availability_deprecated_rename,
-             rawAccessorKind.getValueOr(NOT_ACCESSOR_INDEX), Name,
-             Attr->hasPlatform(), Platform, Attr->Deprecated.hasValue(),
-             DeprecatedVersion, replacementDeclKind.hasValue(), rawReplaceKind,
-             newName)
+             RawAccessorKind, Name, Attr->hasPlatform(), Platform,
+             Attr->Deprecated.hasValue(), DeprecatedVersion,
+             replacementDeclKind.hasValue(), rawReplaceKind, newName)
       .highlight(Attr->getRange());
   }
 
-  if (!Attr->Rename.empty() && !rawAccessorKind.hasValue()) {
+  if (!Attr->Rename.empty() && !isa<AccessorDecl>(DeprecatedDecl)) {
     auto renameDiag = diagnose(ReferenceRange.Start,
                                diag::note_deprecated_rename,
                                newName);
@@ -1999,8 +2014,13 @@
     else
       diagnose(override, diag::override_unavailable_msg,
                override->getBaseName(), attr->Message);
+
+    DeclName name;
+    unsigned rawAccessorKind;
+    std::tie(rawAccessorKind, name) =
+        getAccessorKindAndNameForDiagnostics(base);
     diagnose(base, diag::availability_marked_unavailable,
-             base->getFullName());
+             rawAccessorKind, name);
     return;
   }
 
@@ -2127,7 +2147,9 @@
   }
 
   SourceLoc Loc = R.Start;
-  auto Name = D->getFullName();
+  DeclName Name;
+  unsigned RawAccessorKind;
+  std::tie(RawAccessorKind, Name) = getAccessorKindAndNameForDiagnostics(D);
 
   switch (Attr->getPlatformAgnosticAvailability()) {
   case PlatformAgnosticAvailabilityKind::Deprecated:
@@ -2150,14 +2172,14 @@
 
       if (Attr->Message.empty()) {
         auto diag = diagnose(Loc, diag::availability_decl_unavailable_rename,
-                             Name, replaceKind.hasValue(), rawReplaceKind,
-                             newName);
+                             RawAccessorKind, Name, replaceKind.hasValue(),
+                             rawReplaceKind, newName);
         attachRenameFixIts(diag);
       } else {
         EncodedDiagnosticMessage EncodedMessage(Attr->Message);
         auto diag = diagnose(Loc, diag::availability_decl_unavailable_rename_msg,
-                             Name, replaceKind.hasValue(), rawReplaceKind,
-                             newName, EncodedMessage.Message);
+                             RawAccessorKind, Name, replaceKind.hasValue(),
+                             rawReplaceKind, newName, EncodedMessage.Message);
         attachRenameFixIts(diag);
       }
     } else if (isSubscriptReturningString(D, Context)) {
@@ -2171,12 +2193,13 @@
     } else if (Attr->Message.empty()) {
       diagnose(Loc, inSwift ? diag::availability_decl_unavailable_in_swift
                             : diag::availability_decl_unavailable,
-               Name).highlight(R);
+               RawAccessorKind, Name)
+        .highlight(R);
     } else {
       EncodedDiagnosticMessage EncodedMessage(Attr->Message);
       diagnose(Loc, inSwift ? diag::availability_decl_unavailable_in_swift_msg
                             : diag::availability_decl_unavailable_msg,
-               Name, EncodedMessage.Message)
+               RawAccessorKind, Name, EncodedMessage.Message)
         .highlight(R);
     }
     break;
@@ -2191,20 +2214,23 @@
   case AvailableVersionComparison::Unavailable:
     if (Attr->isLanguageVersionSpecific()
         && Attr->Introduced.hasValue())
-      diagnose(D, diag::availability_introduced_in_swift, Name,
-               *Attr->Introduced).highlight(Attr->getRange());
+      diagnose(D, diag::availability_introduced_in_swift,
+               RawAccessorKind, Name, *Attr->Introduced)
+        .highlight(Attr->getRange());
     else
-      diagnose(D, diag::availability_marked_unavailable, Name)
+      diagnose(D, diag::availability_marked_unavailable, RawAccessorKind, Name)
         .highlight(Attr->getRange());
     break;
 
   case AvailableVersionComparison::Obsoleted:
     // FIXME: Use of the platformString here is non-awesome for application
     // extensions.
-    diagnose(D, diag::availability_obsoleted, Name,
+    diagnose(D, diag::availability_obsoleted,
+             RawAccessorKind, Name,
              (Attr->isLanguageVersionSpecific() ?
               "Swift" : Attr->prettyPlatformString()),
-             *Attr->Obsoleted).highlight(Attr->getRange());
+             *Attr->Obsoleted)
+      .highlight(Attr->getRange());
     break;
   }
   return true;
@@ -2433,8 +2459,18 @@
       return;
     }
 
-    // Make sure not to diagnose an accessor if we already complained about
-    // the property/subscript.
+    // If the property/subscript is unconditionally unavailable, don't bother
+    // with any of the rest of this.
+    if (AvailableAttr::isUnavailable(D->getStorage()))
+      return;
+
+    if (TC.diagnoseExplicitUnavailability(D, ReferenceRange, ReferenceDC,
+                                          /*call*/nullptr)) {
+      return;
+    }
+
+    // Make sure not to diagnose an accessor's deprecation if we already
+    // complained about the property/subscript.
     if (!TypeChecker::getDeprecated(D->getStorage()))
       TC.diagnoseIfDeprecated(ReferenceRange, ReferenceDC, D, /*call*/nullptr);
 
@@ -2549,9 +2585,14 @@
   }
   
   if (!replacement.empty()) {
+    DeclName Name;
+    unsigned RawAccessorKind;
+    std::tie(RawAccessorKind, Name) = getAccessorKindAndNameForDiagnostics(D);
+
     // If we emit a deprecation diagnostic, produce a fixit hint as well.
     auto diag = TC.diagnose(R.Start, diag::availability_decl_unavailable_msg,
-                            D->getFullName(), "it has been removed in Swift 3");
+                            RawAccessorKind, Name,
+                            "it has been removed in Swift 3");
     if (isa<PrefixUnaryExpr>(call)) {
       // Prefix: remove the ++ or --.
       diag.fixItRemove(call->getFn()->getSourceRange());
@@ -2594,9 +2635,13 @@
   if (!args)
     return false;
 
+  DeclName Name;
+  unsigned RawAccessorKind;
+  std::tie(RawAccessorKind, Name) = getAccessorKindAndNameForDiagnostics(D);
+
   EncodedDiagnosticMessage EncodedMessage(Attr->Message);
   auto diag = TC.diagnose(R.Start, diag::availability_decl_unavailable_msg,
-                          D->getFullName(), EncodedMessage.Message);
+                          RawAccessorKind, Name, EncodedMessage.Message);
   diag.highlight(R);
 
   auto subject = args->getSubExpr();
diff --git a/lib/Sema/TypeCheckPattern.cpp b/lib/Sema/TypeCheckPattern.cpp
index cdadb3b..5b8577d 100644
--- a/lib/Sema/TypeCheckPattern.cpp
+++ b/lib/Sema/TypeCheckPattern.cpp
@@ -1383,9 +1383,10 @@
                 EEP->getName().str() == "Some") {
               SmallString<4> Rename;
               camel_case::toLowercaseWord(EEP->getName().str(), Rename);
-              diagnose(EEP->getLoc(), diag::availability_decl_unavailable_rename,
-                          EEP->getName(), /*replaced*/false,
-                          /*special kind*/0, Rename.str())
+              diagnose(EEP->getLoc(),
+                       diag::availability_decl_unavailable_rename,
+                       /*"getter" prefix*/2, EEP->getName(), /*replaced*/false,
+                       /*special kind*/0, Rename.str())
                 .fixItReplace(EEP->getLoc(), Rename.str());
 
               return true;
diff --git a/test/ClangImporter/Inputs/custom-modules/AvailabilityExtras.h b/test/ClangImporter/Inputs/custom-modules/AvailabilityExtras.h
index b151a3b..eb2a7f2 100644
--- a/test/ClangImporter/Inputs/custom-modules/AvailabilityExtras.h
+++ b/test/ClangImporter/Inputs/custom-modules/AvailabilityExtras.h
@@ -107,3 +107,18 @@
 @property (class) int setterDeprecatedClass;
 + (void)setSetterDeprecatedClass:(int)setterDeprecated __attribute__((deprecated));
 @end
+
+
+@interface UnavailableAccessors: NSObject
+@property NSInteger fullyUnavailable __attribute__((unavailable));
+
+@property NSInteger getterUnavailable;
+- (NSInteger)getterUnavailable __attribute__((unavailable));
+@property (class) NSInteger getterUnavailableClass;
++ (NSInteger)getterUnavailableClass __attribute__((unavailable));
+
+@property NSInteger setterUnavailable;
+- (void)setSetterUnavailable:(NSInteger)setterUnavailable __attribute__((unavailable));
+@property (class) NSInteger setterUnavailableClass;
++ (void)setSetterUnavailableClass:(NSInteger)setterUnavailable __attribute__((unavailable));
+@end
diff --git a/test/ClangImporter/availability.swift b/test/ClangImporter/availability.swift
index cc14dd5..9e744f4 100644
--- a/test/ClangImporter/availability.swift
+++ b/test/ClangImporter/availability.swift
@@ -1,4 +1,4 @@
-// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -verify -I %S/Inputs/custom-modules %s
+// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -verify -I %S/Inputs/custom-modules %s -verify-ignore-unknown
 
 // REQUIRES: objc_interop
 
@@ -25,6 +25,28 @@
   NSDeallocateObject(x) // expected-error {{'NSDeallocateObject' is unavailable}}
 }
 
+func test_unavailable_accessors(_ obj: UnavailableAccessors) {
+  _ = obj.fullyUnavailable // expected-error {{'fullyUnavailable' is unavailable}}
+  obj.fullyUnavailable = 0 // expected-error {{'fullyUnavailable' is unavailable}}
+  obj.fullyUnavailable += 1 // expected-error {{'fullyUnavailable' is unavailable}}
+
+  _ = obj.getterUnavailable // expected-error {{getter for 'getterUnavailable' is unavailable}}
+  obj.getterUnavailable = 0
+  obj.getterUnavailable += 1 // expected-error {{getter for 'getterUnavailable' is unavailable}}
+
+  _ = UnavailableAccessors.getterUnavailableClass // expected-error {{getter for 'getterUnavailableClass' is unavailable}}
+  UnavailableAccessors.getterUnavailableClass = 0
+  UnavailableAccessors.getterUnavailableClass += 1 // expected-error {{getter for 'getterUnavailableClass' is unavailable}}
+
+  _ = obj.setterUnavailable
+  obj.setterUnavailable = 0 // expected-error {{setter for 'setterUnavailable' is unavailable}}
+  obj.setterUnavailable += 1 // expected-error {{setter for 'setterUnavailable' is unavailable}}
+
+  _ = UnavailableAccessors.setterUnavailableClass
+  UnavailableAccessors.setterUnavailableClass = 0 // expected-error {{setter for 'setterUnavailableClass' is unavailable}}
+  UnavailableAccessors.setterUnavailableClass += 1 // expected-error {{setter for 'setterUnavailableClass' is unavailable}}
+}
+
 func test_deprecated(_ s:UnsafeMutablePointer<CChar>, _ obj: AccessorDeprecations) {
   _ = tmpnam(s) // expected-warning {{'tmpnam' is deprecated: Due to security concerns inherent in the design of tmpnam(3), it is highly recommended that you use mkstemp(3) instead.}}
 
diff --git a/test/attr/attr_availability.swift b/test/attr/attr_availability.swift
index 61168cf..2772276 100644
--- a/test/attr/attr_availability.swift
+++ b/test/attr/attr_availability.swift
@@ -871,6 +871,61 @@
 deprecatedProperty = 0 // expected-warning {{'deprecatedProperty' is deprecated: bad variable}} {{none}}
 deprecatedProperty += 1 // expected-warning {{'deprecatedProperty' is deprecated: bad variable}} {{none}}
 
+var unavailableGetter: Int {
+  @available(*, unavailable) get { return 0 } // expected-note * {{here}}
+  set {}
+}
+var unavailableGetterOnly: Int {
+  @available(*, unavailable) get { return 0 } // expected-note * {{here}}
+}
+var unavailableSetter: Int {
+  get { return 0 }
+  @available(*, unavailable) set {} // expected-note * {{here}}
+}
+var unavailableBoth: Int {
+  @available(*, unavailable) get { return 0 } // expected-note * {{here}}
+  @available(*, unavailable) set {} // expected-note * {{here}}
+}
+var unavailableMessage: Int {
+  @available(*, unavailable, message: "bad getter") get { return 0 } // expected-note * {{here}}
+  @available(*, unavailable, message: "bad setter") set {} // expected-note * {{here}}
+}
+var unavailableRename: Int {
+  @available(*, unavailable, renamed: "betterThing()") get { return 0 } // expected-note * {{here}}
+  @available(*, unavailable, renamed: "setBetterThing(_:)") set {} // expected-note * {{here}}
+}
+@available(*, unavailable, message: "bad variable")
+var unavailableProperty: Int { // expected-note * {{here}}
+  @available(*, unavailable, message: "bad getter") get { return 0 }
+  @available(*, unavailable, message: "bad setter") set {}
+}
+
+_ = unavailableGetter // expected-error {{getter for 'unavailableGetter' is unavailable}} {{none}}
+unavailableGetter = 0
+unavailableGetter += 1 // expected-error {{getter for 'unavailableGetter' is unavailable}} {{none}}
+
+_ = unavailableGetterOnly // expected-error {{getter for 'unavailableGetterOnly' is unavailable}} {{none}}
+
+_ = unavailableSetter
+unavailableSetter = 0 // expected-error {{setter for 'unavailableSetter' is unavailable}} {{none}}
+unavailableSetter += 1 // expected-error {{setter for 'unavailableSetter' is unavailable}} {{none}}
+
+_ = unavailableBoth // expected-error {{getter for 'unavailableBoth' is unavailable}} {{none}}
+unavailableBoth = 0 // expected-error {{setter for 'unavailableBoth' is unavailable}} {{none}}
+unavailableBoth += 1 // expected-error {{getter for 'unavailableBoth' is unavailable}} {{none}} expected-error {{setter for 'unavailableBoth' is unavailable}} {{none}}
+
+_ = unavailableMessage // expected-error {{getter for 'unavailableMessage' is unavailable: bad getter}} {{none}}
+unavailableMessage = 0 // expected-error {{setter for 'unavailableMessage' is unavailable: bad setter}} {{none}}
+unavailableMessage += 1 // expected-error {{getter for 'unavailableMessage' is unavailable: bad getter}} {{none}} expected-error {{setter for 'unavailableMessage' is unavailable: bad setter}} {{none}}
+
+_ = unavailableRename // expected-error {{getter for 'unavailableRename' has been renamed to 'betterThing()'}} {{none}}
+unavailableRename = 0  // expected-error {{setter for 'unavailableRename' has been renamed to 'setBetterThing(_:)'}} {{none}}
+unavailableRename += 1 // expected-error {{getter for 'unavailableRename' has been renamed to 'betterThing()'}} {{none}} expected-error {{setter for 'unavailableRename' has been renamed to 'setBetterThing(_:)'}} {{none}}
+
+_ = unavailableProperty // expected-error {{'unavailableProperty' is unavailable: bad variable}} {{none}}
+unavailableProperty = 0 // expected-error {{'unavailableProperty' is unavailable: bad variable}} {{none}}
+unavailableProperty += 1 // expected-error {{'unavailableProperty' is unavailable: bad variable}} {{none}}
+
 struct DeprecatedAccessors {
   var deprecatedMessage: Int {
     @available(*, deprecated, message: "bad getter") get { return 0 }
@@ -925,3 +980,58 @@
     other[alsoDeprecated: 0] += 1 // expected-warning {{'subscript(alsoDeprecated:)' is deprecated: bad subscript!}} {{none}}
   }
 }
+
+struct UnavailableAccessors {
+  var unavailableMessage: Int {
+    @available(*, unavailable, message: "bad getter") get { return 0 } // expected-note * {{here}}
+    @available(*, unavailable, message: "bad setter") set {} // expected-note * {{here}}
+  }
+
+  static var staticUnavailable: Int {
+    @available(*, unavailable, message: "bad getter") get { return 0 } // expected-note * {{here}}
+    @available(*, unavailable, message: "bad setter") set {} // expected-note * {{here}}
+  }
+
+  @available(*, unavailable, message: "bad property")
+  var unavailableProperty: Int { // expected-note * {{here}}
+    @available(*, unavailable, message: "bad getter") get { return 0 }
+    @available(*, unavailable, message: "bad setter") set {}
+  }
+
+  subscript(_: Int) -> Int {
+    @available(*, unavailable, message: "bad subscript getter") get { return 0 } // expected-note * {{here}}
+    @available(*, unavailable, message: "bad subscript setter") set {} // expected-note * {{here}}
+  }
+
+  @available(*, unavailable, message: "bad subscript!")
+  subscript(alsoUnavailable _: Int) -> Int { // expected-note * {{here}}
+    @available(*, unavailable, message: "bad subscript getter") get { return 0 }
+    @available(*, unavailable, message: "bad subscript setter") set {}
+  }
+
+  mutating func testAccessors(other: inout UnavailableAccessors) {
+    _ = unavailableMessage // expected-error {{getter for 'unavailableMessage' is unavailable: bad getter}} {{none}}
+    unavailableMessage = 0 // expected-error {{setter for 'unavailableMessage' is unavailable: bad setter}} {{none}}
+    unavailableMessage += 1 // expected-error {{getter for 'unavailableMessage' is unavailable: bad getter}} {{none}} expected-error {{setter for 'unavailableMessage' is unavailable: bad setter}} {{none}}
+
+    _ = other.unavailableMessage // expected-error {{getter for 'unavailableMessage' is unavailable: bad getter}} {{none}}
+    other.unavailableMessage = 0 // expected-error {{setter for 'unavailableMessage' is unavailable: bad setter}} {{none}}
+    other.unavailableMessage += 1 // expected-error {{getter for 'unavailableMessage' is unavailable: bad getter}} {{none}} expected-error {{setter for 'unavailableMessage' is unavailable: bad setter}} {{none}}
+
+    _ = other.unavailableProperty // expected-error {{'unavailableProperty' is unavailable: bad property}} {{none}}
+    other.unavailableProperty = 0 // expected-error {{'unavailableProperty' is unavailable: bad property}} {{none}}
+    other.unavailableProperty += 1 // expected-error {{'unavailableProperty' is unavailable: bad property}} {{none}}
+
+    _ = UnavailableAccessors.staticUnavailable // expected-error {{getter for 'staticUnavailable' is unavailable: bad getter}} {{none}}
+    UnavailableAccessors.staticUnavailable = 0 // expected-error {{setter for 'staticUnavailable' is unavailable: bad setter}} {{none}}
+    UnavailableAccessors.staticUnavailable += 1 // expected-error {{getter for 'staticUnavailable' is unavailable: bad getter}} {{none}} expected-error {{setter for 'staticUnavailable' is unavailable: bad setter}} {{none}}
+
+    _ = other[0] // expected-error {{getter for 'subscript' is unavailable: bad subscript getter}} {{none}}
+    other[0] = 0 // expected-error {{setter for 'subscript' is unavailable: bad subscript setter}} {{none}}
+    other[0] += 1 // expected-error {{getter for 'subscript' is unavailable: bad subscript getter}} {{none}} expected-error {{setter for 'subscript' is unavailable: bad subscript setter}} {{none}}
+
+    _ = other[alsoUnavailable: 0] // expected-error {{'subscript(alsoUnavailable:)' is unavailable: bad subscript!}} {{none}}
+    other[alsoUnavailable: 0] = 0 // expected-error {{'subscript(alsoUnavailable:)' is unavailable: bad subscript!}} {{none}}
+    other[alsoUnavailable: 0] += 1 // expected-error {{'subscript(alsoUnavailable:)' is unavailable: bad subscript!}} {{none}}
+  }
+}
diff --git a/test/attr/attr_availability_osx.swift b/test/attr/attr_availability_osx.swift
index 862476c..473bbdd 100644
--- a/test/attr/attr_availability_osx.swift
+++ b/test/attr/attr_availability_osx.swift
@@ -1,4 +1,12 @@
-// RUN: %swift -typecheck -verify -parse-stdlib -target x86_64-apple-macosx10.10 %s
+// RUN: %swift -typecheck -verify -parse-stdlib -module-name Swift -target x86_64-apple-macosx10.10 %s
+
+// Fake declarations of some standard library features for -parse-stdlib.
+precedencegroup AssignmentPrecedence {}
+enum Optional<T> {
+  case none
+  case some(T)
+}
+
 
 @available(OSX, introduced: 10.5, deprecated: 10.8, obsoleted: 10.9,
               message: "you don't want to do that anyway")
@@ -104,3 +112,32 @@
   TestStruct().doFourthThing() // expected-error {{'doFourthThing()' is only available on OS X 10.12 or newer}} expected-note {{'if #available'}}
   TestStruct().doDeprecatedThing() // expected-warning {{'doDeprecatedThing()' is deprecated}}
 }
+
+extension TestStruct {
+  struct Data {
+    mutating func mutate() {}
+  }
+
+  var unavailableGetter: Data {
+    @available(macOS, unavailable, message: "bad getter")
+    get { return Data() } // expected-note 2 {{here}}
+    set {}
+  }
+
+  var unavailableSetter: Data {
+    get { return Data() }
+    @available(macOS, obsoleted: 10.5, message: "bad setter")
+    set {} // expected-note 2 {{setter for 'unavailableSetter' was obsoleted in OS X 10.5}}
+  }
+}
+
+func testAccessors() {
+  var t = TestStruct()
+  _ = t.unavailableGetter // expected-error {{getter for 'unavailableGetter' is unavailable}}
+  t.unavailableGetter = .init()
+  t.unavailableGetter.mutate() // expected-error {{getter for 'unavailableGetter' is unavailable}}
+
+  _ = t.unavailableSetter
+  t.unavailableSetter = .init() // expected-error {{setter for 'unavailableSetter' is unavailable: bad setter}}
+  t.unavailableSetter.mutate() // expected-error {{setter for 'unavailableSetter' is unavailable: bad setter}}
+}