Skip to content

Commit

Permalink
Modify diagnostics to suggest updating existing available attribute
Browse files Browse the repository at this point in the history
  • Loading branch information
kongkaikai committed Apr 1, 2024
1 parent fc6011c commit 438729b
Show file tree
Hide file tree
Showing 11 changed files with 157 additions and 11 deletions.
3 changes: 3 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -6655,6 +6655,9 @@ ERROR(availability_variadic_type_only_version_newer, none,
NOTE(availability_guard_with_version_check, none,
"add 'if #available' version check", ())

NOTE(availability_update_attribute, none,
"update @available attribute for %0 from %1 to %2 to meet the requirements of '%3'", (StringRef, StringRef, StringRef, StringRef))

NOTE(availability_add_attribute, none,
"add @available attribute to enclosing %0", (DescriptiveDeclKind))
FIXIT(insert_available_attr,
Expand Down
21 changes: 19 additions & 2 deletions lib/Sema/TypeCheckAvailability.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1821,8 +1821,25 @@ static void fixAvailabilityForDecl(SourceRange ReferenceRange, const Decl *D,
if (TypeChecker::diagnosticIfDeclCannotBePotentiallyUnavailable(D).has_value())
return;

if (getActiveAvailableAttribute(D, Context)) {
// For QoI, in future should emit a fixit to update the existing attribute.
if (auto Attr = getActiveAvailableAttribute(D, Context)) {
// Check if the `range` of the attribute `Attr` overlaps with its
// `IntroducedRange`, which can be from a macro or the minimum version
// supported by the compiler, etc. If there's no explicit introduction range
// marked in the code, skip the update logic and return immediately.
if (!Attr->getRange().overlaps(Attr->IntroducedRange)) {
return;
}

auto Version = Attr->Introduced->getAsString();
auto Required = RequiredRange.getLowerEndpoint().getAsString();
auto ReferenceName =
Context.SourceMgr
.extractText(Lexer::getCharSourceRangeFromSourceRange(
Context.SourceMgr, ReferenceRange))
.str();
D->diagnose(diag::availability_update_attribute, Attr->platformString(),
Version, Required, ReferenceName)
.fixItReplace(Attr->IntroducedRange, Required);
return;
}

Expand Down
1 change: 1 addition & 0 deletions test/Sema/api-availability-only.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ public struct S {}

@available(macOS 10.51, *)
public func newFunc() {
// expected-note @-1 {{update @available attribute for macOS from 10.51 to 10.52 to meet the requirements of 'S'}} {{9:18-23=10.52}}
_ = S() // expected-error {{'S' is only available in}}
// expected-note @-1 {{add 'if #available' version check}}
}
1 change: 1 addition & 0 deletions test/Sema/availability_swiftui.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ class AnyColorBox: LessAvailable {} // Ok, exception specifically for AnyColorBo
@available(macOS 10.15, *)
@usableFromInline
class OtherClass: LessAvailable {} // expected-error {{'LessAvailable' is only available in macOS 11 or newer; clients of 'SwiftUI' may have a lower deployment target}}
// expected-note @-1 {{update @available attribute for macOS from 10.15 to 11 to meet the requirements of 'LessAvailable'}} {{13:18-23=11}}
25 changes: 19 additions & 6 deletions test/Sema/availability_versions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ func functionWithoutAvailability() {
// Functions with annotations should refine their bodies.
@available(OSX, introduced: 10.51)
func functionAvailableOn10_51() {
// expected-note@-1 {{update @available attribute for macOS from 10.51 to 10.52 to meet the requirements of 'globalFuncAvailableOn10_52'}} {{43:29-34=10.52}}
let _: Int = globalFuncAvailableOn10_9()
let _: Int = globalFuncAvailableOn10_51()

Expand Down Expand Up @@ -324,10 +325,11 @@ class ClassWithPotentiallyUnavailableProperties {

@available(OSX, introduced: 10.9)
var availableOn10_9Computed: Int {
// expected-note@-1 {{update @available attribute for macOS from 10.9 to 10.51 to meet the requirements of 'availableOn10_51Stored'}} {{326:31-35=10.51}}
get {
let _: Int = availableOn10_51Stored // expected-error {{'availableOn10_51Stored' is only available in macOS 10.51 or newer}}
// expected-note@-1 {{add 'if #available' version check}}

if #available(OSX 10.51, *) {
let _: Int = availableOn10_51Stored
}
Expand Down Expand Up @@ -406,6 +408,7 @@ class ClassWithPotentiallyUnavailableProperties {

@available(OSX, introduced: 10.51)
class ClassWithReferencesInInitializers {
// expected-note@-1 2 {{update @available attribute for macOS from 10.51 to 10.52 to meet the requirements of 'globalFuncAvailableOn10_52'}} {{409:29-34=10.52}}
var propWithInitializer10_51: Int = globalFuncAvailableOn10_51()

var propWithInitializer10_52: Int = globalFuncAvailableOn10_52() // expected-error {{'globalFuncAvailableOn10_52()' is only available in macOS 10.52 or newer}}
Expand Down Expand Up @@ -522,6 +525,8 @@ enum EnumIntroducedOn10_52 {

@available(OSX, introduced: 10.51)
enum CompassPoint {
// expected-note@-1 3 {{update @available attribute for macOS from 10.51 to 10.52 to meet the requirements of 'EnumIntroducedOn10_52'}} {{526:29-34=10.52}}

case North
case South
case East
Expand All @@ -542,7 +547,7 @@ enum CompassPoint {
case WithPotentiallyUnavailablePayload(p : EnumIntroducedOn10_52) // expected-error {{'EnumIntroducedOn10_52' is only available in macOS 10.52 or newer}}

case WithPotentiallyUnavailablePayload1(p : EnumIntroducedOn10_52), WithPotentiallyUnavailablePayload2(p : EnumIntroducedOn10_52) // expected-error 2{{'EnumIntroducedOn10_52' is only available in macOS 10.52 or newer}}

@available(OSX, unavailable)
case WithPotentiallyUnavailablePayload3(p : EnumIntroducedOn10_52)
}
Expand Down Expand Up @@ -876,20 +881,21 @@ class SubWithLargerMemberAvailability : SuperWithLimitedMemberAvailability {
// expected-note@-1 2{{add @available attribute to enclosing class}}
@available(OSX, introduced: 10.9)
override func someMethod() {
// expected-note@-1 {{update @available attribute for macOS from 10.9 to 10.51 to meet the requirements of 'someMethod'}} {{882:31-35=10.51}}
super.someMethod() // expected-error {{'someMethod()' is only available in macOS 10.51 or newer}}
// expected-note@-1 {{add 'if #available' version check}}

if #available(OSX 10.51, *) {
super.someMethod()
}
}

@available(OSX, introduced: 10.9)
override var someProperty: Int {
get {
// expected-note@-1 {{update @available attribute for macOS from 10.9 to 10.51 to meet the requirements of 'someProperty'}} {{893:31-35=10.51}}
get {
let _ = super.someProperty // expected-error {{'someProperty' is only available in macOS 10.51 or newer}}
// expected-note@-1 {{add 'if #available' version check}}

// expected-note@-1 {{add 'if #available' version check}}
if #available(OSX 10.51, *) {
let _ = super.someProperty
}
Expand Down Expand Up @@ -961,6 +967,7 @@ protocol ProtocolAvailableOn10_51 {

@available(OSX, introduced: 10.9)
protocol ProtocolAvailableOn10_9InheritingFromProtocolAvailableOn10_51 : ProtocolAvailableOn10_51 { // expected-error {{'ProtocolAvailableOn10_51' is only available in macOS 10.51 or newer}}
// expected-note@-1 {{update @available attribute for macOS from 10.9 to 10.51 to meet the requirements of 'ProtocolAvailableOn10_51'}} {{968:29-33=10.51}}
}

@available(OSX, introduced: 10.51)
Expand All @@ -973,6 +980,7 @@ protocol UnavailableProtocolInheritingFromProtocolAvailableOn10_51 : ProtocolAva

@available(OSX, introduced: 10.9)
class SubclassAvailableOn10_9OfClassAvailableOn10_51 : ClassAvailableOn10_51 { // expected-error {{'ClassAvailableOn10_51' is only available in macOS 10.51 or newer}}
// expected-note@-1 {{update @available attribute for macOS from 10.9 to 10.51 to meet the requirements of 'ClassAvailableOn10_51'}} {{981:29-33=10.51}}
}

@available(OSX, unavailable)
Expand All @@ -997,12 +1005,14 @@ func castToPotentiallyUnavailableProtocol() {

@available(OSX, introduced: 10.9)
class SubclassAvailableOn10_9OfClassAvailableOn10_51AlsoAdoptingProtocolAvailableOn10_51 : ClassAvailableOn10_51, ProtocolAvailableOn10_51 { // expected-error {{'ClassAvailableOn10_51' is only available in macOS 10.51 or newer}}
// expected-note@-1 {{update @available attribute for macOS from 10.9 to 10.51 to meet the requirements of 'ClassAvailableOn10_51'}} {{1006:29-33=10.51}}
}

class SomeGenericClass<T> { }

@available(OSX, introduced: 10.9)
class SubclassAvailableOn10_9OfSomeGenericClassOfProtocolAvailableOn10_51 : SomeGenericClass<ProtocolAvailableOn10_51> { // expected-error {{'ProtocolAvailableOn10_51' is only available in macOS 10.51 or newer}}
// expected-note@-1 {{update @available attribute for macOS from 10.9 to 10.51 to meet the requirements of 'ProtocolAvailableOn10_51'}} {{1013:29-33=10.51}}
}

@available(OSX, unavailable)
Expand Down Expand Up @@ -1049,6 +1059,7 @@ extension ClassAvailableOn10_51 { }

@available(OSX, introduced: 10.51)
extension ClassAvailableOn10_51 {
// expected-note@-1 {{update @available attribute for macOS from 10.51 to 10.52 to meet the requirements of 'globalFuncAvailableOn10_52'}} {{1060:29-34=10.52}}
func m() {
// expected-note@-1 {{add @available attribute to enclosing instance method}}
let _ = globalFuncAvailableOn10_51()
Expand Down Expand Up @@ -1653,6 +1664,7 @@ class ClassWithShortFormAvailableOn10_54 {

@available(OSX 10.9, *)
func funcWithShortFormAvailableOn10_9() {
// expected-note@-1 {{update @available attribute for macOS from 10.9 to 10.51 to meet the requirements of 'ClassWithShortFormAvailableOn10_51'}} {{1665:16-20=10.51}}
let _ = ClassWithShortFormAvailableOn10_51() // expected-error {{'ClassWithShortFormAvailableOn10_51' is only available in macOS 10.51 or newer}}
// expected-note@-1 {{add 'if #available' version check}}
}
Expand Down Expand Up @@ -1685,6 +1697,7 @@ func funcWithMultipleShortFormAnnotationsForDifferentPlatforms() {
@available(OSX 10.53, *)
@available(OSX 10.52, *)
func funcWithMultipleShortFormAnnotationsForTheSamePlatform() {
// expected-note@-1 {{update @available attribute for macOS from 10.52 to 10.54 to meet the requirements of 'ClassWithShortFormAvailableOn10_54'}} {{1698:16-21=10.54}}
let _ = ClassWithShortFormAvailableOn10_53()

let _ = ClassWithShortFormAvailableOn10_54() // expected-error {{'ClassWithShortFormAvailableOn10_54' is only available in macOS 10.54 or newer}}
Expand Down
6 changes: 6 additions & 0 deletions test/Sema/availability_versions_multi.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ let ignored3: Int = globalAvailableOn99_52 // expected-error {{'globalAvailableO

@available(OSX, introduced: 99.51)
func useFromOtherOn99_51() {
// expected-note@-1 {{update @available attribute for macOS from 99.51 to 99.52 to meet the requirements of 'returns99_52Introduced99_52'}} {{26:29-34=99.52}}
// expected-note@-2 {{update @available attribute for macOS from 99.51 to 99.52 to meet the requirements of 'OtherIntroduced99_52'}} {{26:29-34=99.52}}
// expected-note@-3 {{update @available attribute for macOS from 99.51 to 99.52 to meet the requirements of 'extensionMethodOnOtherIntroduced99_51AvailableOn99_52'}} {{26:29-34=99.52}}
// expected-note@-4 {{update @available attribute for macOS from 99.51 to 99.52 to meet the requirements of 'NestedIntroduced99_52'}} {{26:29-34=99.52}}

// This will trigger validation of OtherIntroduced99_51 in
// in availability_multi_other.swift
let o99_51 = OtherIntroduced99_51()
Expand All @@ -49,6 +54,7 @@ func useFromOtherOn99_51() {

@available(OSX, introduced: 99.52)
func useFromOtherOn99_52() {
// expected-note@-1 {{update @available attribute for macOS from 99.52 to 99.53 to meet the requirements of 'returns99_53'}} {{55:29-34=99.53}}
_ = OtherIntroduced99_52()

let n99_52 = OtherIntroduced99_51.NestedIntroduced99_52()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

@main // expected-error {{'main()' is only available in macOS 10.99 or newer}}
@available(OSX 10.0, *)
struct EntryPoint {
struct EntryPoint { // expected-note {{update @available attribute for macOS from 10.0 to 10.99 to meet the requirements of '@main'}} {{6:16-20=10.99}}
@available(OSX 10.99, *)
static func main() {
}
Expand Down
2 changes: 1 addition & 1 deletion test/attr/attr_availability_osx.swift
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ extension TestStruct {
}

@available(macOS 10.11, *)
func testMemberAvailability() {
func testMemberAvailability() { // expected-note {{update @available attribute for macOS from 10.11 to 10.12 to meet the requirements of 'doFourthThing'}} {{117:18-23=10.12}}
TestStruct().doTheThing() // expected-error {{'doTheThing()' is unavailable}}
TestStruct().doAnotherThing() // expected-error {{'doAnotherThing()' is unavailable}}
TestStruct().doThirdThing() // expected-error {{'doThirdThing()' is unavailable}}
Expand Down
69 changes: 69 additions & 0 deletions test/attr/attr_availability_update.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// RUN: %swift -typecheck -verify -parse-stdlib -module-name Swift -target x86_64-apple-macosx10.10 %s

@available(SwiftStdlib 5.1, *)
func FooForDefineAvailability() {}

@available(SwiftStdlib 5.0, *)
func FooForDefineAvailabilityTest() {
FooForDefineAvailability()
// expected-error@-1 {{'FooForDefineAvailability()' is only available in macOS 10.15 or newer}}
// expected-note@-2 {{add 'if #available' version check}}
}

@available(*, unavailable)
func FooForUnavailable() {} // expected-note {{'FooForUnavailable()' has been explicitly marked unavailable here}}

func FooForUnavailableTest() {
FooForUnavailable()
// expected-error@-1 {{'FooForUnavailable()' is unavailable}}
}

@available(macOS, introduced: 10.15)
func FooForAvailability() {}

@available(macOS, introduced: 10.10)
func FooForAvailabilityTest() {
// expected-note@-1 {{update @available attribute for macOS from 10.10 to 10.15 to meet the requirements of 'FooForAvailability'}} {{24:31-36=10.15}}
FooForAvailability()
// expected-error@-1 {{'FooForAvailability()' is only available in macOS 10.15 or newer}}
// expected-note@-2 {{add 'if #available' version check}}
}

@available(macOS 10.15, iOS 13, *)
func FooForAvailability2() {
}

@available(macOS 10.10, *)
func FooForAvailability2Test() {
// expected-note@-1 {{update @available attribute for macOS from 10.10 to 10.15 to meet the requirements of 'FooForAvailability2'}} {{36:18-23=10.15}}
FooForAvailability2()
// expected-error@-1 {{'FooForAvailability2()' is only available in macOS 10.15 or newer}}
// expected-note@-2 {{add 'if #available' version check}}
}

@available(macOS 10.15, *)
func FooForUnavailable2() {}

@available(macOS, unavailable)
func FooForUnavailable2Test() {
FooForUnavailable2()
// expected-error@-1 {{'FooForUnavailable2()' is only available in macOS 10.15 or newer}}
// expected-note@-2 {{add 'if #available' version check}}
}

@available(macOS 10.15, *)
func FooForDeprecated() {}

@available(macOS, deprecated: 12)
func FooForDeprecatedTest() {
FooForDeprecated()
// expected-error@-1 {{'FooForDeprecated()' is only available in macOS 10.15 or newer}}
// expected-note@-2 {{add 'if #available' version check}}
}

@available(*, deprecated)
func FooForDeprecatedTest2() {
FooForDeprecated()
// expected-error@-1 {{'FooForDeprecated()' is only available in macOS 10.15 or newer}}
// expected-note@-2 {{add 'if #available' version check}}
}
Loading

0 comments on commit 438729b

Please sign in to comment.