mirror of
https://github.com/realm/SwiftLint.git
synced 2026-06-06 20:18:40 +00:00
A long-standing limitation with SourceKit's "editor open" request is
that we weren't able to get certain tokens, such as braces, brackets and
parentheses.
This meant that this code block would be counted as two lines:
```swift
print(
"hi"
)
```
because the trailing `)` would be treated as a whitespace line.
This meant that our "body length" family of rules that measure the
effective line count of declarations like functions, types or closures
would often significantly under-count the number of content lines in a
body.
Now with SwiftSyntax, we can get all tokens, including the ones
SourceKit was previously ignoring, so we can get much more accurate line
counts when ignoring whitespace and comments.
In addition, we weren't very thorough in how we measured body length.
As an exercise, how many lines long would you say the body of this
function is?
```swift
func hello() {
print("hello")
}
```
Does the body span one line or three lines?
I propose that we consistently ignore the left and right brace lines
when calculating the body line count of these scopes so that we measure
body line counts like this:
```swift
// 1 line
{ print("foo") }
// 1 line
{
}
// 1 line
{
print("foo")
}
// 2 lines
{
let sum = 1 + 2
print(sum)
}
```
Now with those changes in place, in order to keep the default
configuration thresholds to similar levels as before, we need to adjust
them slightly. Here's what I'm suggesting:
|Rule|Before|After|
|-|-|-|
|closure_body_length|20/100|30/100|
|function_body_length|40/100|50/100|
|type_body_length|200/350|250/350|
This is a pretty significant breaking change and I suspect we'll hear
from users who are surprised that some of their declarations now exceed
the rule limits, but I believe this new approach to calculating body
lines is more correct and intuitive compared to what we've had until
now.
OSSCheck is also going to report a bazillion changes with this, which is
expected given the scope of this change.
305 lines
7.9 KiB
Swift
305 lines
7.9 KiB
Swift
struct UnusedDeclarationRuleExamples {
|
|
static let nonTriggeringExamples = [
|
|
Example("""
|
|
let kConstant = 0
|
|
_ = kConstant
|
|
"""),
|
|
Example("""
|
|
enum Change<T> {
|
|
case insert(T)
|
|
case delete(T)
|
|
}
|
|
|
|
extension Sequence {
|
|
func deletes<T>() -> [T] where Element == Change<T> {
|
|
return compactMap { operation in
|
|
if case .delete(let value) = operation {
|
|
return value
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
let changes = [Change.insert(0), .delete(0)]
|
|
_ = changes.deletes()
|
|
"""),
|
|
Example("""
|
|
struct Item: Codable {}
|
|
struct ResponseModel: Codable {
|
|
let items: [Item]
|
|
|
|
enum CodingKeys: String, CodingKey {
|
|
case items = "ResponseItems"
|
|
}
|
|
}
|
|
|
|
_ = ResponseModel(items: [Item()]).items
|
|
"""),
|
|
Example("""
|
|
class ResponseModel {
|
|
@objc func foo() {
|
|
}
|
|
}
|
|
_ = ResponseModel()
|
|
"""),
|
|
Example("""
|
|
public func foo() {}
|
|
"""),
|
|
Example("""
|
|
protocol Foo {}
|
|
|
|
extension Foo {
|
|
func bar() {}
|
|
}
|
|
|
|
struct MyStruct: Foo {}
|
|
MyStruct().bar()
|
|
"""),
|
|
Example("""
|
|
import XCTest
|
|
class MyTests: XCTestCase {
|
|
func testExample() {}
|
|
}
|
|
"""),
|
|
Example("""
|
|
import XCTest
|
|
open class BestTestCase: XCTestCase {}
|
|
class MyTests: BestTestCase {
|
|
func testExample() {}
|
|
}
|
|
"""),
|
|
Example("""
|
|
enum Component {
|
|
case string(StaticString)
|
|
indirect case array([Component])
|
|
indirect case optional(Component?)
|
|
}
|
|
|
|
@resultBuilder
|
|
struct ComponentBuilder {
|
|
static func buildBlock(_ components: Component...) -> Component {
|
|
return .array(components)
|
|
}
|
|
|
|
static func buildExpression(_ string: StaticString) -> Component {
|
|
return .string(string)
|
|
}
|
|
|
|
static func buildOptional(_ component: Component?) -> Component {
|
|
return .optional(component)
|
|
}
|
|
|
|
static func buildEither(first component: Component) -> Component {
|
|
return component
|
|
}
|
|
|
|
static func buildEither(second component: Component) -> Component {
|
|
return component
|
|
}
|
|
|
|
static func buildArray(_ components: [Component]) -> Component {
|
|
return .array(components)
|
|
}
|
|
|
|
static func buildLimitedAvailability(_ component: Component) -> Component {
|
|
return component
|
|
}
|
|
|
|
static func buildFinalResult(_ component: Component) -> Component {
|
|
return component
|
|
}
|
|
|
|
static func buildPartialBlock(first component: Component) -> Component {
|
|
return component
|
|
}
|
|
|
|
static func buildPartialBlock(accumulated component: Component, next: Component) -> Component {
|
|
return component
|
|
}
|
|
}
|
|
|
|
func acceptComponentBuilder(@ComponentBuilder _ body: () -> Component) {
|
|
print(body())
|
|
}
|
|
|
|
acceptComponentBuilder {
|
|
"hello"
|
|
}
|
|
""")
|
|
] + platformSpecificNonTriggeringExamples
|
|
|
|
static let triggeringExamples = [
|
|
Example("""
|
|
let ↓kConstant = 0
|
|
"""),
|
|
Example("""
|
|
struct Item {}
|
|
struct ↓ResponseModel: Codable {
|
|
let ↓items: [Item]
|
|
|
|
enum ↓CodingKeys: String {
|
|
case items = "ResponseItems"
|
|
}
|
|
}
|
|
"""),
|
|
Example("""
|
|
class ↓ResponseModel {
|
|
func ↓foo() {
|
|
}
|
|
}
|
|
"""),
|
|
Example("""
|
|
public func ↓foo() {}
|
|
""", configuration: ["include_public_and_open": true]),
|
|
Example("""
|
|
protocol Foo {
|
|
func ↓bar1()
|
|
}
|
|
|
|
extension Foo {
|
|
func bar1() {}
|
|
func ↓bar2() {}
|
|
}
|
|
|
|
struct MyStruct: Foo {}
|
|
_ = MyStruct()
|
|
"""),
|
|
Example("""
|
|
import XCTest
|
|
class ↓MyTests: NSObject {
|
|
func ↓testExample() {}
|
|
}
|
|
"""),
|
|
Example("""
|
|
enum Component {
|
|
case string(StaticString)
|
|
indirect case array([Component])
|
|
indirect case optional(Component?)
|
|
}
|
|
|
|
struct ComponentBuilder {
|
|
func ↓buildExpression(_ string: StaticString) -> Component {
|
|
return .string(string)
|
|
}
|
|
|
|
func ↓buildBlock(_ components: Component...) -> Component {
|
|
return .array(components)
|
|
}
|
|
|
|
func ↓buildIf(_ value: Component?) -> Component {
|
|
return .optional(value)
|
|
}
|
|
|
|
static func ↓buildABear(_ components: Component...) -> Component {
|
|
return .array(components)
|
|
}
|
|
}
|
|
|
|
_ = ComponentBuilder()
|
|
""")
|
|
] + platformSpecificTriggeringExamples
|
|
|
|
#if os(macOS)
|
|
private static let platformSpecificNonTriggeringExamples = [
|
|
Example("""
|
|
import Cocoa
|
|
|
|
@NSApplicationMain
|
|
final class AppDelegate: NSObject, NSApplicationDelegate {
|
|
func applicationWillFinishLaunching(_ notification: Notification) {}
|
|
func applicationWillBecomeActive(_ notification: Notification) {}
|
|
}
|
|
"""),
|
|
Example("""
|
|
import Foundation
|
|
|
|
public final class Foo: NSObject {
|
|
@IBAction private func foo() {}
|
|
}
|
|
"""),
|
|
Example("""
|
|
import Foundation
|
|
|
|
public final class Foo: NSObject {
|
|
@objc func foo() {}
|
|
}
|
|
"""),
|
|
Example("""
|
|
import Foundation
|
|
|
|
public final class Foo: NSObject {
|
|
@IBInspectable private var innerPaddingWidth: Int {
|
|
set { self.backgroundView.innerPaddingWidth = newValue }
|
|
get { return self.backgroundView.innerPaddingWidth }
|
|
}
|
|
}
|
|
"""),
|
|
Example("""
|
|
import Foundation
|
|
|
|
public final class Foo: NSObject {
|
|
@IBOutlet private var bar: NSObject! {
|
|
set { fatalError() }
|
|
get { fatalError() }
|
|
}
|
|
|
|
@IBOutlet private var baz: NSObject! {
|
|
willSet { print("willSet") }
|
|
}
|
|
|
|
@IBOutlet private var buzz: NSObject! {
|
|
didSet { print("didSet") }
|
|
}
|
|
}
|
|
""")
|
|
]
|
|
|
|
private static let platformSpecificTriggeringExamples = [
|
|
Example("""
|
|
import Cocoa
|
|
|
|
@NSApplicationMain
|
|
final class AppDelegate: NSObject, NSApplicationDelegate {
|
|
func ↓appWillFinishLaunching(_ notification: Notification) {}
|
|
func applicationWillBecomeActive(_ notification: Notification) {}
|
|
}
|
|
"""),
|
|
Example("""
|
|
import Cocoa
|
|
|
|
final class ↓AppDelegate: NSObject, NSApplicationDelegate {
|
|
func applicationWillFinishLaunching(_ notification: Notification) {}
|
|
func applicationWillBecomeActive(_ notification: Notification) {}
|
|
}
|
|
"""),
|
|
Example("""
|
|
import Foundation
|
|
|
|
public final class Foo: NSObject {
|
|
@IBOutlet var ↓bar: NSObject!
|
|
}
|
|
"""),
|
|
Example("""
|
|
import Foundation
|
|
|
|
public final class Foo: NSObject {
|
|
@IBInspectable var ↓bar: String!
|
|
}
|
|
"""),
|
|
Example("""
|
|
import Foundation
|
|
|
|
final class Foo: NSObject {}
|
|
final class ↓Bar {
|
|
var ↓foo = Foo()
|
|
}
|
|
""")
|
|
]
|
|
#else
|
|
private static let platformSpecificNonTriggeringExamples = [Example]()
|
|
private static let platformSpecificTriggeringExamples = [Example]()
|
|
#endif
|
|
}
|