@@ -27,7 +27,7 @@ public final class NoAssignmentInExpressions: SyntaxFormatRule {
27
27
public override func visit( _ node: InfixOperatorExprSyntax ) -> ExprSyntax {
28
28
// Diagnose any assignment that isn't directly a child of a `CodeBlockItem` (which would be the
29
29
// case if it was its own statement).
30
- if isAssignmentExpression ( node) && node . parent ? . is ( CodeBlockItemSyntax . self ) == false {
30
+ if isAssignmentExpression ( node) && !isStandaloneAssignmentStatement ( node ) {
31
31
diagnose ( . moveAssignmentToOwnStatement, on: node)
32
32
}
33
33
return ExprSyntax ( node)
@@ -59,7 +59,8 @@ public final class NoAssignmentInExpressions: SyntaxFormatRule {
59
59
item: . expr( ExprSyntax ( assignmentExpr) ) ,
60
60
semicolon: nil
61
61
)
62
- . with ( \. leadingTrivia,
62
+ . with (
63
+ \. leadingTrivia,
63
64
( returnStmt. leadingTrivia) + ( assignmentExpr. leadingTrivia) )
64
65
. with ( \. trailingTrivia, [ ] ) )
65
66
newItems. append (
@@ -106,6 +107,30 @@ public final class NoAssignmentInExpressions: SyntaxFormatRule {
106
107
return context. operatorTable. infixOperator ( named: binaryOp. operatorToken. text) ? . precedenceGroup
107
108
== " AssignmentPrecedence "
108
109
}
110
+
111
+ /// Returns a value indicating whether the given node is a standalone assignment statement.
112
+ ///
113
+ /// This function considers try/await expressions and automatically walks up through them as
114
+ /// needed. This is because `try f().x = y` should still be a standalone assignment for our
115
+ /// purposes, even though a `TryExpr` will wrap the `InfixOperatorExpr` and thus would not be
116
+ /// considered a standalone assignment if we only checked the infix expression for a
117
+ /// `CodeBlockItem` parent.
118
+ private func isStandaloneAssignmentStatement( _ node: InfixOperatorExprSyntax ) -> Bool {
119
+ var node = Syntax ( node)
120
+ while
121
+ let parent = node. parent,
122
+ parent. is ( TryExprSyntax . self) || parent. is ( AwaitExprSyntax . self)
123
+ {
124
+ node = parent
125
+ }
126
+
127
+ guard let parent = node. parent else {
128
+ // This shouldn't happen under normal circumstances (i.e., unless the expression is detached
129
+ // from the rest of a tree). In that case, we may as well consider it to be "standalone".
130
+ return true
131
+ }
132
+ return parent. is ( CodeBlockItemSyntax . self)
133
+ }
109
134
}
110
135
111
136
extension Finding . Message {
0 commit comments