Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@
[nandhinisubbu](https://github.com/nandhinisubbu)
[#6304](https://github.com/realm/SwiftLint/issues/6304)

* Update `implicit_return` rule to trigger violation for if/switch statements with
single return expression in all branches.
[nandhinisubbu](https://github.com/nandhinisubbu)
[#6167](https://github.com/realm/SwiftLint/issues/6167)

### Bug Fixes

* Ignore function, initializer and subscript declarations alike when the
Expand Down
8 changes: 4 additions & 4 deletions Source/SwiftLintBuiltInRules/Models/ImportUsage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,19 @@ enum ImportUsage {
var violationRange: NSRange? {
switch self {
case .unused(_, let range):
return range
range
case .missing:
return nil
nil
}
}

/// The reason why this import usage is a violation.
var violationReason: String? {
switch self {
case .unused:
return nil
nil
case .missing(let module):
return "Missing import for referenced module '\(module)'"
"Missing import for referenced module '\(module)'"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -179,9 +179,9 @@ private extension ExtensionAccessModifierRule {
static func from(tokenKind: TokenKind?) -> Self {
switch tokenKind {
case nil:
return .implicit
.implicit
case let value?:
return .explicit(value)
.explicit(value)
}
}

Expand Down Expand Up @@ -238,12 +238,12 @@ private extension MemberBlockSyntax {
func expandingIfConfigs() -> [DeclSyntax] {
members.flatMap { member in
if let ifConfig = member.decl.as(IfConfigDeclSyntax.self) {
return ifConfig.clauses.flatMap { clause in
return ifConfig.clauses.flatMap { clause -> [DeclSyntax] in
switch clause.elements {
case .decls(let decls):
return decls.map(\.decl)
decls.map(\.decl)
default:
return []
[]
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,11 @@ private extension ImplicitlyUnwrappedOptionalRule {
override func visit(_ node: VariableDeclSyntax) -> SyntaxVisitorContinueKind {
switch configuration.mode {
case .all:
return .visitChildren
.visitChildren
case .allExceptIBOutlets:
return node.isIBOutlet ? .skipChildren : .visitChildren
node.isIBOutlet ? .skipChildren : .visitChildren
case .weakExceptIBOutlets:
return (node.isIBOutlet || node.weakOrUnownedModifier == nil) ? .skipChildren : .visitChildren
(node.isIBOutlet || node.weakOrUnownedModifier == nil) ? .skipChildren : .visitChildren
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,9 @@ private extension FunctionDeclSyntax {
var isOperator: Bool {
switch name.tokenKind {
case .binaryOperator:
return true
true
default:
return false
false
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,20 +61,20 @@ private enum SugaredType: String {
var sugaredExample: String {
switch self {
case .optional:
return "Int?"
"Int?"
case .array:
return "[Int]"
"[Int]"
case .dictionary:
return "[String: Int]"
"[String: Int]"
}
}

var desugaredExample: String {
switch self {
case .optional, .array:
return "\(rawValue)<Int>"
"\(rawValue)<Int>"
case .dictionary:
return "\(rawValue)<String, Int>"
"\(rawValue)<String, Int>"
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,9 @@ private extension TriviaPiece {
var isComment: Bool {
switch self {
case .lineComment, .blockComment, .docLineComment, .docBlockComment:
return true
true
default:
return false
false
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -112,13 +112,13 @@ private enum TwoArgsXCTAssert: String {

private func suggestion(for protectedArgument: String, hasOptional: Bool) -> String? {
switch (self, protectedArgument, hasOptional) {
case (.equal, "true", false): return "XCTAssertTrue"
case (.equal, "false", false): return "XCTAssertFalse"
case (.equal, "nil", _): return "XCTAssertNil"
case (.notEqual, "true", false): return "XCTAssertFalse"
case (.notEqual, "false", false): return "XCTAssertTrue"
case (.notEqual, "nil", _): return "XCTAssertNotNil"
default: return nil
case (.equal, "true", false): "XCTAssertTrue"
case (.equal, "false", false): "XCTAssertFalse"
case (.equal, "nil", _): "XCTAssertNil"
case (.notEqual, "true", false): "XCTAssertFalse"
case (.notEqual, "false", false): "XCTAssertTrue"
case (.notEqual, "nil", _): "XCTAssertNotNil"
default: nil
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,11 +204,11 @@ private extension FunctionCallExprSyntax {
func isDirectAccessibilityModifier(_ name: String) -> Bool {
switch name {
case "accessibilityHidden":
return arguments.first?.expression.as(BooleanLiteralExprSyntax.self)?.literal.tokenKind == .keyword(.true)
arguments.first?.expression.as(BooleanLiteralExprSyntax.self)?.literal.tokenKind == .keyword(.true)
case "accessibilityLabel", "accessibilityValue", "accessibilityHint":
return true
true
case "accessibility":
return arguments.contains { arg in
arguments.contains { arg in
guard let label = arg.label?.text else { return false }
if ["label", "value", "hint"].contains(label) { return true }
if label == "hidden" {
Expand All @@ -217,7 +217,7 @@ private extension FunctionCallExprSyntax {
return false
}
default:
return false
false
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ private enum AvailabilityType {
var displayString: String {
switch self {
case .condition:
return "condition"
"condition"
case .attribute:
return "attribute"
"attribute"
case .negativeCondition:
return "negative condition"
"negative condition"
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ struct UnusedImportRule: CorrectableRule, AnalyzerRule {
let missingImports = importUsages.compactMap { importUsage -> String? in
switch importUsage {
case .unused:
return nil
nil
case .missing(let module):
return module
module
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,9 +190,9 @@ private extension Either<SubscriptDeclSyntax, VariableDeclSyntax> {
var modifiers: DeclModifierListSyntax? {
switch self {
case .left(let left):
return left.modifiers
left.modifiers
case .right(let right):
return right.modifiers
right.modifiers
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -201,9 +201,9 @@ private extension TokenSyntax {
var binaryOperator: String? {
switch tokenKind {
case .binaryOperator(let str):
return str
str
default:
return nil
nil
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ struct NestingConfiguration: RuleConfiguration {

func threshold(with config: Severity, for severity: ViolationSeverity) -> Int {
switch severity {
case .error: return config.error ?? config.warning
case .warning: return config.warning
case .error: config.error ?? config.warning
case .warning: config.warning
}
}
}
8 changes: 4 additions & 4 deletions Source/SwiftLintBuiltInRules/Rules/Style/FileHeaderRule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -215,19 +215,19 @@ private extension TriviaPiece {
var isDocComment: Bool {
switch self {
case .docLineComment, .docBlockComment:
return true
true
default:
return false
false
}
}

var commentText: String? {
switch self {
case .lineComment(let text), .blockComment(let text),
.docLineComment(let text), .docBlockComment(let text):
return text
text
default:
return nil
nil
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ private enum ViolationKind {
var violationDescription: String {
switch self {
case .subscript:
return "Computed read-only subscripts should avoid using the get keyword"
"Computed read-only subscripts should avoid using the get keyword"
case .property:
return "Computed read-only properties should avoid using the get keyword"
"Computed read-only properties should avoid using the get keyword"
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,105 @@ private extension ImplicitReturnRule {
}

private func collectViolation(in itemList: CodeBlockItemListSyntax) {
guard let returnStmt = itemList.onlyElement?.item.as(ReturnStmtSyntax.self) else {
guard let onlyItem = itemList.onlyElement else {
return
}

// Case 1: Direct return statement
if let returnStmt = onlyItem.item.as(ReturnStmtSyntax.self) {
addViolation(for: returnStmt)
return
}

// Case 2: Expression statement containing if or switch
if let exprStmt = onlyItem.item.as(ExpressionStmtSyntax.self) {
analyzeExpressionForImplicitReturns(exprStmt.expression)
}
}

private func analyzeExpressionForImplicitReturns(_ expr: ExprSyntax) {
if let ifExpr = expr.as(IfExprSyntax.self) {
analyzeIfExpression(ifExpr)
} else if let switchExpr = expr.as(SwitchExprSyntax.self) {
analyzeSwitchExpression(switchExpr)
}
}

private func analyzeIfExpression(_ ifExpr: IfExprSyntax) {
guard checkIfAllBranchesCanUseImplicitReturn(ifExpr) else { return }

let returnStatements = extractAllReturnStatements(from: ifExpr)
returnStatements.forEach { addViolation(for: $0) }
}

private func analyzeSwitchExpression(_ switchExpr: SwitchExprSyntax) {
guard checkIfAllCasesCanUseImplicitReturn(switchExpr) else { return }

let returnStatements = extractAllReturnStatements(from: switchExpr)
returnStatements.forEach { addViolation(for: $0) }
}

private func extractAllReturnStatements(from ifExpr: IfExprSyntax) -> [ReturnStmtSyntax] {
var statements: [ReturnStmtSyntax] = []

// Extract from main if body
if let returnStmt = getSingleReturnStatement(from: ifExpr.body.statements) {
statements.append(returnStmt)
}

// Extract from else body (recursively handle nested if-else)
if let elseBody = ifExpr.elseBody {
switch elseBody {
case .codeBlock(let codeBlock):
if let returnStmt = getSingleReturnStatement(from: codeBlock.statements) {
statements.append(returnStmt)
}
case .ifExpr(let nestedIfExpr):
statements.append(contentsOf: extractAllReturnStatements(from: nestedIfExpr))
}
}

return statements
}

private func extractAllReturnStatements(from switchExpr: SwitchExprSyntax) -> [ReturnStmtSyntax] {
switchExpr.cases.compactMap { caseItem in
guard let switchCase = caseItem.as(SwitchCaseSyntax.self) else { return nil }
return getSingleReturnStatement(from: switchCase.statements)
}
}

private func checkIfAllBranchesCanUseImplicitReturn(_ ifExpr: IfExprSyntax) -> Bool {
guard isSingleReturnStatement(ifExpr.body.statements),
let elseBody = ifExpr.elseBody else {
return false
}

switch elseBody {
case .codeBlock(let codeBlock):
return isSingleReturnStatement(codeBlock.statements)
case .ifExpr(let nestedIfExpr):
return checkIfAllBranchesCanUseImplicitReturn(nestedIfExpr)
}
}

private func checkIfAllCasesCanUseImplicitReturn(_ switchExpr: SwitchExprSyntax) -> Bool {
!switchExpr.cases.isEmpty &&
switchExpr.cases.allSatisfy { caseItem in
guard let switchCase = caseItem.as(SwitchCaseSyntax.self) else { return false }
return isSingleReturnStatement(switchCase.statements)
}
}

private func isSingleReturnStatement(_ statements: CodeBlockItemListSyntax) -> Bool {
getSingleReturnStatement(from: statements) != nil
}

private func getSingleReturnStatement(from statements: CodeBlockItemListSyntax) -> ReturnStmtSyntax? {
statements.onlyElement?.item.as(ReturnStmtSyntax.self)
}

private func addViolation(for returnStmt: ReturnStmtSyntax) {
let returnKeyword = returnStmt.returnKeyword
violations.append(
at: returnKeyword.positionAfterSkippingLeadingTrivia,
Expand Down
Loading