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

Rule to check for Swift 5-mode-incompatible API #5987

Open
2 tasks done
mattmassicotte opened this issue Feb 1, 2025 · 4 comments
Open
2 tasks done

Rule to check for Swift 5-mode-incompatible API #5987

mattmassicotte opened this issue Feb 1, 2025 · 4 comments
Labels
rule-request Requests for a new rules.

Comments

@mattmassicotte
Copy link

mattmassicotte commented Feb 1, 2025

New Issue Checklist

Feature or Enhancement Proposal

There are a number of ways to break compatibility with Swift 5 mode (strict concurrency off) that are difficult to notice. Here are some examples:

@MainActor
public class MainThreadOnly {}

public func takeBlock(_ block: @Sendable () - Void)
public func takeGlobalBlock(_ block: @MainActor () - Void)

In all of these cases, to preserve compatibility you must use @preconcurrency.

@preconcurrency @MainActor
public class MainThreadOnly {}

@preconcurrency 
public func takeBlock(_ block: @Sendable () - Void)

@preconcurrency 
public func takeGlobalBlock(_ block: @MainActor () - Void)

I think this could be a great new rule!

@SimplyDanny SimplyDanny added the rule-request Requests for a new rules. label Feb 1, 2025
@mattmassicotte
Copy link
Author

mattmassicotte commented Feb 1, 2025

Ok I'm trying to come up with examples. You have to catch public API that uses global actors, @Sendable closures, Sendable generic constraints, and the use of sending parameters (sending returns are not a problem).

@preconcurrency can address all of these, except for sending parameters for which there is no mechanism to preserve source compatibility.

non-triggering:

Example("public struct S: Sendable {}"),
Example("public class C: Sendable {}"),
Example("public actor A {}"),
Example("public @MyActor enum E { case a }"),
Example("private @MainActtor struct S { }"),
Example("@MainActtor struct S { }"),

triggering:

Example("↓@MainActor public struct S {}"),
Example("↓@MainActor public func globalActor()"),
Example("public func sendableClosure(_ block: ↓@Sendable () -> Void)"),
Example("public func globalActorClosure(_ block: ↓@MainActor () -> Void)"),
Example(
	"public func globalActorClosure(_ block: ↓@MyActor () -> Void)",
	configuration: ["global_actors": ["MyActor"]]
),
Example("public func sendingParameter(p: sending NonSendable)"),
Example("↓@MainActor public protocol GlobalActor {}"),
Example("public struct S { public func sendableClosure(_ block: ↓@Sendable () -> Void) }"),
Example("↓@MyActor public struct S {}", configuration: ["global_actors": ["MyActor"]]),
Example("public func generic<T> where T: ↓Sendable")

@SimplyDanny
Copy link
Collaborator

Thank you for the idea a the examples! What would be a good name for the new rule? Probably the hardest part ... 😅

@mattmassicotte
Copy link
Author

I started playing around with an implementation, and I used "IncompatibleConcurrencyAnnotationRule". But I rapidly realized that I don't have enough Swift syntax knowledge.

What do you think of that one?

@SimplyDanny
Copy link
Collaborator

With "incompatible" meaning "not back-deployable"? But that's probably too long and a too weird combination of words. I think "IncompatibleConcurrencyAnnotationRule" is good with an explanation of what it means in the description.

But I rapidly realized that I don't have enough Swift syntax knowledge.

It's a hurdle to get used to it. But with some experience it's fun to work with it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
rule-request Requests for a new rules.
Projects
None yet
Development

No branches or pull requests

2 participants