-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[Diagnostics] Add support for updating the existing 'available' attribute. #72524
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
[Diagnostics] Add support for updating the existing 'available' attribute. #72524
Conversation
1a224c9
to
765b2ec
Compare
@swift-ci please test |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a great start, but I have a few suggestions to refine it.
@@ -51,6 +51,7 @@ func testCV( | |||
|
|||
acceptCV(ns3) // expected-warning {{conformance of 'NS3' to 'Sendable' is only available in macOS 11.0 or newer}} | |||
// expected-note @-1 {{add 'if #available' version check}} | |||
// expected-note @-2 {{change the @available attribute of the global function on macOS from 10.15 to 11.0}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be more typical to emit this note at the source location of the @available
attribute it will change, not on the call that caused the diagnostic.
(This is true for the other test cases, too.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, I see you test the fix-it along with the note in some of these files, but you should probably do it in all of them. (If you don't expect a fix-it, you can assert that by writing {{none}}
—but I'm thinking you probably shouldn't emit this note without a fix-it, so maybe that's behavior you ought to change!)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be more typical to emit this note at the source location of the
@available
attribute it will change, not on the call that caused the diagnostic.(This is true for the other test cases, too.)
At first I also planned to put this note at the source location of the @available
attribute, but that would introduce a new problem. If there more than one call caused the diagnostic, it would result in multiple waning and notes in the same location; Do you have any suggestions for that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, I see you test the fix-it along with the note in some of these files, but you should probably do it in all of them. (If you don't expect a fix-it, you can assert that by writing
{{none}}
—but I'm thinking you probably shouldn't emit this note without a fix-it, so maybe that's behavior you ought to change!)
Regarding fix-it, in the file test/Sema/availability_define.swift, the -define-availability
is used to declare available
, which causes crashes during fix-it testing. Do you have any suggestions?
// RUN: %target-typecheck-verify-swift \
// RUN: -define-availability "_iOS13Aligned:macOS 10.15, iOS 13.0" \
// ...
@available(_iOS13Aligned, *)
func client() {
// ...
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the -define-availability is used to declare available, which causes crashes during fix-it testing
Oh, interesting! I think you'll probably need to debug this issue to figure out why it's happening. Starting points:
- Where in the compiler is the crash? Is it in the
DiagnosticEngine
class? TheDiagnosticVerifier
class? Is the function you modified on the call stack? - What are the source locations you're using from the
AvailableAttr
? Are any of them invalid? (If so, you might be able to add anif
that checks if they're invalid and skips this diagnostic.)
If you get stuck, push the version of your code where you see the crash and I'll try to debug it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If there more than one call caused the diagnostic, it would result in multiple waning and notes in the same location; Do you have any suggestions for that?
I don't have any suggestion for this, but not sure it would be a big problem and it would definitely help to have the note located at the declaration such as users will have better understanding of the availability attribute looking into declaration. Multiple notes, users will be able to reason about them as well IMO.
lib/Sema/TypeCheckAvailability.cpp
Outdated
SourceManager &SM = Context.SourceMgr; | ||
auto Version = SM.extractText(Lexer::getCharSourceRangeFromSourceRange( | ||
SM, Attr->IntroducedRange)) | ||
.str(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of doing this intricate source-manager dance, could you use Attr->Introduced->getAsString()
? That might not produce exactly the same text if there are leading zeroes or something, but it seems much simpler and ought to give you the same semantic result.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for your review.
Attr->Introduced->getAsString()
works well in most cases, but due to canonicalizePlatformVersion transforming the version number, when encountering@available(macOS, introduced: 10.16)
, Attr->Introduced->getAsString()
will return 11.0 instead of 10.16. This may cause more confusion than leading zeros.
llvm::VersionTuple swift::canonicalizePlatformVersion(
PlatformKind platform, const llvm::VersionTuple &version) {
// Canonicalize macOS version for macOS Big Sur to treat
// 10.16 as 11.0.
if (platform == PlatformKind::macOS ||
platform == PlatformKind::macOSApplicationExtension) {
return llvm::Triple::getCanonicalVersionForOS(llvm::Triple::MacOSX,
version);
}
return version;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is okay because technically there was no macOS 10.16. The compiler is rewriting something less correct into something more correct.
Context.Diags | ||
.diagnose(ReferenceRange.Start, diag::update_availability_attribute, | ||
KindForDiagnostic, Attr->platformString(), Version, Required) | ||
.fixItReplace(Attr->IntroducedRange, Required); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like it'll do the right thing for attributes like:
@available(*, unavailable)
(just skips it)@available(macOS, introduced: 10.15)
@available(macOS 10.15, iOS 13, *)
But what if you have something like these? Are there any tests cases like them?
@available(macOS, unavailable)
@available(macOS, deprecated: 12)
(You could just bail out for these like you do for unconditionally unavailable attributes, or you could make your fix-it insert , introduced: 11
after the platform name in these cases.)
Also:
@available(*, deprecated)
(For this, you probably either have to bail out or you have to act as though there is no active attribute and offer to insert a new one.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the deprecated
, I personally believe it is unnecessary to continue expanding to support new capabilities.
The same for unavailable methods, unless a developer actively removes the unavailable
attribute, we should not consider expanding to support new capabilities.
Therefore, I think return
is a good idea.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay. If you don't want to handle those cases (except by return
ing), I would file a follow-up issue suggesting that they could be supported in the future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would also suggest add test cases for those kind of attributes in this PR as extra scenarios if we don't already.
@@ -6649,6 +6649,9 @@ ERROR(availability_variadic_type_only_version_newer, none, | |||
NOTE(availability_guard_with_version_check, none, | |||
"add 'if #available' version check", ()) | |||
|
|||
NOTE(update_availability_attribute, none, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Since the other diagnostic is named availability_add_attribute
, maybe this one should be named availability_update_attribute
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I will change the name in the next patch, thanks.
765b2ec
to
438729b
Compare
Thank you for your response, @beccadax and @LucianoPAlmeida. I made some updates
Thank you for your time and assistance. |
@@ -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)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Should we use quotes on versions as well?
"update @available attribute for %0 from %1 to %2 to meet the requirements of '%3'", (StringRef, StringRef, StringRef, StringRef)) | |
"update @available attribute for %0 from '%1' to '%2' to meet the requirements of '%3'", (StringRef, StringRef, StringRef, StringRef)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
lib/Sema/TypeCheckAvailability.cpp
Outdated
// `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)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we also check if range is valid?
if (!Attr->getRange().overlaps(Attr->IntroducedRange)) { | |
if (!Attr->getRange().isValid() || !Attr->getRange().overlaps(Attr->IntroducedRange)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
6893a0f
to
cc6e0be
Compare
cc6e0be
to
bf9c330
Compare
Revise the context to recommend an update to the existing 'available' attribute.
Consider the following code:
A new fix-it suggests altering the '@available' attribute of the global function on macOS from 12 to 42.
Resolves #57378