Skip to content

Introduce try/await/unsafe macro lexical contexts with unfolded sequence handling #3037

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 3 commits into from
Apr 4, 2025

Conversation

hamishknight
Copy link
Contributor

@hamishknight hamishknight commented Apr 2, 2025

This is #2724 with additional logic for handling try/await/unsafe in unfolded sequence expressions. We can say that any try/await/unsafe element also covers all elements to the right of it (matching the logic in swiftlang/swift#80461). Cases where this isn't true will be rejected by the compiler, e.g:

0 * try foo() + bar()
_ = try foo() ~~~ bar() // Assuming `~~~` has lower precedence than `=`

rdar://109470248

stmontgomery and others added 2 commits April 2, 2025 14:23
… expression macro in lexicalContext

Resolves rdar://109470248
We can say that any `try`/`await` element also covers all elements to
the right of it in an unfolded sequence. Cases where this isn't true
will be rejected by the compiler, e.g:

```
0 * try foo() + bar()
_ = try foo() ~~~ bar() // Assuming `~~~` has lower precedence than `=`
```

rdar://109470248
@hamishknight
Copy link
Contributor Author

@swift-ci please test

@hamishknight hamishknight requested a review from hborla April 2, 2025 16:21
Copy link
Member

@DougGregor DougGregor left a comment

Choose a reason for hiding this comment

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

I'd like to include unsafe expressions in the lexical context, but otherwise LGTM!

Comment on lines +121 to +131
if let tryElt = elt.as(TryExprSyntax.self) {
sequenceExprContexts.append(tryElt.asMacroLexicalContext()!)
elt = tryElt.expression
continue
}
if let awaitElt = elt.as(AwaitExprSyntax.self) {
sequenceExprContexts.append(awaitElt.asMacroLexicalContext()!)
elt = awaitElt.expression
continue
}
if let unsafeElt = elt.as(UnsafeExprSyntax.self) {
Copy link
Member

Choose a reason for hiding this comment

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

Not for this PR, but this makes me think we need a protocol for the "effect-like" expression nodes. I feel like this pattern is going to come up a bit.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Filed #3040

Copy link
Contributor

Choose a reason for hiding this comment

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

I requested that a while back, actually. :)

Copy link
Contributor

Choose a reason for hiding this comment

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

// No scope for this currently, but we need to look through it
// since it's similar to 'try' in that it's hoisted above a
// binary operator when appearing on the LHS.
elt = unsafeElt.expression
Copy link
Member

Choose a reason for hiding this comment

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

I would like us to include unsafe expressions in the lexical context as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated to include unsafe

Match the behavior of `try` and `await`.
@hamishknight
Copy link
Contributor Author

@swift-ci please test

@hamishknight
Copy link
Contributor Author

@swift-ci please test Windows

@hamishknight hamishknight merged commit 09ea403 into swiftlang:main Apr 4, 2025
26 checks passed
@hamishknight hamishknight deleted the in-sequence branch April 4, 2025 14:35
@hamishknight hamishknight changed the title Introduce try and await macro lexical contexts with unfolded sequence handling Introduce try/await/unsafe macro lexical contexts with unfolded sequence handling Apr 4, 2025
Comment on lines +123 to +126
// `sequenceExprContexts` is built from the top-down, but we
// build the rest of the contexts bottom-up. Reverse for
// consistency.
parentContexts += sequenceExprContexts.reversed()
Copy link
Member

Choose a reason for hiding this comment

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

Wouldn’t it make more sense to move this after the for loop? Just to make sure that we add elements to parentContext in case we should terminate the loop for some other reason than the break below.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Are there cases where we might not be able to determine which element the node is within? The main goal here is to only add the effects for nodes that occur after them in the sequence

Copy link
Member

Choose a reason for hiding this comment

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

No, I just thought that it would read nicer to have

            // `sequenceExprContexts` is built from the top-down, but we
            // build the rest of the contexts bottom-up. Reverse for
            // consistency.
            parentContexts += sequenceExprContexts.reversed()

after the for loop and only having a break in here because the addition of elements to parentContexts conceptually happens after iterating over the elements.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah okay, I don't really feel all that strongly about it, happy to change

stmontgomery added a commit to swiftlang/swift-testing that referenced this pull request Apr 24, 2025
…wait in recorded issues (#1092)

This fixes an issue where code comments placed before an expectation
like `#expect()` which has effect introducer keywords like `try` or
`await` are ignored, and ensures they are included in the recorded issue
if the expectation fails.

Consider this example of two failing expectations:

```swift
// Uh oh!
#expect(try x() == 2)

// Uh oh!
try #expect(x() == 2)
```

Prior to this PR, if `x()` returned a value other than `2`, there would
be two issues recorded, but the second one would not have the comment
`“Uh oh!”` because from the macro’s perspective, that code comment was
on the `try` keyword and it could only see trivia associated with
`#expect()`.

Now, with the recent swift-syntax fix from
swiftlang/swift-syntax#3037, the `try` keyword
and its associated trivia can be included and this bug can be fixed. We
recently adopted a new-enough swift-syntax in #1069, so the only fix
needed is to adopt `lexicalContext` for this new purpose in our macro.

### Checklist:

- [x] Code and documentation should follow the style of the [Style
Guide](https://github.com/apple/swift-testing/blob/main/Documentation/StyleGuide.md).
- [x] If public symbols are renamed or modified, DocC references should
be updated.
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.

5 participants