Skip to content
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

Merged
merged 4 commits into from Apr 6, 2020
Merged

Add new error diagnostic for escaping optional closures #30746

merged 4 commits into from Apr 6, 2020

Conversation

ismetanin
Copy link
Contributor

This PR will add a new specialized error diagnostic when @escaping attribute is applied to an optional closure

Resolves SR-4637

@theblixguy theblixguy requested a review from xedin April 1, 2020 08:48
test/attr/attr_escaping.swift Outdated Show resolved Hide resolved
@@ -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)));
Copy link
Collaborator

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!

Copy link
Collaborator

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

Copy link
Contributor Author

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)

Copy link
Collaborator

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.

Copy link
Contributor Author

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

Copy link
Collaborator

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!

Copy link
Collaborator

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?

Copy link
Collaborator

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

Copy link
Contributor Author

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
diag.flush();
diagnoseInvalid(repr, repr->getLoc(),
diag::escaping_optional_type_argument);
if (i == TAK_escaping && ty->getOptionalObjectType()) {
Copy link
Member

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.

Copy link
Contributor Author

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

Copy link
Collaborator

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.

Copy link
Contributor Author

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?

Copy link
Collaborator

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(...);
};

Copy link
Collaborator

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);
}

Copy link
Contributor Author

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

Copy link
Contributor Author

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?

Copy link
Collaborator

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

@@ -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=}}
Copy link
Member

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.

Copy link
Contributor Author

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

Copy link
Member

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.

Copy link
Contributor Author

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 👍🏻

Copy link
Member

@xedin xedin left a 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.

Copy link
Member

@xedin xedin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good!

@xedin
Copy link
Member

xedin commented Apr 6, 2020

@swift-ci please smoke test

@xedin xedin merged commit cbf3cab into apple:master Apr 6, 2020
@xedin
Copy link
Member

xedin commented Apr 6, 2020

Thank you, @ismetanin! Please don't forget to resolve associated JIRA.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants