From 54d9a6f509c6835186c530f7c5a07a2b029e6015 Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Wed, 1 Apr 2026 15:58:22 +0100 Subject: [PATCH] [Source Warning Control] Rename '@warn' to '@diagnose' Resolves rdar://173774670 --- .../SwiftWarningControl.md | 14 +-- .../SyntaxProtocol+WarningControl.swift | 2 +- .../WarningControlDeclSyntax.swift | 7 +- .../WarningControlRegionBuilder.swift | 2 +- .../WarningControlRegions.swift | 16 +-- Tests/SwiftParserTest/DeclarationTests.swift | 33 ++++++ .../WarningControlTests.swift | 109 +++++++++++------- 7 files changed, 119 insertions(+), 64 deletions(-) diff --git a/Sources/SwiftWarningControl/SwiftWarningControl.md b/Sources/SwiftWarningControl/SwiftWarningControl.md index 9d6a78f0045..8138c218382 100644 --- a/Sources/SwiftWarningControl/SwiftWarningControl.md +++ b/Sources/SwiftWarningControl/SwiftWarningControl.md @@ -1,21 +1,21 @@ # SwiftWarningControl -A library to evaluate `@warn` diagnostic group controls within a Swift syntax tree. +A library to evaluate `@diagnose` diagnostic group controls within a Swift syntax tree. ## Overview -Swift provides a mechanism to control the behavior of specific diagnostic groups for a given declaration's lexical scope with the `@warn` attribute. +Swift provides a mechanism to control the behavior of specific diagnostic groups for a given declaration's lexical scope with the `@diagnose` attribute (also accepts the old `@warn` spelling). -The syntax tree and its parser do not reason about warning group controls. The syntax tree produced by the parser represents the `@warn` attribute in a generic fashion, as it would any other basic attribute on a declaration. The per-declaration nature of the attribute means that for any given lexical scope, the behavior of a given diagnostic group can be queried by checking for the presence of this attribute in its parent declaration scope. +The syntax tree and its parser do not reason about warning group controls. The syntax tree produced by the parser represents the `@diagnose` attribute in a generic fashion, as it would any other basic attribute on a declaration. The per-declaration nature of the attribute means that for any given lexical scope, the behavior of a given diagnostic group can be queried by checking for the presence of this attribute in its parent declaration scope. ```swift -@warn(Deprecate, as: error) +@diagnose(Deprecate, as: error) func foo() { ... - @warn(Deprecate, as: warning) + @diagnose(Deprecate, as: warning) func bar() { ... - @warn("Deprecate", as: ignored, reason: "Foo") + @diagnose("Deprecate", as: ignored, reason: "Foo") func baz() { ... } @@ -23,7 +23,7 @@ func foo() { } ``` -The `SwiftWarningControl` library provides a utility to determine, for a given source location and diagnostic group identifier, whether or not its behavior is affected by an `@warn` attribute of any of its parent declaration scope. +The `SwiftWarningControl` library provides a utility to determine, for a given source location and diagnostic group identifier, whether or not its behavior is affected by an `@diagnose` attribute of any of its parent declaration scope. * `SyntaxProtocol.getWarningGroupControl(for diagnosticGroupIdentifier:)` produces the behavior control specifier (`WarningGroupControl`: `error`, `warning`, `ignored`) which applies at this node. diff --git a/Sources/SwiftWarningControl/SyntaxProtocol+WarningControl.swift b/Sources/SwiftWarningControl/SyntaxProtocol+WarningControl.swift index c3d8a4fd305..461fe2718c4 100644 --- a/Sources/SwiftWarningControl/SyntaxProtocol+WarningControl.swift +++ b/Sources/SwiftWarningControl/SyntaxProtocol+WarningControl.swift @@ -23,7 +23,7 @@ extension SyntaxProtocol { /// - globalControls: The global controls to consider, specified by the client (compiler) /// representing module-wide diagnostic group emission configuration, for example /// with `-Wwarning` and `-Werror` flags. These controls can be overriden at - /// finer-grained scopes with the `@warn` attribute. + /// finer-grained scopes with the `@diagnose` attribute. @_spi(ExperimentalLanguageFeatures) public func warningGroupControl( for diagnosticGroupIdentifier: DiagnosticGroupIdentifier, diff --git a/Sources/SwiftWarningControl/WarningControlDeclSyntax.swift b/Sources/SwiftWarningControl/WarningControlDeclSyntax.swift index 49c90a3d155..a238c80bb99 100644 --- a/Sources/SwiftWarningControl/WarningControlDeclSyntax.swift +++ b/Sources/SwiftWarningControl/WarningControlDeclSyntax.swift @@ -14,8 +14,9 @@ extension AttributeSyntax { var warningGroupControl: (DiagnosticGroupIdentifier, WarningGroupControl)? { - // `@warn` attributes - guard attributeName.as(IdentifierTypeSyntax.self)?.name.text == "warn" + // `@diagnose` attributes (also accepts the old `@warn` spelling) + guard let attrName = attributeName.as(IdentifierTypeSyntax.self)?.name.text, + attrName == "diagnose" || attrName == "warn" else { return nil } @@ -51,7 +52,7 @@ extension AttributeSyntax { } extension WithAttributesSyntax { - /// Compute a dictionary of all `@warn` diagnostic group behavior controls + /// Compute a dictionary of all `@diagnose` diagnostic group behavior controls /// specified on this warning control declaration scope. var allWarningGroupControls: [(DiagnosticGroupIdentifier, WarningGroupControl)] { attributes.reduce(into: [(DiagnosticGroupIdentifier, WarningGroupControl)]()) { result, attr in diff --git a/Sources/SwiftWarningControl/WarningControlRegionBuilder.swift b/Sources/SwiftWarningControl/WarningControlRegionBuilder.swift index c62ecdfeafc..af49e2dcd92 100644 --- a/Sources/SwiftWarningControl/WarningControlRegionBuilder.swift +++ b/Sources/SwiftWarningControl/WarningControlRegionBuilder.swift @@ -45,7 +45,7 @@ extension SyntaxProtocol { } } -/// Add this warning control decl syntax node warning group controls (as specified with `@warn`) +/// Add this warning control decl syntax node warning group controls (as specified with `@diagnose`) /// to the tree. extension WarningControlRegionTree { mutating func addWarningControlRegions(for syntax: some WithAttributesSyntax) { diff --git a/Sources/SwiftWarningControl/WarningControlRegions.swift b/Sources/SwiftWarningControl/WarningControlRegions.swift index ddf2f6da25c..be2975cc67a 100644 --- a/Sources/SwiftWarningControl/WarningControlRegions.swift +++ b/Sources/SwiftWarningControl/WarningControlRegions.swift @@ -43,27 +43,27 @@ public struct DiagnosticGroupIdentifier: Hashable, Sendable, ExpressibleByString public let identifier: String } -/// Describes all of the `@warn` diagnostic group behavior controls within the +/// Describes all of the `@diagnose` diagnostic group behavior controls within the /// given syntax node, indicating each group's active behavior at a given position. /// /// For example, given code like the following: /// /// ``` -/// 1 | @warn(Deprecate, as: error) +/// 1 | @diagnose(Deprecate, as: error) /// 2 | func foo() { /// 3 | let a = dep -/// 4 | @warn(Deprecate, as: warning) +/// 4 | @diagnose(Deprecate, as: warning) /// 5 | func bar() { /// 6 | let b = dep -/// 7 | @warn(Deprecate, as: ignored) +/// 7 | @diagnose(Deprecate, as: ignored) /// 8 | func baz() { /// 9 | let c = dep /// 10 | } -/// 11 | @warn(Deprecate, as: error) -/// 12 | @warn(OtherGroup, as: error) +/// 11 | @diagnose(Deprecate, as: error) +/// 12 | @diagnose(OtherGroup, as: error) /// 13 | func qux() { /// 14 | let d = dep -/// 15 | @warn(SomeOtherGroup, as: warning) +/// 15 | @diagnose(SomeOtherGroup, as: warning) /// 16 | func corge() { /// 17 | let e = dep /// 18 | } @@ -150,7 +150,7 @@ public struct WarningControlRegionTree { } /// Add warning control regions to the tree root node. - /// For example, controls corresponding to a top-level `using @warn()` statement. + /// For example, controls corresponding to a top-level `using @diagnose()` statement. mutating func addRootWarningGroupControls(controls: [(DiagnosticGroupIdentifier, WarningGroupControl)]) { addWarningGroupControls(range: rootRegionNode.range, controls: controls) } diff --git a/Tests/SwiftParserTest/DeclarationTests.swift b/Tests/SwiftParserTest/DeclarationTests.swift index 8b0fd83e221..4e782b211ca 100644 --- a/Tests/SwiftParserTest/DeclarationTests.swift +++ b/Tests/SwiftParserTest/DeclarationTests.swift @@ -3757,6 +3757,39 @@ final class UsingDeclarationTests: ParserTestCase { ) ) + assertParse( + "using @diagnose(DiagGroupID, as: error)", + substructure: UsingDeclSyntax( + usingKeyword: .keyword(.using), + specifier: .attribute( + AttributeSyntax( + attributeName: IdentifierTypeSyntax( + name: .identifier("diagnose") + ), + leftParen: .leftParenToken(), + arguments: .argumentList( + LabeledExprListSyntax([ + LabeledExprSyntax( + expression: DeclReferenceExprSyntax( + baseName: .identifier("DiagGroupID") + ), + trailingComma: .commaToken() + ), + LabeledExprSyntax( + label: .identifier("as"), + colon: .colonToken(), + expression: DeclReferenceExprSyntax( + baseName: .identifier("error") + ) + ), + ]) + ), + rightParen: .rightParenToken() + ) + ) + ) + ) + assertParse( """ nonisolated diff --git a/Tests/SwiftWarningControlTest/WarningControlTests.swift b/Tests/SwiftWarningControlTest/WarningControlTests.swift index 16e82d66d95..fc0fb0b3f68 100644 --- a/Tests/SwiftWarningControlTest/WarningControlTests.swift +++ b/Tests/SwiftWarningControlTest/WarningControlTests.swift @@ -21,7 +21,7 @@ public class WarningGroupControlTests: XCTestCase { func testSimpleFunctionWarningGroupControl() throws { try assertWarningGroupControl( """ - @warn(GroupID, as: error) + @diagnose(GroupID, as: error) func foo() { 1️⃣let x = 1 } @@ -36,23 +36,23 @@ public class WarningGroupControlTests: XCTestCase { func testNestedFunctionWarningGroupControl() throws { try assertWarningGroupControl( """ - @warn(SomeOtherGroup, as: warning) - @warn(GroupID, as: error) - @warn(YetAnotherGroup, as: warning) + @diagnose(SomeOtherGroup, as: warning) + @diagnose(GroupID, as: error) + @diagnose(YetAnotherGroup, as: warning) func foo() { 1️⃣let x = 1 - @warn(GroupID, as: warning) + @diagnose(GroupID, as: warning) func bar() { 2️⃣let x = 1 - @warn(GroupID, as: ignored) - @warn(SomeOtherGroup, as: ignored) + @diagnose(GroupID, as: ignored) + @diagnose(SomeOtherGroup, as: ignored) func baz() { 3️⃣let x = 1 } - @warn(GroupID, as: error) + @diagnose(GroupID, as: error) func qux() { 4️⃣let x = 1 - @warn(SomeOtherGroup, as: warning) + @diagnose(SomeOtherGroup, as: warning) func corge() { 5️⃣let x = 1 } @@ -78,18 +78,18 @@ public class WarningGroupControlTests: XCTestCase { func testEnabledGroupIdentifiers() throws { let source = """ - @warn(Group1, as: warning) - @warn(Group2, as: error) - @warn(Group3, as: ignored) + @diagnose(Group1, as: warning) + @diagnose(Group2, as: error) + @diagnose(Group3, as: ignored) func foo() { - @warn(Group4, as: warning) + @diagnose(Group4, as: warning) func bar() { - @warn(Group5, as: ignored) - @warn(Group6, as: ignored) + @diagnose(Group5, as: ignored) + @diagnose(Group6, as: ignored) func baz() {} - @warn(Group7, as: error) + @diagnose(Group7, as: error) func qux() { - @warn(Group8, as: warning) + @diagnose(Group8, as: warning) func corge() {} } } @@ -112,27 +112,27 @@ public class WarningGroupControlTests: XCTestCase { func testNominalDeclWarningGroupControl() throws { try assertWarningGroupControl( """ - @warn(GroupID, as: error) + @diagnose(GroupID, as: error) class Foo { 1️⃣let x = 1 } - @warn(GroupID, as: ignored) + @diagnose(GroupID, as: ignored) struct Bar { 2️⃣let x = 1 } - @warn(GroupID, as: warning) + @diagnose(GroupID, as: warning) enum Baz { 3️⃣let x = 1 } - @warn(GroupID, as: error) + @diagnose(GroupID, as: error) actor Qux { 4️⃣let x = 1 - @warn(GroupID, as: ignored) + @diagnose(GroupID, as: ignored) struct Quux { 5️⃣let x = 1 } } - @warn(GroupID, as: warning) + @diagnose(GroupID, as: warning) protocol Proto { 6️⃣let x = 1 } @@ -152,14 +152,14 @@ public class WarningGroupControlTests: XCTestCase { func testInitializerWarningGroupControl() throws { try assertWarningGroupControl( """ - @warn(GroupID, as: error) + @diagnose(GroupID, as: error) struct Foo { 1️⃣let x = 1 - @warn(GroupID, as: ignored) + @diagnose(GroupID, as: ignored) init { 2️⃣let x = 1 } - @warn(GroupID, as: warning) + @diagnose(GroupID, as: warning) deinit { 3️⃣let x = 1 } @@ -177,7 +177,7 @@ public class WarningGroupControlTests: XCTestCase { func testExtensionWarningGroupControl() throws { try assertWarningGroupControl( """ - @warn(GroupID, as: error) + @diagnose(GroupID, as: error) extension Foo { 1️⃣let x = 1 } @@ -192,7 +192,7 @@ public class WarningGroupControlTests: XCTestCase { func testImportWarningGroupControl() throws { try assertWarningGroupControl( """ - @warn(GroupID, as: ignored) + @diagnose(GroupID, as: ignored) import1️⃣ Foo """, diagnosticGroupID: "GroupID", @@ -205,9 +205,9 @@ public class WarningGroupControlTests: XCTestCase { func testSubscriptWarningGroupControl() throws { try assertWarningGroupControl( """ - @warn(GroupID, as: ignored) + @diagnose(GroupID, as: ignored) struct Foo { - @warn(GroupID, as: error) + @diagnose(GroupID, as: error) subscript(index: Int) -> Value { 1️⃣let x = 1 } @@ -223,9 +223,9 @@ public class WarningGroupControlTests: XCTestCase { func testComputedPropertyWarningGroupControl() throws { try assertWarningGroupControl( """ - @warn(GroupID, as: ignored) + @diagnose(GroupID, as: ignored) struct Foo { - @warn(GroupID, as: error) + @diagnose(GroupID, as: error) var property: Int { 1️⃣return 11 } @@ -241,14 +241,14 @@ public class WarningGroupControlTests: XCTestCase { func testAccessorWarningGroupControl() throws { try assertWarningGroupControl( """ - @warn(GroupID, as: ignored) + @diagnose(GroupID, as: ignored) struct Foo { var property: Int { - @warn(GroupID, as: error) + @diagnose(GroupID, as: error) get { 1️⃣return 11 } - @warn(GroupID, as: warning) + @diagnose(GroupID, as: warning) set { 2️⃣let x = 1 } @@ -267,7 +267,7 @@ public class WarningGroupControlTests: XCTestCase { // Global control does not override syntactic control try assertWarningGroupControl( """ - @warn(GroupID, as: error) + @diagnose(GroupID, as: error) func foo() { 1️⃣let x = 1 } @@ -283,7 +283,7 @@ public class WarningGroupControlTests: XCTestCase { """ func foo() { 1️⃣let x = 1 - @warn(GroupID, as: ignored) + @diagnose(GroupID, as: ignored) func bar() { 2️⃣let x = 1 } @@ -317,11 +317,11 @@ public class WarningGroupControlTests: XCTestCase { func testSubGroupInheritance() throws { try assertWarningGroupControl( """ - @warn(SuperGroupID, as: error) + @diagnose(SuperGroupID, as: error) func foo() { 1️⃣let x = 1 } - @warn(SuperSuperGroupID, as: ignored) + @diagnose(SuperSuperGroupID, as: ignored) func bar() { 2️⃣let x = 1 } @@ -392,17 +392,17 @@ public class WarningGroupControlTests: XCTestCase { try assertWarningGroupControl( """ 0️⃣let x = 1 - @warn(GroupID, as: warning) + @diagnose(GroupID, as: warning) struct Bar { 1️⃣let y = 1 } struct Foo { - @warn(GroupID, as: ignored) + @diagnose(GroupID, as: ignored) var property: Int { 2️⃣return 11 } } - using @warn(GroupID, as: error) + using @diagnose(GroupID, as: error) 3️⃣let k = 1 """, experimentalFeatures: [.defaultIsolationPerFile], @@ -420,12 +420,12 @@ public class WarningGroupControlTests: XCTestCase { try assertWarningGroupControl( """ 0️⃣let x = 1 - @warn(GroupID, as: warning) + @diagnose(GroupID, as: warning) struct Bar { 1️⃣let y = 1 } struct Foo { - using @warn(GroupID, as: error) + using @diagnose(GroupID, as: error) var property: Int { 2️⃣return 11 } @@ -441,6 +441,27 @@ public class WarningGroupControlTests: XCTestCase { ] ) } + + func testWarnSpellingBackwardCompat() throws { + // The old @warn spelling should continue to work as an alias + try assertWarningGroupControl( + """ + @warn(GroupID, as: error) + func foo() { + 1️⃣let x = 1 + @warn(GroupID, as: ignored) + func bar() { + 2️⃣let x = 1 + } + } + """, + diagnosticGroupID: "GroupID", + states: [ + "1️⃣": .error, + "2️⃣": .ignored, + ] + ) + } } /// Assert that the various marked positions in the source code have the