Skip to content

[Typechecker] Fix fix-it location for missing try when called on optional protocol value #26606

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

Merged
merged 6 commits into from
Aug 16, 2019
Merged

Conversation

theblixguy
Copy link
Collaborator

@theblixguy theblixguy commented Aug 11, 2019

When we call a throwing method on an optional protocol-typed value and forget to add try/try?/try!, then we emit the fix-it at the wrong location. This is because the source locations for OptionalEvaluationExpr points to the throwing method rather than the outer value. We also have some additional expressions, such as an OpenExistentialExpr, which wraps the CallExpr of the throwing method and holds the correct source location for the fix-it.

Example:

protocol Foo {
  func bar() throws
}

class HasFoo {
  var foo: Foo?

  func someMethod() throws {
    foo?.bar()
       ^ this is where we currently emit the fix-it

    foo?.bar()
   ^ this is where we should emit the fix-it
  }
}

Resolves SR-11016.

@theblixguy
Copy link
Collaborator Author

theblixguy commented Aug 11, 2019

cc @xedin

@theblixguy theblixguy changed the title [Typechecker] Fix fix-it location for missing try when called on protocol value [Typechecker] Fix fix-it location for missing try when called on optional protocol value Aug 12, 2019
@hamishknight
Copy link
Contributor

hamishknight commented Aug 12, 2019

Would it be possible to instead store a SourceRange on OpaqueValueExpr which, for cases where it represents some underlying expression, would be that expression's source range? That way I believe we'd be able to preserve the correct source range for the apply here without having to look at the OpenExistentialExpr.

@theblixguy
Copy link
Collaborator Author

theblixguy commented Aug 12, 2019

I think that could work. I believe at the moment we're passing the OpenExistentialExpr (or whatever the base expression is) location to the OpaqueValueExpr in all the places.

open_existential_expr implicit type='()' location=/Users/suyashsrijan/Desktop/test.swift:9:10 range=[/Users/suyashsrijan/Desktop/test.swift:9:8 - line:9:14]
   (opaque_value_expr implicit type='SR_11016_P' location=/Users/suyashsrijan/Desktop/test.swift:9:8 range=[/Users/suyashsrijan/Desktop/test.swift:9:8 - line:9:8] @ 0x7f92280db318)
   (load_expr implicit type='SR_11016_P' location=/Users/suyashsrijan/Desktop/test.swift:9:8 range=[/Users/suyashsrijan/Desktop/test.swift:9:5 - line:9:8]
      (bind_optional_expr type='@lvalue SR_11016_P' location=/Users/suyashsrijan/Desktop/test.swift:9:8 range=[/Users/suyashsrijan/Desktop/test.swift:9:5 - line:9:8] depth=0
      (member_ref_expr type='@lvalue SR_11016_P?' location=/Users/suyashsrijan/Desktop/test.swift:9:5 range=[/Users/suyashsrijan/Desktop/test.swift:9:5 - line:9:5] decl=test

This is what the AST looks like. The same OpaqueValueExpr appears later inside the DotSyntaxCallExpr as well:

(call_expr type='()' location=/Users/suyashsrijan/Desktop/test.swift:9:10 range=[/Users/suyashsrijan/Desktop/test.swift:9:8 - line:9:14] throws arg_labels=
  (dot_syntax_call_expr type='() throws -> ()' location=/Users/suyashsrijan/Desktop/test.swift:9:10 range=[/Users/suyashsrijan/Desktop/test.swift:9:8 - line:9:10] nothrow
     (declref_expr type='(SR_11016_P) -> () throws -> ()' location=/Users/suyashsrijan/Desktop/test.swift:9:10 range=[/Users/suyashsrijan/Desktop/test.swift:9:10 - line:9:10] decl=test.(file).SR_11016_P.bar()@/Users/suyashsrijan/Desktop/test.swift:2:8 [with (substitution_map generic_signature=<Self where Self : SR_11016_P> (substitution Self -> SR_11016_P))] function_ref=single)
     (opaque_value_expr implicit type='SR_11016_P' location=/Users/suyashsrijan/Desktop/test.swift:9:8 range=[/Users/suyashsrijan/Desktop/test.swift:9:8 - line:9:8] @ 0x7f92280db318))

If the OpaqueValueExpr location was the same as the LoadExpr then we could access that when looking at the apply. I don't think we're actually using its location anywhere, so it shouldn't break anything if we make that change.

@hamishknight
Copy link
Contributor

hamishknight commented Aug 12, 2019

@theblixguy Yeah, currently OpaqueValueExpr just stores a single SourceLoc, meaning that its source range can only cover a single token. For an open existential like this, that location is the base's getLoc() (in openExistentialReference I believe). For a bind-optional base such as foo?, that token is the ?, so the resulting source range doesn't cover the foo.

If OpaqueValueExpr used the actual source range of foo?, then I believe both the DotSyntaxCallExpr and CallExpr source ranges would both start at the correct place. This change does have the potential to affect other code, but given we're now using more accurate source range info, I don't believe it should cause any regressions.

@theblixguy
Copy link
Collaborator Author

theblixguy commented Aug 12, 2019

Sounds good. This works for my test case. @xedin Does the change look good to you? I haven't changed it so it stores the entire SourceRange, but I can add that if needed.

@xedin xedin self-requested a review August 12, 2019 21:33
@theblixguy
Copy link
Collaborator Author

ping @xedin did you had a chance to take a look at this? Thanks!

@xedin
Copy link
Contributor

xedin commented Aug 15, 2019

Thanks for the reminder! This is in my queue, I'll try to take a look ASAP.

Copy link
Contributor

@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 great!

@xedin
Copy link
Contributor

xedin commented Aug 16, 2019

@swift-ci please smoke test

@xedin xedin merged commit b26272f into swiftlang:master Aug 16, 2019
@theblixguy theblixguy deleted the fix/SR-11016 branch August 16, 2019 19:19
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.

3 participants