Incompatible Concurrency Annotation

Declaration should be @preconcurrency to maintain compatibility with Swift 5

  • Identifier: incompatible_concurrency_annotation
  • Enabled by default: No
  • Supports autocorrection: Yes
  • Kind: lint
  • Analyzer rule: No
  • Minimum Swift compiler version: 6.0.0
  • Default configuration:
    KeyValue
    severity warning
    global_actors [“MainActor”]

Rationale

Declarations that use concurrency features such as @Sendable closures, Sendable generic type arguments or @MainActor (or other global actors) should be annotated with @preconcurrency to ensure compatibility with Swift 5.

This rule detects public declarations that require @preconcurrency and can automatically add the annotation.

Non Triggering Examples

public struct S: Sendable {}
public class C: Sendable {}
public actor A {}
private @MainActor struct S { }
@MainActor struct S { }
internal @MainActor func globalActor()
private @MainActor init() {}
internal subscript(index: Int) -> String where String: Sendable { get }
@preconcurrency @MainActor public struct S {}
@preconcurrency @MainActor public class C {}
@preconcurrency @MainActor public enum E { case a }
@preconcurrency @MainActor public protocol P {}
@preconcurrency @MainActor public func globalActor()
@preconcurrency public func sendableClosure(_ block: @Sendable () -> Void)
@preconcurrency public func globalActorClosure(_ block: @MainActor () -> Void)
@preconcurrency public init(_ block: @Sendable () -> Void)
@preconcurrency public subscript(index: Int) -> String where String: Sendable { get }
@preconcurrency public func sendableReturningClosure() -> @Sendable () -> Void
@preconcurrency public func globalActorReturningClosure() -> @MainActor () -> Void
@preconcurrency public func sendingParameter(_ value: sending MyClass)
@preconcurrency public func tupleParameterClosures(
    _ handlers: (@Sendable () -> Void, @MainActor () -> Void)
)
@preconcurrency public func tupleReturningClosures() -> (
    @Sendable () -> Void,
    @MainActor () -> Void
)
@preconcurrency public func closureWithSendingArgument(
    _ handler: (_ value: sending MyClass) -> Void
)
public func nonSendableClosure(_ block: () -> Void)
public func generic<T>() where T: Equatable
public func generic<T: Hashable>()
public init<T: Hashable>()
public @MyActor enum E { case a }
public func customActor(_ block: @MyActor () -> Void)

Triggering Examples

@MainActor public struct S {}
@MainActor public class C {}
@MainActor public enum E { case a }
@MainActor public protocol GlobalActor {}
@MainActor public func globalActor()
class C {
    @MainActor public init() {}
}
@MainActor public init<T>()
struct S {
    @MainActor public subscript(index: Int) -> String { get }
}
public subscript<T>(index: T) -> Int where T: ExpressibleByIntegerLiteral & Sendable { get }
public func sendableClosure(_ block: @Sendable () -> Void)
public func globalActorClosure(_ block: @MainActor () -> Void)
public struct S { public func sendableClosure(_ block: @Sendable () -> Void) }
public init(_ block: @Sendable () -> Void)
public init(param: @MainActor () -> Void)
public func tupleParameter(
    _ handlers: (@Sendable () -> Void, @MainActor () -> Void)
)
public func tupleWithSending(
    _ handlers: ((_ value: sending MyClass) -> Void, @MainActor () -> Void)
)
public func generic<T>() where T: Sendable {}
public struct S<T> where T: Sendable {}
public class C<T> where T: Sendable {}
public enum E<T> where T: Sendable { case a }
public init<T>() where T: Sendable {}
public func returnsSendableClosure() -> @Sendable () -> Void
public func returnsActorClosure() -> @MainActor () -> Void
public func returnsClosureTuple() -> (@Sendable () -> Void, @MainActor () -> Void)
//
// global_actors: ["MainActor", "MyActor"]
//

@MyActor public struct S {}

//
// global_actors: ["MainActor", "MyActor"]
//

public func globalActorClosure(_ block: @MyActor () -> Void)

//
// global_actors: ["MainActor", "MyActor"]
//

@MyActor public func customGlobalActor()

//
// global_actors: ["MainActor", "MyActor"]
//

@MyActor public init()