File tree Expand file tree Collapse file tree 8 files changed +77
-0
lines changed
Tests/SwiftFormatTests/Rules Expand file tree Collapse file tree 8 files changed +77
-0
lines changed Original file line number Diff line number Diff line change @@ -189,6 +189,7 @@ Force-unwraps are strongly discouraged and must be documented.
189189
190190This rule does not apply to test code, defined as code which:
191191 * Contains the line ` import XCTest `
192+ * The function is marked with ` @Test ` attribute
192193
193194Lint: If a force unwrap is used, a lint warning is raised.
194195
@@ -200,6 +201,7 @@ Force-try (`try!`) is forbidden.
200201
201202This rule does not apply to test code, defined as code which:
202203 * Contains the line ` import XCTest `
204+ * The function is marked with ` @Test ` attribute
203205
204206Lint: Using ` try! ` results in a lint error.
205207
@@ -215,6 +217,7 @@ Certain properties (e.g. `@IBOutlet`) tied to the UI lifecycle are ignored.
215217
216218This rule does not apply to test code, defined as code which:
217219 * Contains the line ` import XCTest `
220+ * The function is marked with ` @Test ` attribute
218221
219222TODO: Create exceptions for other UI elements (ex: viewDidLoad)
220223
Original file line number Diff line number Diff line change @@ -149,6 +149,21 @@ extension SyntaxProtocol {
149149 }
150150 return leadingTrivia. hasAnyComments
151151 }
152+
153+ /// Indicates whether the node has any function ancestor marked with `@Test` attribute.
154+ var hasTestAncestor : Bool {
155+ var parent = self . parent
156+ while let existingParent = parent {
157+ if let functionDecl = existingParent. as ( FunctionDeclSyntax . self) ,
158+ functionDecl. attributes. contains ( where: {
159+ $0. as ( AttributeSyntax . self) ? . attributeName. as ( IdentifierTypeSyntax . self) ? . name. text == " Test "
160+ } ) {
161+ return true
162+ }
163+ parent = existingParent. parent
164+ }
165+ return false
166+ }
152167}
153168
154169extension SyntaxCollection {
Original file line number Diff line number Diff line change @@ -34,6 +34,8 @@ public final class NeverForceUnwrap: SyntaxLintRule {
3434
3535 public override func visit( _ node: ForceUnwrapExprSyntax ) -> SyntaxVisitorContinueKind {
3636 guard context. importsXCTest == . doesNotImportXCTest else { return . skipChildren }
37+ // Allow force unwrapping if it is in a function marked with @Test attribute.
38+ if node. hasTestAncestor { return . skipChildren }
3739 diagnose ( . doNotForceUnwrap( name: node. expression. trimmedDescription) , on: node)
3840 return . skipChildren
3941 }
@@ -44,6 +46,8 @@ public final class NeverForceUnwrap: SyntaxLintRule {
4446 guard context. importsXCTest == . doesNotImportXCTest else { return . skipChildren }
4547 guard let questionOrExclamation = node. questionOrExclamationMark else { return . skipChildren }
4648 guard questionOrExclamation. tokenKind == . exclamationMark else { return . skipChildren }
49+ // Allow force cast if it is in a function marked with @Test attribute.
50+ if node. hasTestAncestor { return . skipChildren }
4751 diagnose ( . doNotForceCast( name: node. type. trimmedDescription) , on: node)
4852 return . skipChildren
4953 }
Original file line number Diff line number Diff line change @@ -36,6 +36,8 @@ public final class NeverUseForceTry: SyntaxLintRule {
3636 public override func visit( _ node: TryExprSyntax ) -> SyntaxVisitorContinueKind {
3737 guard context. importsXCTest == . doesNotImportXCTest else { return . skipChildren }
3838 guard let mark = node. questionOrExclamationMark else { return . visitChildren }
39+ // Allow force try if it is in a function marked with @Test attribute.
40+ if node. hasTestAncestor { return . skipChildren }
3941 if mark. tokenKind == . exclamationMark {
4042 diagnose ( . doNotForceTry, on: node. tryKeyword)
4143 }
Original file line number Diff line number Diff line change @@ -39,6 +39,8 @@ public final class NeverUseImplicitlyUnwrappedOptionals: SyntaxLintRule {
3939
4040 public override func visit( _ node: VariableDeclSyntax ) -> SyntaxVisitorContinueKind {
4141 guard context. importsXCTest == . doesNotImportXCTest else { return . skipChildren }
42+ // Allow implicitly unwrapping if it is in a function marked with @Test attribute.
43+ if node. hasTestAncestor { return . skipChildren }
4244 // Ignores IBOutlet variables
4345 for attribute in node. attributes {
4446 if ( attribute. as ( AttributeSyntax . self) ) ? . attributeName. as ( IdentifierTypeSyntax . self) ? . name. text == " IBOutlet " {
Original file line number Diff line number Diff line change @@ -40,4 +40,23 @@ final class NeverForceUnwrapTests: LintOrFormatRuleTestCase {
4040 findings: [ ]
4141 )
4242 }
43+
44+ func testIgnoreTestAttributeFunction( ) {
45+ assertLint (
46+ NeverForceUnwrap . self,
47+ """
48+ @Test
49+ func testSomeFunc() {
50+ var b = a as! Int
51+ }
52+ @Test
53+ func testAnotherFunc() {
54+ func nestedFunc() {
55+ let c = someValue()!
56+ }
57+ }
58+ """ ,
59+ findings: [ ]
60+ )
61+ }
4362}
Original file line number Diff line number Diff line change @@ -38,4 +38,20 @@ final class NeverUseForceTryTests: LintOrFormatRuleTestCase {
3838 findings: [ ]
3939 )
4040 }
41+
42+ func testAllowForceTryInTestAttributeFunction( ) {
43+ assertLint (
44+ NeverUseForceTry . self,
45+ """
46+ @Test
47+ func testSomeFunc() {
48+ let document = try! Document(path: " important.data " )
49+ func nestedFunc() {
50+ let x = try! someThrowingFunction()
51+ }
52+ }
53+ """ ,
54+ findings: [ ]
55+ )
56+ }
4157}
Original file line number Diff line number Diff line change @@ -35,4 +35,20 @@ final class NeverUseImplicitlyUnwrappedOptionalsTests: LintOrFormatRuleTestCase
3535 findings: [ ]
3636 )
3737 }
38+
39+ func testIgnoreTestAttrinuteFunction( ) {
40+ assertLint (
41+ NeverUseImplicitlyUnwrappedOptionals . self,
42+ """
43+ @Test
44+ func testSomeFunc() {
45+ var s: String!
46+ func nestedFunc() {
47+ var f: Foo!
48+ }
49+ }
50+ """ ,
51+ findings: [ ]
52+ )
53+ }
3854}
You can’t perform that action at this time.
0 commit comments