-
Notifications
You must be signed in to change notification settings - Fork 10.3k
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
Use the custom @objc name in the available attribute when generates ObjC headers #18081
Conversation
I have a few questions for this PR before proceeding
|
Thanks, I'll review this soon. Answers to your questions:
|
lib/PrintAsObjC/PrintAsObjC.cpp
Outdated
@@ -771,18 +771,18 @@ class ObjCPrinter : private DeclVisitor<ObjCPrinter>, | |||
!FD->getAttrs().hasAttribute<DiscardableResultAttr>()) { | |||
os << " SWIFT_WARN_UNUSED_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.
nit: preserve whitespaces? applies to line 779, 784, 785
test/PrintAsObjC/availability.swift
Outdated
@@ -82,7 +106,7 @@ | |||
|
|||
@objc class Availability { | |||
@objc func alwaysAvailable() {} | |||
|
|||
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: perverse whitespaces?
lib/PrintAsObjC/PrintAsObjC.cpp
Outdated
printEncodedString(renamedObjCRuntimeName, false); | ||
} else { | ||
printEncodedString(AvAttr->Rename, false); | ||
} |
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: This is identical to line 923-931, and almost the same as 839-847. consider refactor this into a function?
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'm with varokas on this. I think you could push this all into findRenamedDecl
and rename it to something like printRenameForDecl
.
@jrose-apple thank you. I’ll work on the number 2 on tomorrow and May try the number 3 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.
This is looking great! I have a lot of small comments, but the general approach is good.
I'd also like to see a lot more tests:
- Renames where there are multiple methods with the same full name but different parameter types.
- Renames where the renamed-to method comes from Objective-C.
- Renames where the renamed-to method is a class method instead of an instance method or vice versa.
- Renames of initializers.
- Renames of methods to properties or properties to methods.
- Renames to things that just don't exist.
…and that's all I can think of for now. But worth testing, yeah? :-)
Thanks again for picking this up!
lib/PrintAsObjC/PrintAsObjC.cpp
Outdated
printEncodedString(renamedObjCRuntimeName, false); | ||
} else { | ||
printEncodedString(AvAttr->Rename, false); | ||
} |
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'm with varokas on this. I think you could push this all into findRenamedDecl
and rename it to something like printRenameForDecl
.
lib/PrintAsObjC/PrintAsObjC.cpp
Outdated
@@ -1884,6 +1906,46 @@ class ObjCPrinter : private DeclVisitor<ObjCPrinter>, | |||
visitPart(RST->getReferentType(), optionalKind); | |||
} | |||
|
|||
Optional<ValueDecl *> findRenamedDecl(const swift::AvailableAttr *AvAttr, const swift::Decl *D) { |
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.
Nitpick: We have an 80-character column limit, but you can also drop the swift::
namespace whenever you're in a Swift source file.
lib/PrintAsObjC/PrintAsObjC.cpp
Outdated
Optional<ValueDecl *>renamedFuncDecl = None; | ||
|
||
if (isa<ClassDecl>(D) || isa<ProtocolDecl>(D)) { | ||
UnqualifiedLookup lookup(renamedDeclName.getBaseIdentifier(), declContext->getModuleScopeContext(), nullptr); |
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.
Nitpick: You can pass UnqualifiedLookup::Flags::TypeLookup
to the "options" parameter here to make the lookup slightly more efficient.
lib/PrintAsObjC/PrintAsObjC.cpp
Outdated
auto renamedDeclName = renamedParsedDeclName.formDeclName(D->getASTContext()); | ||
|
||
auto declContext = D->getDeclContext(); | ||
Optional<ValueDecl *>renamedFuncDecl = 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.
Nitpick: This isn't always a function now. :-) You can also probably use nullptr
as your "not found" value and drop the Optional
. It's a little more subtle, but a pretty common idiom in our C++ code.
lib/PrintAsObjC/PrintAsObjC.cpp
Outdated
if (!isa<ValueDecl>(D)) | ||
return None; | ||
|
||
SmallVector<ValueDecl *, 4> lookupResults; |
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.
Nitpick: Might as well move this into the qualified lookup branch now!
lib/PrintAsObjC/PrintAsObjC.cpp
Outdated
if (candidate->getKind() == DeclKind::Func && isa<FuncDecl>(candidate) | ||
&& cast<FuncDecl>(candidate)->getParameterLists().size() != | ||
cast<FuncDecl>(D)->getParameterLists().size()) | ||
continue; |
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 you're trying to check that the number of parameters is the same here? That's not a bad idea, but it's not getParameterLists()
that you want. Fortunately Slava P just added a useful API for what you do want: getParameters()
.
Another good thing to check is whether they're both instance members.
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.
THAT IS GREAT!!! Thank you Slava P :D
lib/PrintAsObjC/PrintAsObjC.cpp
Outdated
if (candidate->getKind() != D->getKind()) | ||
continue; | ||
|
||
if (candidate->getKind() == DeclKind::Func && isa<FuncDecl>(candidate) |
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.
It seems like you can use isa
for candidate
too, no?
renamedDeclName, NL_QualifiedDefault, NULL, | ||
lookupResults); | ||
for (auto candidate : lookupResults) { | ||
if (!shouldInclude(candidate)) |
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.
Good idea here—easy way to filter for ObjC-compatible things only!
lib/PrintAsObjC/PrintAsObjC.cpp
Outdated
continue; | ||
|
||
if (renamedFuncDecl) | ||
/* TODO: Diagnose to compiler to tell the user that we found multiple candidates */; |
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's okay to just fail in this case, at least for now. Incremental improvements are good!
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.
How should I make it fail here? Just fallback to the name in the rename 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.
Yeah, that's what it's doing now. We could also consider not putting anything in the "rename" attribute when we can't get an ObjC name, and rely on the message. But for now, let's keep the existing behavior as our fallback.
…s the ObjC header
…d protocols when generates the ObjC header
…d not instance method
…d not instance method and vice versa
…d be instance members or otherwise
0824aa7
to
045fb7c
Compare
@jrose-apple I fixed and updated the code as you suggested. Also I added some test cases too
However I do have a few questions.
Please have a look again :) |
Well, you could have a test that imports the real Foundation and subclasses or extends something there. (Ooh, that's another test case: "renames where the renamed-to method comes from a superclass".)
Yeah, I'm happy to call this a fallback case. We could arguably handle the method-to-property case by using the name of the getter, and the property-to-method case by finding a no-argument method, but again, those changes can come later. |
lib/PrintAsObjC/PrintAsObjC.cpp
Outdated
void printRenameForDecl(const AvailableAttr *AvAttr, const Decl *D, | ||
bool includeQuotes) { | ||
if (AvAttr->Rename.empty() && isa<ValueDecl>(D)) | ||
return ; |
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.
We've already checked that this is a ValueDecl at all call sites, so you can probably just drop that. (Also, stray space before the semicolon.)
lib/PrintAsObjC/PrintAsObjC.cpp
Outdated
continue; | ||
|
||
if (renamedFuncDecl) | ||
/* TODO: Diagnose to compiler to tell the user that we found multiple candidates */; |
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.
Still gotta do something here before we check this in. Just keeping track of a second boolean flag is probably fine.
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.
So we should just fail here and fallback to the AvAttr->Rename
here?
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.
Yeah, the same thing we'd do for a "we found nothing that matches" case.
lib/PrintAsObjC/PrintAsObjC.cpp
Outdated
auto renamedDeclName = renamedParsedDeclName.formDeclName(D->getASTContext()); | ||
|
||
auto declContext = D->getDeclContext(); | ||
ValueDecl *renamedFuncDecl = nullptr; |
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.
Still not necessarily a function anymore. :-) Also, may as well mark this const
.
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.
Still not necessarily a function anymore
I'm not sure what you mean here
test/PrintAsObjC/availability.swift
Outdated
@@ -45,11 +45,29 @@ | |||
// CHECK-DAG: SWIFT_AVAILABILITY(ios_app_extension,unavailable) | |||
// CHECK-DAG: SWIFT_AVAILABILITY(tvos_app_extension,unavailable) | |||
// CHECK-DAG: SWIFT_AVAILABILITY(watchos_app_extension,unavailable) | |||
// CHECK-DAG: ; |
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's okay to leave this out, but if you want to have it you should use CHECK-SAME
rather than CHECK-DAG
. The "DAG" suffix means it's reorderable with other CHECK-DAG
lines immediately before and after this 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.
Oh I just learn about CHECK-SAME
, thanks
test/PrintAsObjC/availability.swift
Outdated
// CHECK-NEXT: + (void)makeDeprecatedAvailabilityWithValue:(NSInteger)value SWIFT_DEPRECATED_MSG("use something else", "deprecatedAvailabilityWithValue:"); | ||
// CHECK-NEXT: + (void)unavailableAvailabilityWithValue:(NSInteger)value; | ||
// CHECK-NEXT: + (void)makeUnavailableAvailabilityWithValue:(NSInteger)value | ||
// CHECK-DAG: SWIFT_UNAVAILABLE_MSG("'__makeUnavailableAvailability' has been renamed to 'unavailableAvailabilityWithValue:': use something else"); |
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.
CHECK-SAME would be a better choice here too (since in this case there's only one thing to match).
test/PrintAsObjC/availability.swift
Outdated
} | ||
|
||
@objc(SWTReplacementAvailableProtocol) protocol ReplacementAvailableProtocol { | ||
@objc(methodReplacingInDeprecatedClassWithFirst:second:) func methodReplacingInDeprecatedClass(first: Int, second: Int) -> Void |
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'll note that this is the same as the default name; you may want to pick something else.
test/PrintAsObjC/availability.swift
Outdated
@objc func deprecatedMethodRenamedToOverloadMethod1(first: Int, second: Int) {} | ||
|
||
@objc(overloadMethodWithFirst:second:) func overloadMethod1(first: Int, second: Int) {} | ||
@objc func overloadMethod2(first: Double, second: Double) {} |
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.
Wait, these aren't overloads if you give them different base names.
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.
OH what a mistake!!!
Oh, more possible test cases:
|
@jrose-apple I updated the code and added more test cases as you suggested. Could you review it again? However there is an error while I was trying to add a test case for
What I did was adding How could I fix this? |
@jrose-apple I also implement the type following feature (e.g. Should I include this change with this PR? |
Most of the PrintAsObjC tests, including availability.swift, use the "mock SDK" in test/Inputs/clang-importer-sdk/ in order to build faster, as well as decreasing the likelihood that a test will need to be updated for a new Xcode release. The catch, though, is that when you use the mock SDK, you have to set up appropriate equivalents for the overlays as well. You can see this in test/PrintAsObjC/classes.swift as "-enable-source-import hackaround". There are a few options here: you can copy that workaround over to availability.swift, or you could make a new test file that uses the real SDK instead of the mock SDK (which would mostly just mean removing the "(mock-sdk: %clang-importer-sdk)" and "-disable-objc-attr-requires-foundation-module" from the |
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 code logic looks good, just a few tiny more changes I suggest. I'll look over all the tests I asked you to add tomorrow (Pacific time).
lib/PrintAsObjC/PrintAsObjC.cpp
Outdated
auto renamedDeclName = renamedParsedDeclName.formDeclName(D->getASTContext()); | ||
|
||
auto declContext = D->getDeclContext(); | ||
const ValueDecl *renamedFuncDecl = nullptr; |
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 still shouldn't be called "renamedFuncDecl" when it's very often not a function. :-)
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.
Oh right, sorry that I missed that
lib/PrintAsObjC/PrintAsObjC.cpp
Outdated
void printRenameForDecl(const AvailableAttr *AvAttr, const ValueDecl *D, | ||
bool includeQuotes) { | ||
if (AvAttr->Rename.empty()) | ||
return; |
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 you can assert this rather than early-exiting. Any caller would already have to deal with the no-rename case differently.
(Sorry I didn't make this comment last time. I guess I was distracted by the formatting.)
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'll change this to assert instead
At this point I feel bad for dragging this one out, so let's not include that. That way we can merge this PR sooner. |
OK I'll open it as a new PR since I also have some ideas on improving this further :) |
@jrose-apple I fixed the code and added test cases you asked for yesterday already. Please have a look again. P.S. I also added a new |
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.
Tests all look good! Thanks for ticking all the boxes I asked for.
test/PrintAsObjC/availability.swift
Outdated
// CHECK-NEXT: + (void)makeDeprecatedAvailabilityWithValue:(NSInteger)value SWIFT_DEPRECATED_MSG("use something else", "deprecatedAvailabilityWithValue:"); | ||
// CHECK-NEXT: + (void)unavailableAvailabilityWithValue:(NSInteger)value; | ||
// CHECK-NEXT: + (void)makeUnavailableAvailabilityWithValue:(NSInteger)value | ||
// CHECK-SAME: SWIFT_UNAVAILABLE_MSG("'__makeUnavailableAvailability' has been renamed to 'unavailableAvailabilityWithValue:': use something else"); |
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.
Hm, yeah, we should indeed fix the unavailable macro.
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.
Fix the unavailable macro? How?
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.
Oh, never mind. I forgot that __attribute__((deprecated(…)))
supports a replacement but __attribute__((unavailable(…)))
does not. (Arguably we could use __attribute__((available(*, unavailable, renamed="…")))
for this, but that can be tested out later.
// CHECK-NEXT: - (nonnull instancetype)initWithNewZ:(NSInteger)z OBJC_DESIGNATED_INITIALIZER; | ||
// CHECK-NEXT: @end | ||
|
||
// CHECK-LABEL: SWIFT_AVAILABILITY(macos,deprecated=0.0.1,message="'DeprecatedAvailability' has been renamed to 'SWTReplacementAvailable'") |
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.
Another case where we ought to support renamed=""
but don't. (Not a problem for this PR.)
@swift-ci Please test |
Build failed |
Build failed |
@jrose-apple Thank you. I'll add the test cases for the unavailable in the afternoon Thailand time and will ask Swift-CI to run the test. Hope it'll be ready to merge within this week :D |
…orm unavailable PrintAsObjC
@jrose-apple I added the remaining platform deprecation, unavailable and platform unavailable test cases. Could you have a look again and ask the @swift-ci to run a test |
@swift-ci Please test |
Build failed |
Build failed |
test/PrintAsObjC/availability.swift
Outdated
@objc public func methodWithoutCustomObjCName(value: Int) -> Int { return -1 } | ||
|
||
@available(*, deprecated, renamed: "methodWithoutCustomObjCName(value:)") | ||
@objc public func deprecatedMethodRenamedToMethodWithouCustomObjCName(value: Int) -> Int { return -1 } |
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.
Not that it's super important, but for this family you wrote "Withou" instead of "Without".
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.
@jrose-apple Should I fix it here or can leave it for the next PR?
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's okay to leave it. Let's get this merged!
Thanks once again for working on this! Looking forward to more patches from you in the future. |
@jrose-apple OMG OMG OMG Thank you for helping me from the start. I'm glad to have a chance to work on this and on Swift open source project. I'm looking forward to contribute to Swift in the future too :D |
Printing the custom
@objc
name in availability attributes in the generated Objective-C header files instead of printing the Swift name. This should help the compiler/fix-it to change the deprecated/unavailable API usage with the appropriate nameResolves SR-8231.