Skip to content

Conversation

grynspan
Copy link
Contributor

@grynspan grynspan commented Apr 6, 2024

This change is similar to #332 except that the diagnostic is applied when a test or suite is declared inside a protocol rather than inside a non-final class.

Without this change, the compiler will fail to build a test target with such a test anyway, but the diagnostics produced will refer to the emitted macro expansion and are opaque. For example, given the declaration:

protocol P {
  @Test func f()
}

The compiler produces these diagnostics which don't really explain the problem very well:

🛑 'private' modifier cannot be used in protocols
🛑 Protocol methods must not have bodies
🛑 Type '$s12TestingTests1PP1f4TestfMp_37__🟠$test_container__function__funcf__fMu_' cannot be nested in protocol 'P'
🛑 Use of protocol 'P' as a type must be written 'any P'
🛑 Cannot find '$s12TestingTests1PP1f4TestfMp_7funcf__fMu0_' in scope

This is because the swift-testing macro target is unaware that the enclosing lexical context is a protocol, so it tries to emit code that would only make sense inside a concrete type. With this change, you instead get:

🛑 Attribute 'Test' cannot be applied to a function within protocol 'P'

I also took the liberty of improving the diagnostic emitted if a test/suite is declared inside a closure.

Note

As with #332, this change only takes effect when swift-syntax-600 is used.

Checklist:

  • Code and documentation should follow the style of the Style Guide.
  • If public symbols are renamed or modified, DocC references should be updated.

This change is similar to #332 except that the diagnostic is applied when a test
or suite is declared inside a protocol rather than inside a non-final class.

Without this change, the compiler will fail to build a test target with such a
test _anyway_, but the diagnostics produced will refer to the emitted macro
expansion and are opaque. For example, given the declaration:

```swift
protocol P {
  @test func f()
}
```

The compiler produces these diagnostics which don't really explain the problem
very well:

> 🛑 'private' modifier cannot be used in protocols
> 🛑 Protocol methods must not have bodies
> 🛑 Type '$s12TestingTests1PP1f4TestfMp_37__🟠$test_container__function__funcf__fMu_' cannot be nested in protocol 'P'
> 🛑 Use of protocol 'P' as a type must be written 'any P'
> 🛑 Cannot find '$s12TestingTests1PP1f4TestfMp_7funcf__fMu0_' in scope

This is because the swift-testing macro target is unaware that the enclosing
lexical context is a protocol, so it tries to emit code that would only make
sense inside a concrete type. With this change, you instead get:

> 🛑 Attribute 'Test' cannot be applied to a function within protocol 'P'

I also took the liberty of improving the diagnostic emitted if a test/suite is
declared inside a closure.

> [!NOTE]
> As with #332, this change only takes effect when swift-syntax-600 is used.
@grynspan grynspan added bug 🪲 Something isn't working swift-6 labels Apr 6, 2024
@grynspan grynspan self-assigned this Apr 6, 2024
@grynspan
Copy link
Contributor Author

grynspan commented Apr 6, 2024

@swift-ci please test

@grynspan grynspan merged commit 52a4565 into main Apr 6, 2024
@grynspan grynspan deleted the jgrynspan/disallow-tests-in-protocols branch April 6, 2024 16:09
grynspan added a commit that referenced this pull request Apr 6, 2024
This PR uses the swift-syntax-600 `lexicalContext` property to enforce the
compile-time requirement that tags declared with `@Tag` must be declared in an
extension to `Tag` or in a type declared inside `Tag`. Fix-its are provided for
a few scenarios. The new diagnostics are:

```swift
@tag var x: Tag // 🛑 Attribute 'Tag' cannot be applied to a global variable
                // Declare in an extension to 'Tag' [Fix]
                // Remove attribute 'Tag' [Fix]

extension String {
  @tag var x: Tag // 🛑 Attribute 'Tag' cannot be applied to a property except in an extension to 'Tag'
}

extension Tag {
  @tag var x: Tag // 🛑 Attribute 'Tag' cannot be applied to an instance property
                  // Add 'static' [Fix]
}

extension Tag {
  @tag var x: String // 🛑 Attribute 'Tag' cannot be applied to a property of type 'String'
                     // Change type to 'Tag' [Fix]
                     // Remove attribute 'Tag' [Fix]
}
```

As with #332 and #333, these problems are only diagnosed when using
swift-syntax-600.
grynspan added a commit that referenced this pull request Apr 8, 2024
This PR uses the swift-syntax-600 `lexicalContext` property to enforce
the compile-time requirement that tags declared with `@Tag` must be
declared in an extension to `Tag` or in a type declared inside `Tag`.
Fix-its are provided for a few scenarios. The new diagnostics are:

```swift
@tag var x: Tag // 🛑 Attribute 'Tag' cannot be applied to a global variable
                // Declare in an extension to 'Tag' [Fix]
                // Remove attribute 'Tag' [Fix]

extension String {
  @tag var x: Tag // 🛑 Attribute 'Tag' cannot be applied to a property except in an extension to 'Tag'
}

extension Tag {
  @tag var x: Tag // 🛑 Attribute 'Tag' cannot be applied to an instance property
                  // Add 'static' [Fix]
}

extension Tag {
  @tag var x: String // 🛑 Attribute 'Tag' cannot be applied to a property of type 'String'
                     // Change type to 'Tag' [Fix]
                     // Remove attribute 'Tag' [Fix]
}
```

As with #332 and #333, these problems are only diagnosed when using
swift-syntax-600.

### 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.
grynspan added a commit that referenced this pull request Apr 8, 2024
This PR applies all suite diagnostics\* to every decl group (declaring type or
extension) in the lexical context during `@Suite` and `@Test` expansion. For
example:

```swift
struct S1<T> {
  @suite struct S2 { // 🛑 Attribute 'Suite' cannot be applied to a generic structure
    @test func f() {} // 🛑 Attribute 'Test' cannot be applied to a generic function
  }
}
```

This PR is follow-up to #332 and #333.

\* Except for diagnostics specific to the `@Suite` attribute itself.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug 🪲 Something isn't working
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants