Files
SwiftLint/Source/SwiftLintFramework/Rules/Lint/UnusedImportRuleExamples.swift
T
JP SimardandGitHub fa6bf50a22 Rethink body line count calculation (#4369)
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.
2022-10-14 03:16:26 -04:00

233 lines
5.7 KiB
Swift

struct UnusedImportRuleExamples {
static let nonTriggeringExamples = [
Example("""
import Dispatch // This is used
dispatchMain()
"""),
Example("""
@testable import Dispatch
dispatchMain()
"""),
Example("""
import Foundation
@objc
class A {}
"""),
Example("""
import UnknownModule
func foo(error: Swift.Error) {}
"""),
Example("""
import Foundation
import ObjectiveC
let 👨‍👩‍👧‍👦 = #selector(NSArray.contains(_:))
👨‍👩‍👧‍👦 == 👨‍👩‍👧‍👦
""")
]
static let triggeringExamples = [
Example("""
↓import Dispatch
struct A {
static func dispatchMain() {}
}
A.dispatchMain()
"""),
Example("""
↓import Foundation // This is unused
struct A {
static func dispatchMain() {}
}
A.dispatchMain()
↓import Dispatch
"""),
Example("""
↓import Foundation
dispatchMain()
"""),
Example("""
↓import Foundation
// @objc
class A {}
"""),
Example("""
↓import Foundation
import UnknownModule
func foo(error: Swift.Error) {}
"""),
Example("""
↓import Swift
↓import SwiftShims
func foo(error: Swift.Error) {}
""")
]
static let corrections = [
Example("""
↓import Dispatch
struct A {
static func dispatchMain() {}
}
A.dispatchMain()
"""):
Example("""
struct A {
static func dispatchMain() {}
}
A.dispatchMain()
"""),
Example("""
↓import Foundation // This is unused
struct A {
static func dispatchMain() {}
}
A.dispatchMain()
↓import Dispatch
"""):
Example("""
struct A {
static func dispatchMain() {}
}
A.dispatchMain()
"""),
Example("""
↓import Foundation
dispatchMain()
"""):
Example("""
dispatchMain()
"""),
Example("""
↓@testable import Foundation
import Dispatch
dispatchMain()
"""):
Example("""
import Dispatch
dispatchMain()
"""),
Example("""
↓@_exported import Foundation
import Dispatch
dispatchMain()
"""):
Example("""
import Dispatch
dispatchMain()
"""),
Example("""
↓import Foundation
// @objc
class A {}
"""):
Example("""
// @objc
class A {}
"""),
Example("""
@testable import Foundation
↓import Dispatch
@objc
class A {}
"""):
Example("""
@testable import Foundation
@objc
class A {}
"""),
Example("""
@testable import Foundation
↓@testable import Dispatch
@objc
class A {}
"""):
Example("""
@testable import Foundation
@objc
class A {}
"""),
Example("""
↓↓import Foundation
typealias Foo = CFArray
""", configuration: [
"require_explicit_imports": true,
"allowed_transitive_imports": [
[
"module": "Foundation",
"allowed_transitive_imports": ["CoreFoundation"]
]
]
], testMultiByteOffsets: false, testOnLinux: false):
Example("""
import CoreFoundation
typealias Foo = CFArray
"""),
Example("""
↓↓import Foundation
typealias Foo = CFData
""", configuration: [
"require_explicit_imports": true
], testMultiByteOffsets: false, testOnLinux: false):
Example("""
import CoreFoundation
typealias Foo = CFData
"""),
Example("""
import Foundation
typealias Foo = CFData
@objc
class A {}
""", configuration: [
"require_explicit_imports": true,
"allowed_transitive_imports": [
[
"module": "Foundation",
"allowed_transitive_imports": ["CoreFoundation"]
]
]
]):
Example("""
import Foundation
typealias Foo = CFData
@objc
class A {}
"""),
Example("""
↓import Foundation
typealias Bar = CFData
@objc
class A {}
""", configuration: [
"require_explicit_imports": true
], testMultiByteOffsets: false, testOnLinux: false):
Example("""
import CoreFoundation
import Foundation
typealias Bar = CFData
@objc
class A {}
"""),
Example("""
import Foundation
func bar() {}
""", configuration: [
"always_keep_imports": ["Foundation"]
]):
Example("""
import Foundation
func bar() {}
"""),
Example("""
↓import Swift
↓import SwiftShims
func foo(error: Swift.Error) {}
"""):
Example("""
func foo(error: Swift.Error) {}
""")
]
}