1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
|
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
import SwiftSyntax
/// Enforces rules around parentheses in conditions or matched expressions.
///
/// Parentheses are not used around any condition of an `if`, `guard`, or `while` statement, or
/// around the matched expression in a `switch` statement.
///
/// Lint: If a top-most expression in a `switch`, `if`, `guard`, or `while` statement is surrounded
/// by parentheses, and it does not include a function call with a trailing closure, a lint
/// error is raised.
///
/// Format: Parentheses around such expressions are removed, if they do not cause a parse ambiguity.
/// Specifically, parentheses are allowed if and only if the expression contains a function
/// call with a trailing closure.
@_spi(Rules)
public final class NoParensAroundConditions: SyntaxFormatRule {
public override func visit(_ node: IfExprSyntax) -> ExprSyntax {
var result = node
fixKeywordTrailingTrivia(&result.ifKeyword.trailingTrivia)
result.conditions = visit(node.conditions)
result.body = visit(node.body)
if let elseBody = node.elseBody {
result.elseBody = visit(elseBody)
}
return ExprSyntax(result)
}
public override func visit(_ node: ConditionElementSyntax) -> ConditionElementSyntax {
guard
case .expression(let condition) = node.condition,
let newExpr = minimalSingleExpression(condition)
else {
return super.visit(node)
}
var result = node
result.condition = .expression(newExpr)
return result
}
public override func visit(_ node: GuardStmtSyntax) -> StmtSyntax {
var result = node
fixKeywordTrailingTrivia(&result.guardKeyword.trailingTrivia)
result.conditions = visit(node.conditions)
result.body = visit(node.body)
return StmtSyntax(result)
}
public override func visit(_ node: SwitchExprSyntax) -> ExprSyntax {
guard let newSubject = minimalSingleExpression(node.subject) else {
return super.visit(node)
}
var result = node
fixKeywordTrailingTrivia(&result.switchKeyword.trailingTrivia)
result.subject = newSubject
result.cases = visit(node.cases)
return ExprSyntax(result)
}
public override func visit(_ node: RepeatStmtSyntax) -> StmtSyntax {
guard let newCondition = minimalSingleExpression(node.condition) else {
return super.visit(node)
}
var result = node
fixKeywordTrailingTrivia(&result.whileKeyword.trailingTrivia)
result.condition = newCondition
result.body = visit(node.body)
return StmtSyntax(result)
}
public override func visit(_ node: WhileStmtSyntax) -> StmtSyntax {
var result = node
fixKeywordTrailingTrivia(&result.whileKeyword.trailingTrivia)
result.conditions = visit(node.conditions)
result.body = visit(node.body)
return StmtSyntax(result)
}
private func fixKeywordTrailingTrivia(_ trivia: inout Trivia) {
guard trivia.isEmpty else { return }
trivia = [.spaces(1)]
}
private func minimalSingleExpression(_ original: ExprSyntax) -> ExprSyntax? {
guard
let tuple = original.as(TupleExprSyntax.self),
tuple.elements.count == 1,
let expr = tuple.elements.first?.expression
else {
return nil
}
// If the condition is a function with a trailing closure or if it's an immediately called
// closure, removing the outer set of parentheses introduces a parse ambiguity.
if let fnCall = expr.as(FunctionCallExprSyntax.self) {
if fnCall.trailingClosure != nil {
// Leave parentheses around call with trailing closure.
return ExprSyntax(tuple)
} else if fnCall.calledExpression.as(ClosureExprSyntax.self) != nil {
// Leave parentheses around immediately called closure.
return ExprSyntax(tuple)
}
}
diagnose(.removeParensAroundExpression, on: tuple.leftParen)
var visitedExpr = visit(expr)
visitedExpr.leadingTrivia = tuple.leftParen.leadingTrivia
visitedExpr.trailingTrivia = tuple.rightParen.trailingTrivia
return visitedExpr
}
}
extension Finding.Message {
fileprivate static let removeParensAroundExpression: Finding.Message =
"remove the parentheses around this expression"
}
|