@@ -22,6 +22,25 @@ struct RedundantDiscardableLetRule: Rule {
22
22
return Text( " Hello, World! " )
23
23
}
24
24
""" , configuration: [ " ignore_swiftui_view_bodies " : true ] ) ,
25
+ Example ( """
26
+ @ViewBuilder
27
+ func bar() -> some View {
28
+ let _ = foo()
29
+ Text( " Hello, World! " )
30
+ }
31
+ """ , configuration: [ " ignore_swiftui_view_bodies " : true ] ) ,
32
+ Example ( """
33
+ #Preview {
34
+ let _ = foo()
35
+ Text( " Hello, World! " )
36
+ }
37
+ """ , configuration: [ " ignore_swiftui_view_bodies " : true ] ) ,
38
+ Example ( """
39
+ static var previews: some View {
40
+ let _ = foo()
41
+ Text( " Hello, World! " )
42
+ }
43
+ """ , configuration: [ " ignore_swiftui_view_bodies " : true ] ) ,
25
44
] ,
26
45
triggeringExamples: [
27
46
Example ( " ↓let _ = foo() " ) ,
@@ -32,6 +51,25 @@ struct RedundantDiscardableLetRule: Rule {
32
51
Text( " Hello, World! " )
33
52
}
34
53
""" ) ,
54
+ Example ( """
55
+ @ViewBuilder
56
+ func bar() -> some View {
57
+ let _ = foo()
58
+ return Text( " Hello, World! " )
59
+ }
60
+ """ ) ,
61
+ Example ( """
62
+ #Preview {
63
+ let _ = foo()
64
+ return Text( " Hello, World! " )
65
+ }
66
+ """ ) ,
67
+ Example ( """
68
+ static var previews: some View {
69
+ let _ = foo()
70
+ Text( " Hello, World! " )
71
+ }
72
+ """ ) ,
35
73
] ,
36
74
corrections: [
37
75
Example ( " ↓let _ = foo() " ) : Example ( " _ = foo() " ) ,
@@ -50,23 +88,32 @@ private extension RedundantDiscardableLetRule {
50
88
private var codeBlockScopes = Stack < CodeBlockKind > ( )
51
89
52
90
override func visit( _ node: AccessorBlockSyntax ) -> SyntaxVisitorContinueKind {
53
- codeBlockScopes. push ( node. isViewBody ? . view : . normal)
91
+ codeBlockScopes. push ( node. isViewBody || node . isPreviewProviderBody ? . view : . normal)
54
92
return . visitChildren
55
93
}
56
94
57
95
override func visitPost( _: AccessorBlockSyntax ) {
58
96
codeBlockScopes. pop ( )
59
97
}
60
98
61
- override func visit( _: CodeBlockSyntax ) -> SyntaxVisitorContinueKind {
62
- codeBlockScopes. push ( . normal)
99
+ override func visit( _ node : CodeBlockSyntax ) -> SyntaxVisitorContinueKind {
100
+ codeBlockScopes. push ( node . isViewBuilderFunctionBody ? . view : . normal)
63
101
return . visitChildren
64
102
}
65
103
66
104
override func visitPost( _: CodeBlockSyntax ) {
67
105
codeBlockScopes. pop ( )
68
106
}
69
107
108
+ override func visit( _ node: ClosureExprSyntax ) -> SyntaxVisitorContinueKind {
109
+ codeBlockScopes. push ( node. isPreviewMacroBody ? . view : . normal)
110
+ return . visitChildren
111
+ }
112
+
113
+ override func visitPost( _: ClosureExprSyntax ) {
114
+ codeBlockScopes. pop ( )
115
+ }
116
+
70
117
override func visitPost( _ node: VariableDeclSyntax ) {
71
118
if codeBlockScopes. peek ( ) != . view || !configuration. ignoreSwiftUIViewBodies,
72
119
node. bindingSpecifier. tokenKind == . keyword( . let) ,
@@ -100,4 +147,56 @@ private extension AccessorBlockSyntax {
100
147
}
101
148
return false
102
149
}
150
+
151
+ var isPreviewProviderBody : Bool {
152
+ guard let binding = parent? . as ( PatternBindingSyntax . self) ,
153
+ binding. pattern. as ( IdentifierPatternSyntax . self) ? . identifier. text == " previews " ,
154
+ let bindingList = binding. parent? . as ( PatternBindingListSyntax . self) ,
155
+ let variableDecl = bindingList. parent? . as ( VariableDeclSyntax . self) else {
156
+ return false
157
+ }
158
+
159
+ guard variableDecl. modifiers. contains ( keyword: . static) &&
160
+ variableDecl. bindingSpecifier. tokenKind == . keyword( . var) else {
161
+ return false
162
+ }
163
+
164
+ if let type = binding. typeAnnotation? . type. as ( SomeOrAnyTypeSyntax . self) {
165
+ return type. someOrAnySpecifier. text == " some " &&
166
+ type. constraint. as ( IdentifierTypeSyntax . self) ? . name. text == " View "
167
+ }
168
+
169
+ return false
170
+ }
171
+ }
172
+
173
+ private extension CodeBlockSyntax {
174
+ var isViewBuilderFunctionBody : Bool {
175
+ parent? . as ( FunctionDeclSyntax . self) ? . isViewBuilderFunction == true
176
+ }
177
+ }
178
+
179
+ private extension FunctionDeclSyntax {
180
+ var isViewBuilderFunction : Bool {
181
+ guard attributes. contains ( attributeNamed: " ViewBuilder " ) else {
182
+ return false
183
+ }
184
+
185
+ guard let returnType = signature. returnClause? . type. as ( SomeOrAnyTypeSyntax . self) else {
186
+ return false
187
+ }
188
+
189
+ return returnType. someOrAnySpecifier. text == " some " &&
190
+ returnType. constraint. as ( IdentifierTypeSyntax . self) ? . name. text == " View "
191
+ }
192
+ }
193
+
194
+ private extension ClosureExprSyntax {
195
+ var isPreviewMacroBody : Bool {
196
+ if let macroExpansionExpr = parent? . as ( MacroExpansionExprSyntax . self) ,
197
+ macroExpansionExpr. macroName. text == " Preview " {
198
+ return true
199
+ }
200
+ return false
201
+ }
103
202
}
0 commit comments