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
Add new error diagnostic for escaping optional closures #30746
Add new error diagnostic for escaping optional closures #30746
Conversation
lib/Sema/TypeCheckType.cpp
Outdated
@@ -2428,7 +2428,7 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, | |||
// a function type, we'll remove it. | |||
if (i == TAK_escaping) { | |||
diag.fixItRemove(getTypeAttrRangeWithAt(Context, | |||
attrs.getLoc(TAK_escaping))); | |||
attrs.getLoc(TAK_escaping))); |
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.
Oops, not this one though, line 2421. Thanks!
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'd suggest running clang-format on your changes @ismetanin
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.
Thanks for your suggestion, now everything should be ok (or at least clang-formatted)
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've only run it on the last commit.
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.
Looks like you're right. I've run clang-format over all my changes now
Sorry for these formatting mistakes, now I know how to properly format code
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.
No worries, thanks for being patient!
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.
Maybe would be good if those indentation changes, could be squashed in a single commit?
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.
@xedin can squash them upon merging
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.
No problems, I've squashed indentation changes with the second commit
…f original message when object type is a function type
lib/Sema/TypeCheckType.cpp
Outdated
diag.flush(); | ||
diagnoseInvalid(repr, repr->getLoc(), | ||
diag::escaping_optional_type_argument); | ||
if (i == TAK_escaping && ty->getOptionalObjectType()) { |
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.
Unfortunately checking for ty->getOptionalObjectType()
is not enough, to produce escaping_optional_type_argument
we have to make sure that object type is indeed a function type (AnyFunctionType
to be precise). I think it makes sense to try and unify this if/else into a single if
for i == TAK_escaping
and have some additional logic to pick which message to produce inside it because both cases share logic to emit a fix-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.
So, I tried but couldn't unify if/else into a single if
for i == TAK_escaping
😕
We can't reassign InFlightDiagnostic so I couldn't reuse one fixItRemove
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.
You could define a lambda for the correct InFlightDiagnostic
outside the loop and call it instead of diagnoseInvalid
. As a bonus, we get to adjust the flow to only check against an optional when i == TAK_escaping
.
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 didn't catch the idea with lambda 😢
If we declare lambda outside it will anyway produce InFlightDiagnostic
that we can't reassign so we'll still have to use double-check i == TAK_escaping
and double-use fixItRemove
Am I wrong?
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 lambda emits the correct diagnostic and returns an InFlightDiagnostic
you can then attach stuff to; no need to reassign anything:
const auto diagnoseInvalidAttr = [&](TypeAttrKind kind) {
if (kind == TAK_escaping) {
Type optionalObjectType = ty->getOptionalObjectType();
if (optionalObjectType && optionalObjectType->is<AnyFunctionType>())
return diagnoseInvalid(...);
}
return diagnoseInvalid(...);
};
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.
Agree with @AnthonyLatsis, You can even pass the attr so you don't have to define the lambda inside the loop :)
const auto diagnoseInvalidAttr = [&](Type attributeType, TypeAttrKind attrKind) {
if (attributeType &&
attributeType->is<AnyFunctionType>()) {
return diagnoseInvalid(repr, attrs.getLoc(attrKind), ...);
} else {
return diagnoseInvalid(...);
}
};
for (auto i : FunctionAttrs) {
if (!attrs.has(i))
continue;
Type optionalObjectType = ty->getOptionalObjectType();
if (i == TAK_escaping) {
diagnoseInvalidAttr(optionalObjectType, i)
.fixItRemove(
getTypeAttrRangeWithAt(Context, attrs.getLoc(TAK_escaping)));
}
attrs.clearAttribute(i);
}
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 my gosh, thanks a lot, now it looks way better
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.
@LucianoPAlmeida I'm not sure that it will work when i != TAK_escaping
, will 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.
Ahh, sure ... this was just a sketch real quick to show a possible approach :)
I think you right, it has to work for other function attrs the only applies to function types e.g. @autoclosure
test/attr/attr_escaping.swift
Outdated
@@ -72,15 +72,14 @@ struct GenericStruct<T> {} | |||
|
|||
func misuseEscaping(_ a: @escaping Int) {} // expected-error{{@escaping attribute only applies to function types}} {{26-36=}} | |||
func misuseEscaping(_ a: (@escaping Int)?) {} // expected-error{{@escaping attribute only applies to function types}} {{27-36=}} | |||
func misuseEscaping(opt a: @escaping ((Int) -> Int)?) {} // expected-error{{@escaping attribute only applies to function types}} {{28-38=}} | |||
// expected-note@-1{{closure is already escaping in optional type argument}} | |||
func misuseEscaping(opt a: @escaping ((Int) -> Int)?) {} // expected-error{{closure is already escaping in optional type argument}} {{28-38=}} | |||
|
|||
func misuseEscaping(_ a: (@escaping (Int) -> Int)?) {} // expected-error{{@escaping attribute may only be used in function parameter position}} {{27-36=}} |
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 believe in this call and all cases below it we wouldn't actually produce a @escaping attribute may only be used in function parameter position
diagnostic anymore.
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.
Unfortunately, we would. They produced from a different part of code, to be precise from 2387-2394 lines in TypeCheckType.cpp
I'm not sure that we should fix these cases too because it's correct to produce @escaping attribute may only be used in function parameter position
here cause escaping inside closure declaration and it's an error too
But I can fix this behavior 🧐
@xedin
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.
Let's try to fix all these cases because optional closure diagnostic supersedes the one about function type.
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.
No problems, I’ll give it a try 👍🏻
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's starting to shape up pretty well, thank you! Regarding clang-format
there is a git-clang-format in llvm-project
repo you can add to your PATH
which could be used to format files after you git add
them.
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.
Looks good!
@swift-ci please smoke test |
Thank you, @ismetanin! Please don't forget to resolve associated JIRA. |
This PR will add a new specialized error diagnostic when @escaping attribute is applied to an optional closure
Resolves SR-4637