Compare commits
299 Commits
Author | SHA1 | Date |
---|---|---|
![]() |
3ec9bc85bc | |
![]() |
f5ea2492dc | |
![]() |
b89e43a799 | |
![]() |
c8cd311615 | |
![]() |
ba42a6aa36 | |
![]() |
83ab977a1b | |
![]() |
f12b8d66d3 | |
![]() |
e29de7a99f | |
![]() |
d32bf2b879 | |
![]() |
e5cf99088b | |
![]() |
7462187191 | |
![]() |
23326fc433 | |
![]() |
156f6aabc8 | |
![]() |
43190834c5 | |
![]() |
f1d5a55658 | |
![]() |
c735e6320f | |
![]() |
2f9997e2cc | |
![]() |
8f3eea06d3 | |
![]() |
a53a1f052b | |
![]() |
704c9ae116 | |
![]() |
521df18fdb | |
![]() |
a2ba0a0626 | |
![]() |
1094a3b70e | |
![]() |
754127924f | |
![]() |
3f039f26d5 | |
![]() |
671589b1d4 | |
![]() |
d938b2d3c3 | |
![]() |
7fd22e0d6e | |
![]() |
07740506b5 | |
![]() |
26d06bcebb | |
![]() |
fe312a06fe | |
![]() |
d73970e9d7 | |
![]() |
c0ea9d1925 | |
![]() |
405113f793 | |
![]() |
f4987071f0 | |
![]() |
306dec0fcc | |
![]() |
4d4a330d46 | |
![]() |
1e73835e83 | |
![]() |
866001db2f | |
![]() |
34f5ffa7f7 | |
![]() |
1b1b19a902 | |
![]() |
15a18fd4e8 | |
![]() |
5802fd99b1 | |
![]() |
e7bf813f15 | |
![]() |
3cf9585b2e | |
![]() |
8827fca693 | |
![]() |
9f7c57b719 | |
![]() |
73acbaf6d5 | |
![]() |
5616d858bc | |
![]() |
6cdb0aa689 | |
![]() |
7483e61615 | |
![]() |
87e230ce87 | |
![]() |
7f3b93147a | |
![]() |
7501d44b69 | |
![]() |
0ffe0c5911 | |
![]() |
c99a4286b8 | |
![]() |
8d5700afca | |
![]() |
1fa8e484c4 | |
![]() |
9e15e37255 | |
![]() |
97259e24d0 | |
![]() |
eb6d50a04c | |
![]() |
9c1678968f | |
![]() |
fa32fbc4f0 | |
![]() |
8822d40687 | |
![]() |
c0cf1bf5c9 | |
![]() |
444653bfd5 | |
![]() |
9138213121 | |
![]() |
6fd5f98d38 | |
![]() |
bc18ff14e3 | |
![]() |
5e15039554 | |
![]() |
7756793356 | |
![]() |
f0138ea1df | |
![]() |
6a2e973de3 | |
![]() |
e86c06c390 | |
![]() |
6b094dd711 | |
![]() |
9054ec0658 | |
![]() |
71b89e66de | |
![]() |
a7bc9e20c7 | |
![]() |
eaf34d7204 | |
![]() |
86d60400c1 | |
![]() |
740572f049 | |
![]() |
3541ef4d02 | |
![]() |
39a05f2c35 | |
![]() |
711d92e26f | |
![]() |
d8dfc3c937 | |
![]() |
3a2bb15ca3 | |
![]() |
a7e3909c6a | |
![]() |
72c2a5488d | |
![]() |
ea56405983 | |
![]() |
9c414932d8 | |
![]() |
8a21549ca9 | |
![]() |
4abcf5f0dd | |
![]() |
b2a4c9fcb9 | |
![]() |
603fff9a82 | |
![]() |
2544dc79d3 | |
![]() |
ea6b3b4d64 | |
![]() |
172b8b8a94 | |
![]() |
4c04cdafa7 | |
![]() |
165172e0fa | |
![]() |
3f52acd0a2 | |
![]() |
e14a73438f | |
![]() |
817de197d5 | |
![]() |
214a749ace | |
![]() |
fd4f9afb08 | |
![]() |
c241935635 | |
![]() |
46ff727a13 | |
![]() |
dc89109622 | |
![]() |
9c2525139a | |
![]() |
d601917e40 | |
![]() |
498b4c8fbe | |
![]() |
69fadb6918 | |
![]() |
f866ec32b1 | |
![]() |
6d51459323 | |
![]() |
21dc766c93 | |
![]() |
0f802f766a | |
![]() |
fbbccf9db8 | |
![]() |
8b72eb0406 | |
![]() |
c22d60fac0 | |
![]() |
1892c846b0 | |
![]() |
41290a23d3 | |
![]() |
5814ec1a1a | |
![]() |
f9b0e9f0af | |
![]() |
3ef44cf742 | |
![]() |
5aed7ce95c | |
![]() |
97fd216455 | |
![]() |
f127ba14dd | |
![]() |
74b82daba0 | |
![]() |
8c617d7412 | |
![]() |
a4f4680442 | |
![]() |
62b2914271 | |
![]() |
1b0f37c6f6 | |
![]() |
5226725689 | |
![]() |
e361e14c90 | |
![]() |
70a56a1420 | |
![]() |
409f117da9 | |
![]() |
7fe7bb28d9 | |
![]() |
bb11e6a0a7 | |
![]() |
eee97a7b2b | |
![]() |
cf1e2e27dc | |
![]() |
a2facce70c | |
![]() |
bd444fcd77 | |
![]() |
ee849bcb10 | |
![]() |
6d4bc78cb4 | |
![]() |
16e2bb0f18 | |
![]() |
b0cbb440c3 | |
![]() |
58a07eb452 | |
![]() |
82491e6f84 | |
![]() |
3d15419adb | |
![]() |
6a09af169e | |
![]() |
7dad240ea7 | |
![]() |
ca43d2359b | |
![]() |
da27f1c7fd | |
![]() |
eb85125a5f | |
![]() |
62b9c2de7a | |
![]() |
aeee6325af | |
![]() |
5a91b1e392 | |
![]() |
efc5afd5d5 | |
![]() |
f2d15355be | |
![]() |
a840058cf5 | |
![]() |
358bdc89a5 | |
![]() |
a81a532091 | |
![]() |
6983c813c8 | |
![]() |
fb89ab2fb5 | |
![]() |
7ac128c83d | |
![]() |
06578e5d91 | |
![]() |
1940e0e2d2 | |
![]() |
bd77cbcf6e | |
![]() |
1bf2f25e40 | |
![]() |
7d7bee5eee | |
![]() |
31510b662e | |
![]() |
0bd8a7aba6 | |
![]() |
084ad9dfd4 | |
![]() |
228d53787b | |
![]() |
4fee8e6a0c | |
![]() |
4f9a608cf4 | |
![]() |
1c3c62e422 | |
![]() |
d6e3bbb64d | |
![]() |
128f37a6b8 | |
![]() |
cfd9a26e33 | |
![]() |
a71f588637 | |
![]() |
d73d87ac97 | |
![]() |
04791929a7 | |
![]() |
c9b1b961f5 | |
![]() |
613e916c39 | |
![]() |
e2f3f3ad45 | |
![]() |
5c94a43352 | |
![]() |
183c0aa784 | |
![]() |
ed6793f8f1 | |
![]() |
1790ad56dd | |
![]() |
2f0e537f9b | |
![]() |
393318d903 | |
![]() |
f3e5557de9 | |
![]() |
352ffdfc57 | |
![]() |
6a9e6776a9 | |
![]() |
ec38c244fd | |
![]() |
ad29864d7f | |
![]() |
b9e5cfb202 | |
![]() |
9c216baf20 | |
![]() |
7eb479d546 | |
![]() |
aafc574e90 | |
![]() |
5af4291b53 | |
![]() |
d3ebfc5567 | |
![]() |
60d0dd8a05 | |
![]() |
0163ffd328 | |
![]() |
eb5712582f | |
![]() |
6ea0397620 | |
![]() |
aba0f63704 | |
![]() |
993f34a96c | |
![]() |
eaacd9873e | |
![]() |
843198d241 | |
![]() |
325d0ee1e4 | |
![]() |
5eed8fe91b | |
![]() |
60610cef84 | |
![]() |
ca09bc5a22 | |
![]() |
0796236031 | |
![]() |
651b00eb70 | |
![]() |
500f143c7d | |
![]() |
81f1dbfd1b | |
![]() |
0750d5d465 | |
![]() |
84c6d200b6 | |
![]() |
9ac13f078a | |
![]() |
c84124d8bc | |
![]() |
3907772163 | |
![]() |
d98fd5386a | |
![]() |
36c24c822c | |
![]() |
e1fdf17d1b | |
![]() |
23e4db3e5f | |
![]() |
b4641e7e60 | |
![]() |
341db66f3c | |
![]() |
c9791783ba | |
![]() |
dc228d57ac | |
![]() |
fbd9f16955 | |
![]() |
d120f41181 | |
![]() |
db6aea5d07 | |
![]() |
876d8fc872 | |
![]() |
45c5f06754 | |
![]() |
040096a641 | |
![]() |
f91a2d5310 | |
![]() |
f8f2317bdb | |
![]() |
82249f5ed4 | |
![]() |
78c9ad6f33 | |
![]() |
693e504258 | |
![]() |
b66dd13e2f | |
![]() |
5ec6112ba1 | |
![]() |
74dbd52add | |
![]() |
cc44c989b7 | |
![]() |
e64e82d80d | |
![]() |
ea8b17dd9c | |
![]() |
672e19651f | |
![]() |
67eb330f1c | |
![]() |
e6d4ad653c | |
![]() |
100b3eec2a | |
![]() |
bcfc2c4b6c | |
![]() |
68dc0f58d2 | |
![]() |
d6ff2a7f37 | |
![]() |
64d9619a8a | |
![]() |
470d471e51 | |
![]() |
5ff40867b3 | |
![]() |
58611e6718 | |
![]() |
c740da48d5 | |
![]() |
eda0d92f44 | |
![]() |
d76daf5f62 | |
![]() |
1a39194f65 | |
![]() |
0e53e19cb1 | |
![]() |
3cba460f9a | |
![]() |
d3928cb8e8 | |
![]() |
a6c3cec8c9 | |
![]() |
7bd8362dae | |
![]() |
e1cddb710d | |
![]() |
4a6387da67 | |
![]() |
83ee9a89dd | |
![]() |
540003e2a1 | |
![]() |
3745704c03 | |
![]() |
33fa42becb | |
![]() |
25c3fb161d | |
![]() |
53ee4a955d | |
![]() |
40fb3796cb | |
![]() |
a876e860ee | |
![]() |
4c406459bd | |
![]() |
4ddeb22b6d | |
![]() |
ff06e0ed00 | |
![]() |
6734a64554 | |
![]() |
73a64d674c | |
![]() |
7f8eb9de77 | |
![]() |
609418f702 | |
![]() |
4adabd8e4b | |
![]() |
93d0d8fa7f | |
![]() |
4dfef014ab | |
![]() |
95d56e4130 | |
![]() |
7f9dd69c62 | |
![]() |
ac44fe23b6 | |
![]() |
60128ab196 | |
![]() |
7a8b2d1dab | |
![]() |
92304cdd98 | |
![]() |
ab143685a4 | |
![]() |
9cb1069090 | |
![]() |
e405d3d583 | |
![]() |
97ba82d740 | |
![]() |
d8d5e5bb43 |
|
@ -0,0 +1 @@
|
||||||
|
.build
|
6
.bazelrc
6
.bazelrc
|
@ -1,8 +1,14 @@
|
||||||
|
common --enable_bzlmod
|
||||||
|
|
||||||
try-import %workspace%/ci.bazelrc
|
try-import %workspace%/ci.bazelrc
|
||||||
try-import %workspace%/user.bazelrc
|
try-import %workspace%/user.bazelrc
|
||||||
|
|
||||||
build --macos_minimum_os=12.0 --host_macos_minimum_os=12.0
|
build --macos_minimum_os=12.0 --host_macos_minimum_os=12.0
|
||||||
build --disk_cache=~/.bazel_cache
|
build --disk_cache=~/.bazel_cache
|
||||||
|
build --experimental_remote_cache_compression
|
||||||
|
build --experimental_remote_build_event_upload=minimal
|
||||||
|
build --nolegacy_important_outputs
|
||||||
|
build --swiftcopt=-warnings-as-errors
|
||||||
|
|
||||||
build:release \
|
build:release \
|
||||||
--compilation_mode=opt \
|
--compilation_mode=opt \
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
5.3.2
|
6.2.0
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
fixedReleaser:
|
||||||
|
login: jpsim
|
||||||
|
email: jp@jpsim.com
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"homepage": "https://github.com/realm/SwiftLint",
|
||||||
|
"maintainers": [
|
||||||
|
{
|
||||||
|
"email": "jp@jpsim.com",
|
||||||
|
"github": "jpsim",
|
||||||
|
"name": "JP Simard"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"repository": [
|
||||||
|
"github:realm/SwiftLint"
|
||||||
|
],
|
||||||
|
"versions": [],
|
||||||
|
"yanked_versions": {}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
shell_commands: &shell_commands
|
||||||
|
- "echo --- Downloading and extracting Swift $SWIFT_VERSION to $SWIFT_HOME"
|
||||||
|
- "mkdir $SWIFT_HOME"
|
||||||
|
- "curl https://download.swift.org/swift-${SWIFT_VERSION}-release/ubuntu2004/swift-${SWIFT_VERSION}-RELEASE/swift-${SWIFT_VERSION}-RELEASE-ubuntu20.04.tar.gz | tar xvz --strip-components=1 -C $SWIFT_HOME"
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
verify_targets_linux:
|
||||||
|
name: Verify targets (Linux)
|
||||||
|
platform: ubuntu2004
|
||||||
|
environment:
|
||||||
|
CC: "clang"
|
||||||
|
SWIFT_VERSION: "5.7.2"
|
||||||
|
SWIFT_HOME: "$HOME/swift-$SWIFT_VERSION"
|
||||||
|
PATH: "$PATH:$SWIFT_HOME/usr/bin"
|
||||||
|
shell_commands: *shell_commands
|
||||||
|
build_flags:
|
||||||
|
- "--action_env=PATH"
|
||||||
|
build_targets:
|
||||||
|
- '@SwiftLint//:swiftlint'
|
||||||
|
verify_targets_macos:
|
||||||
|
name: Verify targets (macOS)
|
||||||
|
platform: macos
|
||||||
|
build_targets:
|
||||||
|
- '@SwiftLint//:swiftlint'
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"url": "https://github.com/realm/SwiftLint/releases/download/{TAG}/bazel.tar.gz",
|
||||||
|
"integrity": "",
|
||||||
|
"strip_prefix": ""
|
||||||
|
}
|
|
@ -11,23 +11,19 @@ steps:
|
||||||
- swift test --parallel -Xswiftc -DDISABLE_FOCUSED_EXAMPLES
|
- swift test --parallel -Xswiftc -DDISABLE_FOCUSED_EXAMPLES
|
||||||
- label: "Danger"
|
- label: "Danger"
|
||||||
commands:
|
commands:
|
||||||
- echo "--- Build Danger"
|
- echo "--- Install Bundler"
|
||||||
- bazel build //tools:danger
|
- gem install bundler
|
||||||
|
- echo "--- Bundle Install"
|
||||||
|
- bundle install
|
||||||
- echo "+++ Run Danger"
|
- echo "+++ Run Danger"
|
||||||
- ./bazel-bin/tools/danger --verbose
|
- bundle exec danger --verbose
|
||||||
- label: "Analyze"
|
|
||||||
commands:
|
|
||||||
- echo "+++ Analyze"
|
|
||||||
- bazel test -c opt --test_output=streamed --test_timeout=1800 --spawn_strategy=local analyze
|
|
||||||
- label: "TSan Tests"
|
- label: "TSan Tests"
|
||||||
commands:
|
commands:
|
||||||
- echo "+++ Test"
|
- echo "+++ Test"
|
||||||
- bazel test --test_output=streamed --build_tests_only --features=tsan --test_timeout=1000 //Tests/...
|
- bazel test --test_output=errors --build_tests_only --features=tsan --test_timeout=1000 //Tests/...
|
||||||
- label: "TSan Runs"
|
- label: "Sourcery"
|
||||||
commands:
|
commands:
|
||||||
- echo "--- Build"
|
- echo "+++ Run Sourcery"
|
||||||
- bazel build --config=release --features=tsan swiftlint
|
- make --always-make sourcery
|
||||||
- echo "+++ Pre-cache SwiftLint Run"
|
- echo "+++ Diff Files"
|
||||||
- ./bazel-bin/swiftlint --progress --lenient
|
- git diff --quiet HEAD
|
||||||
- echo "+++ Post-cache SwiftLint Run"
|
|
||||||
- ./bazel-bin/swiftlint --progress --lenient
|
|
||||||
|
|
|
@ -9,10 +9,10 @@ on:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-20.04
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Extract DOCKER_TAG using tag name
|
- name: Extract DOCKER_TAG using tag name
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
name: update_swift_syntax
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
schedule:
|
|
||||||
# Mondays at 1pm UTC (9am EDT)
|
|
||||||
- cron: "0 13 * * 1"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
update_swift_syntax:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout source
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
- name: Update SwiftSyntax
|
|
||||||
id: update-swift-syntax
|
|
||||||
run: ./tools/update-swift-syntax.sh
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
- name: Check for changes
|
|
||||||
id: state
|
|
||||||
run: |
|
|
||||||
if ! git diff-index --quiet HEAD --; then
|
|
||||||
echo "dirty=true" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
- name: Create PR
|
|
||||||
if: steps.state.outputs.dirty == 'true'
|
|
||||||
uses: peter-evans/create-pull-request@923ad837f191474af6b1721408744feb989a4c27
|
|
||||||
with:
|
|
||||||
title: Update SwiftSyntax
|
|
||||||
body: |
|
|
||||||
Diff: https://github.com/apple/swift-syntax/compare/${{ steps.update-swift-syntax.outputs.old_commit }}...${{ steps.update-swift-syntax.outputs.new_commit }}
|
|
||||||
commit-message: Update SwiftSyntax
|
|
||||||
delete-branch: true
|
|
||||||
branch: update-swift-syntax
|
|
||||||
branch-suffix: timestamp
|
|
|
@ -1,6 +1,5 @@
|
||||||
# Xcode
|
# Xcode
|
||||||
#
|
#
|
||||||
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
|
|
||||||
|
|
||||||
## Build generated
|
## Build generated
|
||||||
build/
|
build/
|
||||||
|
@ -22,6 +21,7 @@ xcuserdata
|
||||||
*.moved-aside
|
*.moved-aside
|
||||||
*.xcuserstate
|
*.xcuserstate
|
||||||
*.xcscmblueprint
|
*.xcscmblueprint
|
||||||
|
default.profraw
|
||||||
|
|
||||||
## Obj-C/Swift specific
|
## Obj-C/Swift specific
|
||||||
*.hmap
|
*.hmap
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
module: SwiftLintFramework
|
module: SwiftLintCore
|
||||||
author: JP Simard, SwiftLint Contributors
|
author: JP Simard, SwiftLint Contributors
|
||||||
author_url: https://jpsim.com
|
author_url: https://jpsim.com
|
||||||
root_url: https://realm.github.io/SwiftLint/
|
root_url: https://realm.github.io/SwiftLint/
|
||||||
|
@ -7,13 +7,14 @@ github_file_prefix: https://github.com/realm/SwiftLint/tree/main
|
||||||
swift_build_tool: spm
|
swift_build_tool: spm
|
||||||
theme: fullwidth
|
theme: fullwidth
|
||||||
clean: true
|
clean: true
|
||||||
copyright: '© 2022 [JP Simard](https://jpsim.com) under MIT.'
|
copyright: '© 2023 [JP Simard](https://jpsim.com) under MIT.'
|
||||||
|
|
||||||
documentation: rule_docs/*.md
|
documentation: rule_docs/*.md
|
||||||
hide_unlisted_documentation: true
|
hide_unlisted_documentation: true
|
||||||
custom_categories_unlisted_prefix: ''
|
custom_categories_unlisted_prefix: ''
|
||||||
exclude:
|
exclude:
|
||||||
- Source/SwiftLintFramework/Rules/**/*.swift
|
# TODO: Document extensions
|
||||||
|
- Source/SwiftLintCore/Extensions/*.swift
|
||||||
custom_categories:
|
custom_categories:
|
||||||
- name: Rules
|
- name: Rules
|
||||||
children:
|
children:
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
|
|
||||||
/// The rule list containing all available rules built into SwiftLint.
|
/// The rule list containing all available rules built into SwiftLint.
|
||||||
public let primaryRuleList = RuleList(rules: [
|
public let builtInRules: [Rule.Type] = [
|
||||||
{% for rule in types.structs where rule.name|hasSuffix:"Rule" or rule.name|hasSuffix:"Rules" %} {{ rule.name }}.self{% if not forloop.last %},{% endif %}
|
{% for rule in types.structs where rule.name|hasSuffix:"Rule" or rule.name|hasSuffix:"Rules" %} {{ rule.name }}.self{% if not forloop.last %},{% endif %}
|
||||||
{% endfor %}] + extraRules())
|
{% endfor %}]
|
|
@ -1,12 +1,14 @@
|
||||||
@testable import SwiftLintFramework
|
@testable import SwiftLintBuiltInRules
|
||||||
|
@_spi(TestHelper)
|
||||||
|
@testable import SwiftLintCore
|
||||||
import SwiftLintTestHelpers
|
import SwiftLintTestHelpers
|
||||||
import XCTest
|
|
||||||
|
|
||||||
|
// swiftlint:disable:next blanket_disable_command
|
||||||
// swiftlint:disable file_length single_test_class type_name
|
// swiftlint:disable file_length single_test_class type_name
|
||||||
|
|
||||||
{% for rule in types.structs %}
|
{% for rule in types.structs %}
|
||||||
{% if rule.name|hasSuffix:"Rule" %}
|
{% if rule.name|hasSuffix:"Rule" %}
|
||||||
class {{ rule.name }}GeneratedTests: XCTestCase {
|
class {{ rule.name }}GeneratedTests: SwiftLintTestCase {
|
||||||
func testWithDefaultConfiguration() {
|
func testWithDefaultConfiguration() {
|
||||||
verifyRule({{ rule.name }}.description)
|
verifyRule({{ rule.name }}.description)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
|
||||||
|
/// The reporters list containing all the reporters built into SwiftLint.
|
||||||
|
public let reportersList: [Reporter.Type] = [
|
||||||
|
{% for reporter in types.structs where reporter.name|hasSuffix:"Reporter" %}
|
||||||
|
{{ reporter.name }}.self{% if not forloop.last %},{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
]
|
131
.swiftlint.yml
131
.swiftlint.yml
|
@ -8,75 +8,55 @@ analyzer_rules:
|
||||||
- unused_declaration
|
- unused_declaration
|
||||||
- unused_import
|
- unused_import
|
||||||
opt_in_rules:
|
opt_in_rules:
|
||||||
- array_init
|
- all
|
||||||
- attributes
|
disabled_rules:
|
||||||
- closure_end_indentation
|
- anonymous_argument_in_multiline_closure
|
||||||
- closure_spacing
|
- anyobject_protocol
|
||||||
- collection_alignment
|
- closure_body_length
|
||||||
- contains_over_filter_count
|
- conditional_returns_on_newline
|
||||||
- contains_over_filter_is_empty
|
- convenience_type
|
||||||
- contains_over_first_not_nil
|
- discouraged_optional_collection
|
||||||
- contains_over_range_nil_comparison
|
- explicit_acl
|
||||||
- discouraged_none_name
|
- explicit_enum_raw_value
|
||||||
- discouraged_object_literal
|
- explicit_top_level_acl
|
||||||
- empty_collection_literal
|
- explicit_type_interface
|
||||||
- empty_count
|
- file_types_order
|
||||||
- empty_string
|
- force_unwrapping
|
||||||
- empty_xctest_method
|
- function_default_parameter_at_end
|
||||||
- enum_case_associated_values_count
|
- implicit_return
|
||||||
- explicit_init
|
- implicitly_unwrapped_optional
|
||||||
- extension_access_modifier
|
- indentation_width
|
||||||
- fallthrough
|
- inert_defer
|
||||||
- fatal_error_message
|
- missing_docs
|
||||||
- file_header
|
- multiline_arguments
|
||||||
- file_name
|
- multiline_arguments_brackets
|
||||||
- first_where
|
- multiline_function_chains
|
||||||
- flatmap_over_map_reduce
|
- multiline_literal_brackets
|
||||||
- identical_operands
|
- multiline_parameters
|
||||||
- joined_default_parameter
|
- multiline_parameters_brackets
|
||||||
- last_where
|
- no_extension_access_modifier
|
||||||
- legacy_multiple
|
- no_fallthrough_only
|
||||||
- literal_expression_end_indentation
|
- no_grouping_extension
|
||||||
- local_doc_comment
|
- no_magic_numbers
|
||||||
- lower_acl_than_parent
|
- prefer_nimble
|
||||||
- modifier_order
|
- prefer_self_in_static_references
|
||||||
- nimble_operator
|
- prefixed_toplevel_constant
|
||||||
- nslocalizedstring_key
|
- redundant_self_in_closure
|
||||||
- number_separator
|
- required_deinit
|
||||||
- object_literal
|
- self_binding
|
||||||
- operator_usage_whitespace
|
- sorted_enum_cases
|
||||||
- overridden_super_call
|
- strict_fileprivate
|
||||||
- override_in_extension
|
- superfluous_else
|
||||||
- pattern_matching_keywords
|
- switch_case_on_newline
|
||||||
- prefer_self_type_over_type_of_self
|
- todo
|
||||||
- private_action
|
- trailing_closure
|
||||||
- private_outlet
|
- type_contents_order
|
||||||
- prohibited_interface_builder
|
- unused_capture_list
|
||||||
- prohibited_super_call
|
- vertical_whitespace_between_cases
|
||||||
- quick_discouraged_call
|
|
||||||
- quick_discouraged_focused_test
|
|
||||||
- quick_discouraged_pending_test
|
|
||||||
- reduce_into
|
|
||||||
- redundant_nil_coalescing
|
|
||||||
- redundant_type_annotation
|
|
||||||
- return_value_from_void_function
|
|
||||||
- single_test_class
|
|
||||||
- sorted_first_last
|
|
||||||
- sorted_imports
|
|
||||||
- static_operator
|
|
||||||
- strong_iboutlet
|
|
||||||
- test_case_accessibility
|
|
||||||
- toggle_bool
|
|
||||||
- unavailable_function
|
|
||||||
- unneeded_parentheses_in_closure_argument
|
|
||||||
- unowned_variable_capture
|
|
||||||
- untyped_error_in_catch
|
|
||||||
- vertical_parameter_alignment_on_call
|
|
||||||
- vertical_whitespace_closing_braces
|
|
||||||
- vertical_whitespace_opening_braces
|
|
||||||
- xct_specific_matcher
|
|
||||||
- yoda_condition
|
|
||||||
|
|
||||||
|
attributes:
|
||||||
|
always_on_line_above:
|
||||||
|
- "@OptionGroup"
|
||||||
identifier_name:
|
identifier_name:
|
||||||
excluded:
|
excluded:
|
||||||
- id
|
- id
|
||||||
|
@ -85,19 +65,27 @@ number_separator:
|
||||||
minimum_length: 5
|
minimum_length: 5
|
||||||
file_name:
|
file_name:
|
||||||
excluded:
|
excluded:
|
||||||
- SwiftSyntax+SwiftLint.swift
|
- Exports.swift
|
||||||
- GeneratedTests.swift
|
- GeneratedTests.swift
|
||||||
|
- SwiftSyntax+SwiftLint.swift
|
||||||
- TestHelpers.swift
|
- TestHelpers.swift
|
||||||
|
|
||||||
|
balanced_xctest_lifecycle: &unit_test_configuration
|
||||||
|
test_parent_classes:
|
||||||
|
- SwiftLintTestCase
|
||||||
|
- XCTestCase
|
||||||
|
empty_xctest_method: *unit_test_configuration
|
||||||
|
single_test_class: *unit_test_configuration
|
||||||
|
|
||||||
function_body_length: 60
|
function_body_length: 60
|
||||||
type_body_length: 400
|
type_body_length: 400
|
||||||
|
|
||||||
custom_rules:
|
custom_rules:
|
||||||
rule_id:
|
rule_id:
|
||||||
included: Source/SwiftLintFramework/Rules/.+/\w+\.swift
|
included: Source/SwiftLintBuiltInRules/Rules/.+/\w+\.swift
|
||||||
name: Rule ID
|
name: Rule ID
|
||||||
message: Rule IDs must be all lowercase, snake case and not end with `rule`
|
message: Rule IDs must be all lowercase, snake case and not end with `rule`
|
||||||
regex: identifier:\s*("\w+_rule"|"\S*[^a-z_]\S*")
|
regex: ^\s+identifier:\s*("\w+_rule"|"\S*[^a-z_]\S*")
|
||||||
severity: error
|
severity: error
|
||||||
fatal_error:
|
fatal_error:
|
||||||
name: Fatal Error
|
name: Fatal Error
|
||||||
|
@ -117,3 +105,4 @@ custom_rules:
|
||||||
unused_import:
|
unused_import:
|
||||||
always_keep_imports:
|
always_keep_imports:
|
||||||
- SwiftSyntaxBuilder # we can't detect uses of string interpolation of swift syntax nodes
|
- SwiftSyntaxBuilder # we can't detect uses of string interpolation of swift syntax nodes
|
||||||
|
- SwiftLintFramework # now that this is a wrapper around other modules, don't treat as unused
|
||||||
|
|
76
BUILD
76
BUILD
|
@ -5,29 +5,68 @@ load(
|
||||||
"swift_library",
|
"swift_library",
|
||||||
)
|
)
|
||||||
load(
|
load(
|
||||||
"@com_github_buildbuddy_io_rules_xcodeproj//xcodeproj:defs.bzl",
|
"@rules_xcodeproj//xcodeproj:defs.bzl",
|
||||||
"xcode_schemes",
|
"xcode_schemes",
|
||||||
"xcodeproj",
|
"xcodeproj",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Targets
|
# Targets
|
||||||
|
|
||||||
|
swift_library(
|
||||||
|
name = "SwiftLintCore",
|
||||||
|
srcs = glob(["Source/SwiftLintCore/**/*.swift"]),
|
||||||
|
module_name = "SwiftLintCore",
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = [
|
||||||
|
"@SwiftSyntax//:SwiftIDEUtils_opt",
|
||||||
|
"@SwiftSyntax//:SwiftOperators_opt",
|
||||||
|
"@SwiftSyntax//:SwiftParserDiagnostics_opt",
|
||||||
|
"@SwiftSyntax//:SwiftSyntaxBuilder_opt",
|
||||||
|
"@SwiftSyntax//:SwiftSyntax_opt",
|
||||||
|
"@com_github_jpsim_sourcekitten//:SourceKittenFramework",
|
||||||
|
"@sourcekitten_com_github_jpsim_yams//:Yams",
|
||||||
|
"@swiftlint_com_github_scottrhoyt_swifty_text_table//:SwiftyTextTable",
|
||||||
|
] + select({
|
||||||
|
"@platforms//os:linux": ["@com_github_krzyzanowskim_cryptoswift//:CryptoSwift"],
|
||||||
|
"//conditions:default": [":DyldWarningWorkaround"],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
swift_library(
|
||||||
|
name = "SwiftLintBuiltInRules",
|
||||||
|
srcs = glob(["Source/SwiftLintBuiltInRules/**/*.swift"]),
|
||||||
|
module_name = "SwiftLintBuiltInRules",
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = [
|
||||||
|
":SwiftLintCore",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
swift_library(
|
||||||
|
name = "SwiftLintExtraRules",
|
||||||
|
srcs = [
|
||||||
|
"Source/SwiftLintExtraRules/Exports.swift",
|
||||||
|
"@swiftlint_extra_rules//:extra_rules",
|
||||||
|
],
|
||||||
|
module_name = "SwiftLintExtraRules",
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = [
|
||||||
|
":SwiftLintCore",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
swift_library(
|
swift_library(
|
||||||
name = "SwiftLintFramework",
|
name = "SwiftLintFramework",
|
||||||
srcs = glob(
|
srcs = glob(
|
||||||
["Source/SwiftLintFramework/**/*.swift"],
|
["Source/SwiftLintFramework/**/*.swift"],
|
||||||
exclude = ["Source/SwiftLintFramework/Rules/ExcludedFromBazel/ExtraRules.swift"],
|
),
|
||||||
) + ["@swiftlint_extra_rules//:extra_rules"],
|
|
||||||
module_name = "SwiftLintFramework",
|
module_name = "SwiftLintFramework",
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
deps = [
|
deps = [
|
||||||
"@com_github_jpsim_sourcekitten//:SourceKittenFramework",
|
":SwiftLintBuiltInRules",
|
||||||
"@com_github_apple_swift_syntax//:optlibs",
|
":SwiftLintCore",
|
||||||
"@sourcekitten_com_github_jpsim_yams//:Yams",
|
":SwiftLintExtraRules",
|
||||||
] + select({
|
],
|
||||||
"@platforms//os:linux": ["@com_github_krzyzanowskim_cryptoswift//:CryptoSwift"],
|
|
||||||
"//conditions:default": [],
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
swift_library(
|
swift_library(
|
||||||
|
@ -63,6 +102,21 @@ apple_universal_binary(
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "DyldWarningWorkaroundSources",
|
||||||
|
srcs = [
|
||||||
|
"Source/DyldWarningWorkaround/DyldWarningWorkaround.c",
|
||||||
|
"Source/DyldWarningWorkaround/include/objc_dupclass.h",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "DyldWarningWorkaround",
|
||||||
|
srcs = ["//:DyldWarningWorkaroundSources"],
|
||||||
|
includes = ["Source/DyldWarningWorkaround/include"],
|
||||||
|
alwayslink = True,
|
||||||
|
)
|
||||||
|
|
||||||
# Linting
|
# Linting
|
||||||
|
|
||||||
filegroup(
|
filegroup(
|
||||||
|
@ -81,6 +135,8 @@ filegroup(
|
||||||
srcs = [
|
srcs = [
|
||||||
"BUILD",
|
"BUILD",
|
||||||
"LICENSE",
|
"LICENSE",
|
||||||
|
"MODULE.bazel",
|
||||||
|
"//:DyldWarningWorkaroundSources",
|
||||||
"//:LintInputs",
|
"//:LintInputs",
|
||||||
"//Tests:BUILD",
|
"//Tests:BUILD",
|
||||||
"//bazel:release_files",
|
"//bazel:release_files",
|
||||||
|
|
494
CHANGELOG.md
494
CHANGELOG.md
|
@ -1,3 +1,497 @@
|
||||||
|
## Main
|
||||||
|
|
||||||
|
#### Breaking
|
||||||
|
|
||||||
|
* None.
|
||||||
|
|
||||||
|
#### Experimental
|
||||||
|
|
||||||
|
* None.
|
||||||
|
|
||||||
|
#### Enhancements
|
||||||
|
|
||||||
|
* Mention a rule's identifier in the console message that is printed when the
|
||||||
|
rule's associated configuration entry contains invalid values.
|
||||||
|
[SimplyDanny](https://github.com/SimplyDanny)
|
||||||
|
|
||||||
|
* Silence `xct_specific_matcher` rule on "one argument asserts" if there are
|
||||||
|
potential types or tuples involved in the comparison as types and tuples do
|
||||||
|
not conform to `Equatable`.
|
||||||
|
[SimplyDanny](https://github.com/SimplyDanny)
|
||||||
|
[#4990](https://github.com/realm/SwiftLint/issues/4990)
|
||||||
|
|
||||||
|
* Add `grouping` option to the `sorted_imports` rule allowing
|
||||||
|
to sort groups of imports defined by their preceding attributes
|
||||||
|
(e.g. `@testable`, `@_exported`, ...).
|
||||||
|
[hiltonc](https://github.com/hiltonc)
|
||||||
|
|
||||||
|
* Do not trigger `redundant_self_in_closure` rule when another idenfier `x` in
|
||||||
|
scope shadows the field accessed by `self.x` to avoid semantical changes.
|
||||||
|
[SimplyDanny](https://github.com/SimplyDanny)
|
||||||
|
[#5010](https://github.com/realm/SwiftLint/issues/5010)
|
||||||
|
|
||||||
|
#### Bug Fixes
|
||||||
|
|
||||||
|
* The option `validates_start_with_lowercase` can now be disabled by setting it
|
||||||
|
to `off`.
|
||||||
|
[SimplyDanny](https://github.com/SimplyDanny)
|
||||||
|
[#5036](https://github.com/realm/SwiftLint/issues/5036)
|
||||||
|
|
||||||
|
* Do not trigger `prefer_self_in_static_references` rule on `typealias`
|
||||||
|
declarations in classes.
|
||||||
|
[SimplyDanny](https://github.com/SimplyDanny)
|
||||||
|
[#5009](https://github.com/realm/SwiftLint/issues/5009)
|
||||||
|
|
||||||
|
* Do not trigger `prefer_self_in_static_references` rule on collection types in
|
||||||
|
classes, but on initializers like `[C]()` in all types.
|
||||||
|
[SimplyDanny](https://github.com/SimplyDanny)
|
||||||
|
[#5042](https://github.com/realm/SwiftLint/issues/5042)
|
||||||
|
|
||||||
|
* Fix false positives on `redundant_objc_attribute` rule for enums
|
||||||
|
and private members.
|
||||||
|
[Martin Redington](https://github.com/mildm8nnered)
|
||||||
|
[#4633](https://github.com/realm/SwiftLint/issues/4633)
|
||||||
|
|
||||||
|
* Fix autocorrect for `CGIntersectionRect` in `legacy_cggeometry_functions`
|
||||||
|
rule.
|
||||||
|
[Haoocen](https://github.com/Haoocen)
|
||||||
|
[#5023](https://github.com/realm/SwiftLint/pull/5023)
|
||||||
|
|
||||||
|
* Fix false positives on `sorted_first_last` rule when `first`/`last` have
|
||||||
|
a predicate.
|
||||||
|
[woxtu](https://github.com/woxtu)
|
||||||
|
[#3023](https://github.com/realm/SwiftLint/issues/3023)
|
||||||
|
|
||||||
|
## 0.52.2: Crisper Clearer Pleats
|
||||||
|
|
||||||
|
#### Breaking
|
||||||
|
|
||||||
|
* None.
|
||||||
|
|
||||||
|
#### Experimental
|
||||||
|
|
||||||
|
* None.
|
||||||
|
|
||||||
|
#### Enhancements
|
||||||
|
|
||||||
|
* Exclude simple assignments of the form `self.x = x` from being reported by
|
||||||
|
the `redundant_self_in_closure` rule.
|
||||||
|
[SimplyDanny](https://github.com/SimplyDanny)
|
||||||
|
[#4988](https://github.com/realm/SwiftLint/issues/4988)
|
||||||
|
|
||||||
|
#### Bug Fixes
|
||||||
|
|
||||||
|
* Make `unhandled_throwing_task` opt-in instead of enabled by default. The rule
|
||||||
|
is still prone to false positives at this point, so this makes enabling the
|
||||||
|
rule a conscious decision by end-users.
|
||||||
|
[JP Simard](https://github.com/jpsim)
|
||||||
|
[#4987](https://github.com/realm/SwiftLint/issues/4987)
|
||||||
|
|
||||||
|
* Fix `unhandled_throwing_task` false positives when the `Task` is returned or
|
||||||
|
where the throwing code is handled in a `Result` initializer.
|
||||||
|
[JP Simard](https://github.com/jpsim)
|
||||||
|
[#4987](https://github.com/realm/SwiftLint/issues/4987)
|
||||||
|
|
||||||
|
## 0.52.1: Crisp Clear Pleats
|
||||||
|
|
||||||
|
#### Breaking
|
||||||
|
|
||||||
|
* None.
|
||||||
|
|
||||||
|
#### Experimental
|
||||||
|
|
||||||
|
* None.
|
||||||
|
|
||||||
|
#### Enhancements
|
||||||
|
|
||||||
|
* None.
|
||||||
|
|
||||||
|
#### Bug Fixes
|
||||||
|
|
||||||
|
* Let the `validates_start_with_lowercase` option in name configurations
|
||||||
|
expect a severity (warning or error). Not setting it disables the check.
|
||||||
|
Boolean values are now deprecated. A `true` value enables the check as an
|
||||||
|
error for the time being to keep the previous behavior.
|
||||||
|
[SimplyDanny](https://github.com/SimplyDanny)
|
||||||
|
[#2180](https://github.com/realm/SwiftLint/issues/2180)
|
||||||
|
|
||||||
|
* Fixed a false positive in `unhandled_throwing_task`.
|
||||||
|
[kylebshr](https://github.com/kylebshr)
|
||||||
|
[#4984](https://github.com/realm/SwiftLint/issues/4984)
|
||||||
|
|
||||||
|
* Fix Bazel release tarball for compiling on macOS.
|
||||||
|
[JP Simard](https://github.com/jpsim)
|
||||||
|
[#4985](https://github.com/realm/SwiftLint/issues/4985)
|
||||||
|
|
||||||
|
## 0.52.0: Crisp Clear Pleats
|
||||||
|
|
||||||
|
#### Breaking
|
||||||
|
|
||||||
|
* The `attributes` rule now expects attributes with arguments to be placed
|
||||||
|
on their own line above the declaration they are supposed to influence.
|
||||||
|
This applies to attributes with any kinds of arguments including single
|
||||||
|
key path arguments which were previously handled in a different way. This
|
||||||
|
behavior can be turned off by setting `attributes_with_arguments_always_on_line_above`
|
||||||
|
to `false.
|
||||||
|
[SimplyDanny](https://github.com/SimplyDanny)
|
||||||
|
[#4843](https://github.com/realm/SwiftLint/issues/4843)
|
||||||
|
|
||||||
|
* The internal module structure for SwiftLint has changed to split the
|
||||||
|
monolithic `SwiftLintFramework` into new `SwiftLintCore` for core linter
|
||||||
|
infrastructure, `SwiftLintBuiltInRules` for built-in rules and
|
||||||
|
`SwiftLintExtraRules` to add your own native rules to SwiftLint.
|
||||||
|
[JP Simard](https://github.com/jpsim)
|
||||||
|
|
||||||
|
#### Experimental
|
||||||
|
|
||||||
|
* None.
|
||||||
|
|
||||||
|
#### Enhancements
|
||||||
|
|
||||||
|
* Add new `superfluous_else` rule that triggers on `if`-statements when an
|
||||||
|
attached `else`-block can be removed, because all branches of the previous
|
||||||
|
`if`-block(s) would certainly exit the current scope already.
|
||||||
|
[SimplyDanny](https://github.com/SimplyDanny)
|
||||||
|
|
||||||
|
* Add `sorted_enum_cases` rule which warns when enum cases are not sorted.
|
||||||
|
[kimdv](https://github.com/kimdv)
|
||||||
|
|
||||||
|
* Add new `redundant_self_in_closure` rule that triggers in closures on
|
||||||
|
explicitly used `self` when it's actually not needed due to:
|
||||||
|
* Strongly captured `self` (`{ [self] in ... }`)
|
||||||
|
* Closure used in a struct declaration (`self` can always be omitted)
|
||||||
|
* Anonymous closures that are directly called (`{ ... }()`) as they are
|
||||||
|
definitly not escaping
|
||||||
|
* Weakly captured `self` with explicit unwrapping
|
||||||
|
|
||||||
|
[SimplyDanny](https://github.com/SimplyDanny)
|
||||||
|
[#59](https://github.com/realm/SwiftLint/issues/59)
|
||||||
|
|
||||||
|
* Extend `xct_specific_matcher` rule to check for boolean asserts on (un)equal
|
||||||
|
comparisons. The rule can be configured with the matchers that should trigger
|
||||||
|
rule violations. By default, all matchers trigger, but that can be limited to
|
||||||
|
just `one-argument-asserts` or `two-argument-asserts`.
|
||||||
|
[SimplyDanny](https://github.com/SimplyDanny)
|
||||||
|
[JP Simard](https://github.com/jpsim)
|
||||||
|
[#3726](https://github.com/realm/SwiftLint/issues/3726)
|
||||||
|
|
||||||
|
* Trigger `prefer_self_in_static_references` rule on more type references.
|
||||||
|
[SimplyDanny](https://github.com/SimplyDanny)
|
||||||
|
|
||||||
|
* Adds a new `reporters` command, to improve discoverability of reporters.
|
||||||
|
[Martin Redington](https://github.com/mildm8nnered)
|
||||||
|
[#4819](https://github.com/realm/SwiftLint/issues/4819)
|
||||||
|
|
||||||
|
* Adds `test_parent_classes` option to the `no_magic_numbers` rule.
|
||||||
|
Violations within test classes will now be ignored by default.
|
||||||
|
[Martin Redington](https://github.com/mildm8nnered)
|
||||||
|
[#4896](https://github.com/realm/SwiftLint/issues/4896)
|
||||||
|
|
||||||
|
* Stop enforcing calls to super from the override functions `setUp()`,
|
||||||
|
`tearDown()`, `setUpWithError()`, and `tearDownWithError()` in `XCTestCase`
|
||||||
|
subclasses.
|
||||||
|
[AndrewDMontgomery](https://github.com/andrewdmontgomery)
|
||||||
|
[#4875](https://github.com/realm/SwiftLint/pull/4875)
|
||||||
|
|
||||||
|
* Prepend `warning: ` to error messages so that they show in Xcode.
|
||||||
|
[whiteio](https://github.com/whiteio)
|
||||||
|
[#4923](https://github.com/realm/SwiftLint/issues/4923)
|
||||||
|
|
||||||
|
* The `attributes` rule received a new boolean option
|
||||||
|
`attributes_with_arguments_always_on_line_above` which is `true` by default.
|
||||||
|
Setting it to `false` ensures that attributes with arguments like
|
||||||
|
`@Persisted(primaryKey: true)` don't violate the rule if they are on the same
|
||||||
|
line with the variable declaration.
|
||||||
|
[SimplyDanny](https://github.com/SimplyDanny)
|
||||||
|
[#4843](https://github.com/realm/SwiftLint/issues/4843)
|
||||||
|
|
||||||
|
* Add new `unhandled_throwing_task` rule that triggers when a Task with an
|
||||||
|
implicit error type has unhandled trys or errors thrown inside its body.
|
||||||
|
This results in errors being silently discarded, which may be unexpected.
|
||||||
|
See this forum thread for more details: https://forums.swift.org/t/56066
|
||||||
|
[kylebshr](https://github.com/kylebshr)
|
||||||
|
|
||||||
|
#### Bug Fixes
|
||||||
|
|
||||||
|
* Fix `lower_acl_than_parent` rule rewriter by preserving leading whitespace.
|
||||||
|
[SimplyDanny](https://github.com/SimplyDanny)
|
||||||
|
[#4860](https://github.com/realm/SwiftLint/issues/4860)
|
||||||
|
|
||||||
|
* Ignore block comments in `let_var_whitespace` rule.
|
||||||
|
[SimplyDanny](https://github.com/SimplyDanny)
|
||||||
|
[#4871](https://github.com/realm/SwiftLint/issues/4871)
|
||||||
|
|
||||||
|
* Fix false positives in `indentation_width` rule.
|
||||||
|
[Sven Münnich](https://github.com/svenmuennich)
|
||||||
|
|
||||||
|
* Do not trigger `reduce_boolean` on `reduce` methods with a first named
|
||||||
|
argument that is different from `into`.
|
||||||
|
[SimplyDanny](https://github.com/SimplyDanny)
|
||||||
|
[#4894](https://github.com/realm/SwiftLint/issues/4894)
|
||||||
|
|
||||||
|
* Work around dyld warning about duplicate SwiftSyntax classes.
|
||||||
|
[keith](https://github.com/keith)
|
||||||
|
[#4782](https://github.com/realm/SwiftLint/issues/4782)
|
||||||
|
|
||||||
|
* Improve lint times of SwiftLintPlugin by moving the
|
||||||
|
`excludedPaths(fileManager:)` operation out of the linting iterations.
|
||||||
|
[andyyhope](https://github.com/andyyhope)
|
||||||
|
[#4844](https://github.com/realm/SwiftLint/issues/4844)
|
||||||
|
|
||||||
|
## 0.51.0: bzllint
|
||||||
|
|
||||||
|
#### Breaking
|
||||||
|
|
||||||
|
* Deprecate the `unused_capture_list` rule in favor of the Swift compiler
|
||||||
|
warning. At the same time, make it an opt-in rule.
|
||||||
|
[Cyberbeni](https://github.com/Cyberbeni)
|
||||||
|
[#4656](https://github.com/realm/SwiftLint/issues/4656)
|
||||||
|
|
||||||
|
* Deprecate the `inert_defer` rule in favor of the Swift compiler warning.
|
||||||
|
At the same time, make it an opt-in rule.
|
||||||
|
[SimplyDanny](https://github.com/SimplyDanny)
|
||||||
|
[#4615](https://github.com/realm/SwiftLint/issues/4615)
|
||||||
|
|
||||||
|
* Interpret strings in `excluded` option of `identifier_name`,
|
||||||
|
`type_name` and `generic_type_name` rules as regular expression. Existing
|
||||||
|
configurations should remain working without notice as long as they don't
|
||||||
|
contain characters that must be escaped in regular expression.
|
||||||
|
[Moly](https://github.com/kyounh12)
|
||||||
|
[#4655](https://github.com/realm/SwiftLint/pull/4655)
|
||||||
|
|
||||||
|
#### Experimental
|
||||||
|
|
||||||
|
* None.
|
||||||
|
|
||||||
|
#### Enhancements
|
||||||
|
|
||||||
|
* Add `duplicate_conditions` rule which warns when a condition is duplicated
|
||||||
|
in separate branches of the same branching statement (if-else, or switch).
|
||||||
|
[1in1](https://github.com/1in1)
|
||||||
|
[#4666](https://github.com/realm/SwiftLint/issues/4666)
|
||||||
|
|
||||||
|
* Add local links to rule descriptions to every rule listed
|
||||||
|
in `Rule Directory.md`.
|
||||||
|
[kattouf](https://github.com/kattouf)
|
||||||
|
|
||||||
|
* Make forceExclude work with directly specified files.
|
||||||
|
[jimmya](https://github.com/jimmya)
|
||||||
|
[#4609](https://github.com/realm/SwiftLint/issues/4609)
|
||||||
|
|
||||||
|
* Adds `all` pseudo-rule for `opt_in_rules` - enables all opt in rules
|
||||||
|
that are not listed in `disabled_rules`
|
||||||
|
[Martin Redington](https://github.com/mildm8nnered)
|
||||||
|
[#4540](https://github.com/realm/SwiftLint/issues/4540)
|
||||||
|
|
||||||
|
* Separate analyzer rules as an independent section in the rule directory of
|
||||||
|
the reference.
|
||||||
|
[Ethan Wong](https://github.com/GetToSet)
|
||||||
|
[#4664](https://github.com/realm/SwiftLint/pull/4664)
|
||||||
|
|
||||||
|
* Add rule identifier to output of Emoji reporter.
|
||||||
|
[SimplyDanny](https://github.com/SimplyDanny)
|
||||||
|
[#4707](https://github.com/realm/SwiftLint/issues/4707)
|
||||||
|
|
||||||
|
* Add new `direct_return` rule that triggers on `return` statements returning
|
||||||
|
variables that have been declared in the statement before only.
|
||||||
|
[SimplyDanny](https://github.com/SimplyDanny)
|
||||||
|
|
||||||
|
* Add `period_spacing` opt-in rule that checks periods are not followed
|
||||||
|
by 2 or more spaces in comments.
|
||||||
|
[Julioacarrettoni](https://github.com/Julioacarrettoni)
|
||||||
|
[#4624](https://github.com/realm/SwiftLint/pull/4624)
|
||||||
|
|
||||||
|
* Allow to pass a rule identifier to the `swiftlint docs` command to open its
|
||||||
|
specific documentation website, e.g. `swiftlint docs for_where`.
|
||||||
|
[SimplyDanny](https://github.com/SimplyDanny)
|
||||||
|
[#4707](https://github.com/realm/SwiftLint/issues/4707)
|
||||||
|
|
||||||
|
* Allow new Quick APIs `aroundEach` and `justBeforeEach` for
|
||||||
|
`quick_discouraged_call`.
|
||||||
|
[David Steinacher](https://github.com/stonko1994)
|
||||||
|
[#4626](https://github.com/realm/SwiftLint/issues/4626)
|
||||||
|
|
||||||
|
* Add `relative-path` reporter to generate reports with relative file paths.
|
||||||
|
[Roya1v](https://github.com/roya1v)
|
||||||
|
[#4660](https://github.com/realm/SwiftLint/issues/4660)
|
||||||
|
|
||||||
|
* Let `number_separator` rule trigger on misplaced separators, e.g. `10_00`.
|
||||||
|
[SimplyDanny](https://github.com/SimplyDanny)
|
||||||
|
[#4637](https://github.com/realm/SwiftLint/issues/4637)
|
||||||
|
|
||||||
|
* Rewrite `multiline_arguments` rule using SwiftSyntax, ignoring trailing
|
||||||
|
closures.
|
||||||
|
[Marcelo Fabri](https://github.com/marcelofabri)
|
||||||
|
[#3399](https://github.com/realm/SwiftLint/issues/3399)
|
||||||
|
[#3605](https://github.com/realm/SwiftLint/issues/3605)
|
||||||
|
|
||||||
|
* Speed up linting by up to 6% updating to use a newer version of
|
||||||
|
`SwiftSyntax`.
|
||||||
|
[JP Simard](https://github.com/jpsim)
|
||||||
|
|
||||||
|
* Catch more valid `legacy_multiple` violations.
|
||||||
|
[JP Simard](https://github.com/jpsim)
|
||||||
|
|
||||||
|
* Catch more valid `no_magic_numbers` violations.
|
||||||
|
[JP Simard](https://github.com/jpsim)
|
||||||
|
|
||||||
|
* Add `blanket_disable_command` rule that checks whether
|
||||||
|
rules are re-enabled after being disabled.
|
||||||
|
[Martin Redington](https://github.com/mildm8nnered)
|
||||||
|
[#4731](https://github.com/realm/SwiftLint/pull/4731)
|
||||||
|
|
||||||
|
* Add `invalid_swiftlint_command` rule that validates
|
||||||
|
`// swiftlint:enable` and `disable` commands.
|
||||||
|
[Martin Redington](https://github.com/mildm8nnered)
|
||||||
|
[#4546](https://github.com/realm/SwiftLint/pull/4546)
|
||||||
|
|
||||||
|
* Improve `identifier_name` documentation.
|
||||||
|
[Martin Redington](https://github.com/mildm8nnered)
|
||||||
|
[#4767](https://github.com/realm/SwiftLint/issues/4767)
|
||||||
|
|
||||||
|
* Adds `include_multiline_strings` option to `indentation_width` rule.
|
||||||
|
[Martin Redington](https://github.com/mildm8nnered)
|
||||||
|
[#4248](https://github.com/realm/SwiftLint/issues/4248)
|
||||||
|
|
||||||
|
* Adds a new `summary` reporter, that displays the number of violations
|
||||||
|
of each rule in a text table.
|
||||||
|
[Martin Redington](https://github.com/mildm8nnered)
|
||||||
|
|
||||||
|
#### Bug Fixes
|
||||||
|
|
||||||
|
* Report violations in all `<scope>_length` rules when the error threshold is
|
||||||
|
smaller than the warning threshold.
|
||||||
|
[SimplyDanny](https://github.com/SimplyDanny)
|
||||||
|
[#4645](https://github.com/realm/SwiftLint/issues/4645)
|
||||||
|
|
||||||
|
* Consider custom attributes in `attributes` rule.
|
||||||
|
[SimplyDanny](https://github.com/SimplyDanny)
|
||||||
|
[#4599](https://github.com/realm/SwiftLint/issues/4599)
|
||||||
|
|
||||||
|
* Fix whitespaces issue in auto-fix of `redundant_optional_initialization`
|
||||||
|
rule when multiple variable declaration are involved.
|
||||||
|
[SimplyDanny](https://github.com/SimplyDanny)
|
||||||
|
[#4794](https://github.com/realm/SwiftLint/issues/4794)
|
||||||
|
|
||||||
|
* Stop triggering `strict_fileprivate` rule on symbols implementing a protocol
|
||||||
|
in the same file.
|
||||||
|
[SimplyDanny](https://github.com/SimplyDanny)
|
||||||
|
[#4692](https://github.com/realm/SwiftLint/issues/4692)
|
||||||
|
|
||||||
|
* Fix false positives on `private_subject` rule when using
|
||||||
|
subjects inside functions.
|
||||||
|
[Marcelo Fabri](https://github.com/marcelofabri)
|
||||||
|
[#4643](https://github.com/realm/SwiftLint/issues/4643)
|
||||||
|
|
||||||
|
* Fix for compiler directives masking subsequent `opening_brace`
|
||||||
|
violations.
|
||||||
|
[Martin Redington](https://github.com/mildm8nnered)
|
||||||
|
[#3712](https://github.com/realm/SwiftLint/issues/3712)
|
||||||
|
|
||||||
|
* Rewrite `explicit_type_interface` rule with SwiftSyntax fixing a
|
||||||
|
false-positive in if-case-let statements.
|
||||||
|
[SimplyDanny](https://github.com/SimplyDanny)
|
||||||
|
[#4548](https://github.com/realm/SwiftLint/issues/4548)
|
||||||
|
|
||||||
|
* Stop triggering `unused_capture_list` on captured variable that is only
|
||||||
|
referenced by a shorthand optional binding (`if let capturedVar { ... }`).
|
||||||
|
[SimplyDanny](https://github.com/SimplyDanny)
|
||||||
|
[#4804](https://github.com/realm/SwiftLint/issues/4804)
|
||||||
|
|
||||||
|
* Ensure that negative literals in initializers do not trigger
|
||||||
|
`no_magic_numbers` rule.
|
||||||
|
[SimplyDanny](https://github.com/SimplyDanny)
|
||||||
|
[#4677](https://github.com/realm/SwiftLint/issues/4677)
|
||||||
|
|
||||||
|
* Fix caching of `indentation_width` rule.
|
||||||
|
[SimplyDanny](https://github.com/SimplyDanny)
|
||||||
|
[#4121](https://github.com/realm/SwiftLint/issues/4121)
|
||||||
|
|
||||||
|
* Updated JUnit reporter to output error count and warning count.
|
||||||
|
[patricks](https://github.com/patricks)
|
||||||
|
[#4725](https://github.com/realm/SwiftLint/pull/4725)
|
||||||
|
|
||||||
|
* Fix correction on `lower_acl_than_parent` rule for `open` declarations.
|
||||||
|
[Marcelo Fabri](https://github.com/marcelofabri)
|
||||||
|
[#4753](https://github.com/realm/SwiftLint/issues/4753)
|
||||||
|
|
||||||
|
* Fix `void_return` rule to support async and async throws functions.
|
||||||
|
[Mathias Schreck](https://github.com/lo1tuma)
|
||||||
|
[#4772](https://github.com/realm/SwiftLint/issues/4772)
|
||||||
|
|
||||||
|
* Fix false positives in `attributes` rule when using property wrappers
|
||||||
|
with keypath arguments.
|
||||||
|
[JP Simard](https://github.com/jpsim)
|
||||||
|
|
||||||
|
* Fix for `superfluous_disable_command` not being completely disabled
|
||||||
|
by `disable` commands.
|
||||||
|
[Martin Redington](https://github.com/mildm8nnered)
|
||||||
|
[#4788](https://github.com/realm/SwiftLint/issues/4788)
|
||||||
|
|
||||||
|
* Fixed correction for `trailing_comma` rule wrongly removing trailing
|
||||||
|
comments.
|
||||||
|
[Martin Redington](https://github.com/mildm8nnered)
|
||||||
|
[#4814](https://github.com/realm/SwiftLint/issues/4814)
|
||||||
|
|
||||||
|
## 0.50.3: Bundle of Towels
|
||||||
|
|
||||||
|
#### Breaking
|
||||||
|
|
||||||
|
* None.
|
||||||
|
|
||||||
|
#### Experimental
|
||||||
|
|
||||||
|
* None.
|
||||||
|
|
||||||
|
#### Enhancements
|
||||||
|
|
||||||
|
* The `SwiftLintPlugin` SwiftPM plugin now uses a prebuilt binary on
|
||||||
|
macOS.
|
||||||
|
[Tony Arnold](https://github.com/tonyarnold)
|
||||||
|
[JP Simard](https://github.com/jpsim)
|
||||||
|
[#4558](https://github.com/realm/SwiftLint/issues/4558)
|
||||||
|
|
||||||
|
* Don't trigger `shorthand_operator` violations inside a shorthand
|
||||||
|
operator function declaration.
|
||||||
|
[Marcelo Fabri](https://github.com/marcelofabri)
|
||||||
|
[#4611](https://github.com/realm/SwiftLint/issues/4611)
|
||||||
|
|
||||||
|
* The `balanced_xctest_lifecycle`, `single_test_class`,
|
||||||
|
`empty_xctest_method` and `test_case_accessibility` rules will now be
|
||||||
|
applied to subclasses of `QuickSpec`, as well as `XCTestCase`, by
|
||||||
|
default.
|
||||||
|
[Martin Redington](https://github.com/mildm8nnered)
|
||||||
|
|
||||||
|
* Add `test_parent_classes` option to `balanced_xctest_lifecycle`,
|
||||||
|
`single_test_class` and `empty_xctest_method` rules.
|
||||||
|
[Martin Redington](https://github.com/mildm8nnered)
|
||||||
|
[#4200](https://github.com/realm/SwiftLint/issues/4200)
|
||||||
|
|
||||||
|
* Show warnings in the console for Analyzer rules that are listed in the
|
||||||
|
`opt_in_rules` configuration section.
|
||||||
|
[SimplyDanny](https://github.com/SimplyDanny)
|
||||||
|
[#4612](https://github.com/realm/SwiftLint/issues/4612)
|
||||||
|
|
||||||
|
#### Bug Fixes
|
||||||
|
|
||||||
|
* Fix configuration parsing error in `unused_declaration` rule.
|
||||||
|
[SimplyDanny](https://github.com/SimplyDanny)
|
||||||
|
[#4612](https://github.com/realm/SwiftLint/issues/4612)
|
||||||
|
|
||||||
|
* Skip `defer` statements being last in an `#if` block if the `#if`
|
||||||
|
statement is not itself the last statement in a block.
|
||||||
|
[SimplyDanny](https://github.com/SimplyDanny)
|
||||||
|
[#4615](https://github.com/realm/SwiftLint/issues/4615)
|
||||||
|
|
||||||
|
* Fix false positives in `empty_enum_arguments` when the called
|
||||||
|
expression is an identifier or an init call.
|
||||||
|
[Steffen Matthischke](https://github.com/heeaad)
|
||||||
|
[#4597](https://github.com/realm/SwiftLint/issues/4597)
|
||||||
|
|
||||||
|
* Fix correction issue in `comma` when there was too much whitespace
|
||||||
|
following the comma.
|
||||||
|
[JP Simard](https://github.com/jpsim)
|
||||||
|
|
||||||
## 0.50.1: Artisanal Clothes Pegs Fixup Edition
|
## 0.50.1: Artisanal Clothes Pegs Fixup Edition
|
||||||
|
|
||||||
#### Breaking
|
#### Breaking
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
## Tutorial
|
||||||
|
|
||||||
|
If you'd like to write a SwiftLint rule but aren't sure how to start,
|
||||||
|
please watch and follow along with
|
||||||
|
[this video tutorial](https://vimeo.com/819268038).
|
||||||
|
|
||||||
## Pull Requests
|
## Pull Requests
|
||||||
|
|
||||||
All changes, no matter how trivial, must be done via pull request. Commits
|
All changes, no matter how trivial, must be done via pull request. Commits
|
||||||
|
@ -58,7 +64,7 @@ $ make docker_test
|
||||||
|
|
||||||
## Rules
|
## Rules
|
||||||
|
|
||||||
New rules should be added in the `Source/SwiftLintFramework/Rules` directory.
|
New rules should be added in the `Source/SwiftLintBuiltInRules/Rules` directory.
|
||||||
|
|
||||||
Rules should conform to either the `Rule` or `ASTRule` protocols.
|
Rules should conform to either the `Rule` or `ASTRule` protocols.
|
||||||
|
|
||||||
|
@ -98,11 +104,11 @@ configuration object via the `configuration` property:
|
||||||
* If none of the provided `RuleConfiguration`s are applicable, you can create one
|
* If none of the provided `RuleConfiguration`s are applicable, you can create one
|
||||||
specifically for your rule.
|
specifically for your rule.
|
||||||
|
|
||||||
See [`ForceCastRule`](https://github.com/realm/SwiftLint/blob/main/Source/SwiftLintFramework/Rules/Idiomatic/ForceCastRule.swift)
|
See [`ForceCastRule`](https://github.com/realm/SwiftLint/blob/main/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ForceCastRule.swift)
|
||||||
for a rule that allows severity configuration,
|
for a rule that allows severity configuration,
|
||||||
[`FileLengthRule`](https://github.com/realm/SwiftLint/blob/main/Source/SwiftLintFramework/Rules/Metrics/FileLengthRule.swift)
|
[`FileLengthRule`](https://github.com/realm/SwiftLint/blob/main/Source/SwiftLintBuiltInRules/Rules/Metrics/FileLengthRule.swift)
|
||||||
for a rule that has multiple severity levels,
|
for a rule that has multiple severity levels,
|
||||||
[`IdentifierNameRule`](https://github.com/realm/SwiftLint/blob/main/Source/SwiftLintFramework/Rules/Style/IdentifierNameRule.swift)
|
[`IdentifierNameRule`](https://github.com/realm/SwiftLint/blob/main/Source/SwiftLintBuiltInRules/Rules/Style/IdentifierNameRule.swift)
|
||||||
for a rule that allows name evaluation configuration:
|
for a rule that allows name evaluation configuration:
|
||||||
|
|
||||||
``` yaml
|
``` yaml
|
||||||
|
@ -174,10 +180,5 @@ To bring up a new Buildkite worker from MacStadium:
|
||||||
`brew install aria2 bazelisk htop buildkite/buildkite/buildkite-agent robotsandpencils/made/xcodes`
|
`brew install aria2 bazelisk htop buildkite/buildkite/buildkite-agent robotsandpencils/made/xcodes`
|
||||||
1. Install latest Xcode version: `xcodes update && xcodes install 14.0.0`
|
1. Install latest Xcode version: `xcodes update && xcodes install 14.0.0`
|
||||||
1. Add `DANGER_GITHUB_API_TOKEN` and `HOME` to `/opt/homebrew/etc/buildkite-agent/hooks/environment`
|
1. Add `DANGER_GITHUB_API_TOKEN` and `HOME` to `/opt/homebrew/etc/buildkite-agent/hooks/environment`
|
||||||
1. Add `echo "build --remote_cache=grpc://<creds>@swiftlint-ci.jpsim.com:9092" > ci.bazelrc`
|
|
||||||
to `/opt/homebrew/etc/buildkite-agent/hooks/pre-command`, replacing `<creds>` with the
|
|
||||||
bazel-remote credentials
|
|
||||||
1. Configure and launch buildkite agent: `brew info buildkite-agent` /
|
1. Configure and launch buildkite agent: `brew info buildkite-agent` /
|
||||||
https://buildkite.com/organizations/swiftlint/agents#setup-macos
|
https://buildkite.com/organizations/swiftlint/agents#setup-macos
|
||||||
1. On the `swiftlint-ci.jpsim.com` machine only:
|
|
||||||
`docker run -d -u 1000:1000 -v /tmp/swiftlint-bazel-remote-cache:/data -v ~/Desktop/bazel-remote.htpasswd:/etc/bazel-remote/htpasswd -p 9090:8080 -p 9092:9092 buchgr/bazel-remote-cache --htpasswd_file=/etc/bazel-remote/htpasswd --max_size=5`
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ has_app_changes = !modified_files.grep(/Source/).empty?
|
||||||
has_test_changes = !modified_files.grep(/Tests/).empty?
|
has_test_changes = !modified_files.grep(/Tests/).empty?
|
||||||
has_danger_changes = !modified_files.grep(/Dangerfile|tools\/oss-check|Gemfile/).empty?
|
has_danger_changes = !modified_files.grep(/Dangerfile|tools\/oss-check|Gemfile/).empty?
|
||||||
has_package_changes = !modified_files.grep(/Package\.swift/).empty?
|
has_package_changes = !modified_files.grep(/Package\.swift/).empty?
|
||||||
has_bazel_changes = !modified_files.grep(/\.bazelrc|\.bazelversion|WORKSPACE|bazel\/|BUILD/).empty?
|
has_bazel_changes = !modified_files.grep(/\.bazelrc|\.bazelversion|WORKSPACE|bazel\/|BUILD|MODULE\.bazel/).empty?
|
||||||
|
|
||||||
# Add a CHANGELOG entry for app changes
|
# Add a CHANGELOG entry for app changes
|
||||||
if !modified_files.include?('CHANGELOG.md') && has_app_changes
|
if !modified_files.include?('CHANGELOG.md') && has_app_changes
|
||||||
|
|
80
Gemfile.lock
80
Gemfile.lock
|
@ -1,15 +1,14 @@
|
||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
CFPropertyList (3.0.5)
|
CFPropertyList (3.0.6)
|
||||||
rexml
|
rexml
|
||||||
activesupport (6.1.6.1)
|
activesupport (7.0.4.3)
|
||||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
i18n (>= 1.6, < 2)
|
i18n (>= 1.6, < 2)
|
||||||
minitest (>= 5.1)
|
minitest (>= 5.1)
|
||||||
tzinfo (~> 2.0)
|
tzinfo (~> 2.0)
|
||||||
zeitwerk (~> 2.3)
|
addressable (2.8.4)
|
||||||
addressable (2.8.1)
|
|
||||||
public_suffix (>= 2.0.2, < 6.0)
|
public_suffix (>= 2.0.2, < 6.0)
|
||||||
algoliasearch (1.27.5)
|
algoliasearch (1.27.5)
|
||||||
httpclient (~> 2.8, >= 2.8.3)
|
httpclient (~> 2.8, >= 2.8.3)
|
||||||
|
@ -20,15 +19,15 @@ GEM
|
||||||
cork
|
cork
|
||||||
nap
|
nap
|
||||||
open4 (~> 1.3)
|
open4 (~> 1.3)
|
||||||
cocoapods (1.11.3)
|
cocoapods (1.12.1)
|
||||||
addressable (~> 2.8)
|
addressable (~> 2.8)
|
||||||
claide (>= 1.0.2, < 2.0)
|
claide (>= 1.0.2, < 2.0)
|
||||||
cocoapods-core (= 1.11.3)
|
cocoapods-core (= 1.12.1)
|
||||||
cocoapods-deintegrate (>= 1.0.3, < 2.0)
|
cocoapods-deintegrate (>= 1.0.3, < 2.0)
|
||||||
cocoapods-downloader (>= 1.4.0, < 2.0)
|
cocoapods-downloader (>= 1.6.0, < 2.0)
|
||||||
cocoapods-plugins (>= 1.0.0, < 2.0)
|
cocoapods-plugins (>= 1.0.0, < 2.0)
|
||||||
cocoapods-search (>= 1.0.0, < 2.0)
|
cocoapods-search (>= 1.0.0, < 2.0)
|
||||||
cocoapods-trunk (>= 1.4.0, < 2.0)
|
cocoapods-trunk (>= 1.6.0, < 2.0)
|
||||||
cocoapods-try (>= 1.1.0, < 2.0)
|
cocoapods-try (>= 1.1.0, < 2.0)
|
||||||
colored2 (~> 3.1)
|
colored2 (~> 3.1)
|
||||||
escape (~> 0.0.4)
|
escape (~> 0.0.4)
|
||||||
|
@ -36,10 +35,10 @@ GEM
|
||||||
gh_inspector (~> 1.0)
|
gh_inspector (~> 1.0)
|
||||||
molinillo (~> 0.8.0)
|
molinillo (~> 0.8.0)
|
||||||
nap (~> 1.0)
|
nap (~> 1.0)
|
||||||
ruby-macho (>= 1.0, < 3.0)
|
ruby-macho (>= 2.3.0, < 3.0)
|
||||||
xcodeproj (>= 1.21.0, < 2.0)
|
xcodeproj (>= 1.21.0, < 2.0)
|
||||||
cocoapods-core (1.11.3)
|
cocoapods-core (1.12.1)
|
||||||
activesupport (>= 5.0, < 7)
|
activesupport (>= 5.0, < 8)
|
||||||
addressable (~> 2.8)
|
addressable (~> 2.8)
|
||||||
algoliasearch (~> 1.0)
|
algoliasearch (~> 1.0)
|
||||||
concurrent-ruby (~> 1.1)
|
concurrent-ruby (~> 1.1)
|
||||||
|
@ -58,61 +57,42 @@ GEM
|
||||||
netrc (~> 0.11)
|
netrc (~> 0.11)
|
||||||
cocoapods-try (1.2.0)
|
cocoapods-try (1.2.0)
|
||||||
colored2 (3.1.2)
|
colored2 (3.1.2)
|
||||||
concurrent-ruby (1.1.10)
|
concurrent-ruby (1.2.2)
|
||||||
cork (0.3.0)
|
cork (0.3.0)
|
||||||
colored2 (~> 3.1)
|
colored2 (~> 3.1)
|
||||||
danger (8.6.1)
|
danger (9.2.0)
|
||||||
claide (~> 1.0)
|
claide (~> 1.0)
|
||||||
claide-plugins (>= 0.9.2)
|
claide-plugins (>= 0.9.2)
|
||||||
colored2 (~> 3.1)
|
colored2 (~> 3.1)
|
||||||
cork (~> 0.1)
|
cork (~> 0.1)
|
||||||
faraday (>= 0.9.0, < 2.0)
|
faraday (>= 0.9.0, < 3.0)
|
||||||
faraday-http-cache (~> 2.0)
|
faraday-http-cache (~> 2.0)
|
||||||
git (~> 1.7)
|
git (~> 1.7)
|
||||||
kramdown (~> 2.3)
|
kramdown (~> 2.3)
|
||||||
kramdown-parser-gfm (~> 1.0)
|
kramdown-parser-gfm (~> 1.0)
|
||||||
no_proxy_fix
|
no_proxy_fix
|
||||||
octokit (~> 4.7)
|
octokit (~> 5.0)
|
||||||
terminal-table (>= 1, < 4)
|
terminal-table (>= 1, < 4)
|
||||||
escape (0.0.4)
|
escape (0.0.4)
|
||||||
ethon (0.15.0)
|
ethon (0.16.0)
|
||||||
ffi (>= 1.15.0)
|
ffi (>= 1.15.0)
|
||||||
faraday (1.10.2)
|
faraday (2.7.4)
|
||||||
faraday-em_http (~> 1.0)
|
faraday-net_http (>= 2.0, < 3.1)
|
||||||
faraday-em_synchrony (~> 1.0)
|
|
||||||
faraday-excon (~> 1.1)
|
|
||||||
faraday-httpclient (~> 1.0)
|
|
||||||
faraday-multipart (~> 1.0)
|
|
||||||
faraday-net_http (~> 1.0)
|
|
||||||
faraday-net_http_persistent (~> 1.0)
|
|
||||||
faraday-patron (~> 1.0)
|
|
||||||
faraday-rack (~> 1.0)
|
|
||||||
faraday-retry (~> 1.0)
|
|
||||||
ruby2_keywords (>= 0.0.4)
|
ruby2_keywords (>= 0.0.4)
|
||||||
faraday-em_http (1.0.0)
|
|
||||||
faraday-em_synchrony (1.0.0)
|
|
||||||
faraday-excon (1.1.0)
|
|
||||||
faraday-http-cache (2.4.1)
|
faraday-http-cache (2.4.1)
|
||||||
faraday (>= 0.8)
|
faraday (>= 0.8)
|
||||||
faraday-httpclient (1.0.1)
|
faraday-net_http (3.0.2)
|
||||||
faraday-multipart (1.0.4)
|
|
||||||
multipart-post (~> 2)
|
|
||||||
faraday-net_http (1.0.1)
|
|
||||||
faraday-net_http_persistent (1.2.0)
|
|
||||||
faraday-patron (1.0.0)
|
|
||||||
faraday-rack (1.0.0)
|
|
||||||
faraday-retry (1.0.3)
|
|
||||||
ffi (1.15.5)
|
ffi (1.15.5)
|
||||||
fourflusher (2.3.1)
|
fourflusher (2.3.1)
|
||||||
fuzzy_match (2.0.4)
|
fuzzy_match (2.0.4)
|
||||||
gh_inspector (1.1.3)
|
gh_inspector (1.1.3)
|
||||||
git (1.12.0)
|
git (1.18.0)
|
||||||
addressable (~> 2.8)
|
addressable (~> 2.8)
|
||||||
rchardet (~> 1.8)
|
rchardet (~> 1.8)
|
||||||
httpclient (2.8.3)
|
httpclient (2.8.3)
|
||||||
i18n (1.12.0)
|
i18n (1.12.0)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
jazzy (0.14.2)
|
jazzy (0.14.3)
|
||||||
cocoapods (~> 1.5)
|
cocoapods (~> 1.5)
|
||||||
mustache (~> 1.1)
|
mustache (~> 1.1)
|
||||||
open4 (~> 1.3)
|
open4 (~> 1.3)
|
||||||
|
@ -122,27 +102,26 @@ GEM
|
||||||
sassc (~> 2.1)
|
sassc (~> 2.1)
|
||||||
sqlite3 (~> 1.3)
|
sqlite3 (~> 1.3)
|
||||||
xcinvoke (~> 0.3.0)
|
xcinvoke (~> 0.3.0)
|
||||||
json (2.6.2)
|
json (2.6.3)
|
||||||
kramdown (2.4.0)
|
kramdown (2.4.0)
|
||||||
rexml
|
rexml
|
||||||
kramdown-parser-gfm (1.1.0)
|
kramdown-parser-gfm (1.1.0)
|
||||||
kramdown (~> 2.0)
|
kramdown (~> 2.0)
|
||||||
liferaft (0.0.6)
|
liferaft (0.0.6)
|
||||||
minitest (5.16.3)
|
minitest (5.18.0)
|
||||||
molinillo (0.8.0)
|
molinillo (0.8.0)
|
||||||
multipart-post (2.2.3)
|
|
||||||
mustache (1.1.1)
|
mustache (1.1.1)
|
||||||
nanaimo (0.3.0)
|
nanaimo (0.3.0)
|
||||||
nap (1.1.0)
|
nap (1.1.0)
|
||||||
netrc (0.11.0)
|
netrc (0.11.0)
|
||||||
no_proxy_fix (0.1.2)
|
no_proxy_fix (0.1.2)
|
||||||
octokit (4.25.1)
|
octokit (5.6.1)
|
||||||
faraday (>= 1, < 3)
|
faraday (>= 1, < 3)
|
||||||
sawyer (~> 0.9)
|
sawyer (~> 0.9)
|
||||||
open4 (1.3.4)
|
open4 (1.3.4)
|
||||||
public_suffix (4.0.7)
|
public_suffix (4.0.7)
|
||||||
rchardet (1.8.0)
|
rchardet (1.8.0)
|
||||||
redcarpet (3.5.1)
|
redcarpet (3.6.0)
|
||||||
rexml (3.2.5)
|
rexml (3.2.5)
|
||||||
rouge (3.30.0)
|
rouge (3.30.0)
|
||||||
ruby-macho (2.5.1)
|
ruby-macho (2.5.1)
|
||||||
|
@ -152,14 +131,14 @@ GEM
|
||||||
sawyer (0.9.2)
|
sawyer (0.9.2)
|
||||||
addressable (>= 2.3.5)
|
addressable (>= 2.3.5)
|
||||||
faraday (>= 0.17.3, < 3)
|
faraday (>= 0.17.3, < 3)
|
||||||
sqlite3 (1.4.4)
|
sqlite3 (1.6.2-arm64-darwin)
|
||||||
terminal-table (3.0.2)
|
terminal-table (3.0.2)
|
||||||
unicode-display_width (>= 1.1.1, < 3)
|
unicode-display_width (>= 1.1.1, < 3)
|
||||||
typhoeus (1.4.0)
|
typhoeus (1.4.0)
|
||||||
ethon (>= 0.9.0)
|
ethon (>= 0.9.0)
|
||||||
tzinfo (2.0.5)
|
tzinfo (2.0.6)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
unicode-display_width (2.2.0)
|
unicode-display_width (2.4.2)
|
||||||
xcinvoke (0.3.0)
|
xcinvoke (0.3.0)
|
||||||
liferaft (~> 0.0.6)
|
liferaft (~> 0.0.6)
|
||||||
xcodeproj (1.22.0)
|
xcodeproj (1.22.0)
|
||||||
|
@ -169,11 +148,10 @@ GEM
|
||||||
colored2 (~> 3.1)
|
colored2 (~> 3.1)
|
||||||
nanaimo (~> 0.3.0)
|
nanaimo (~> 0.3.0)
|
||||||
rexml (~> 3.2.4)
|
rexml (~> 3.2.4)
|
||||||
zeitwerk (2.6.0)
|
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
arm64-darwin-21
|
arm64-darwin-21
|
||||||
ruby
|
arm64-darwin-22
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
cocoapods
|
cocoapods
|
||||||
|
@ -181,4 +159,4 @@ DEPENDENCIES
|
||||||
jazzy
|
jazzy
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
2.3.8
|
2.4.12
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
module(
|
||||||
|
name = "swiftlint",
|
||||||
|
version = "0.52.2",
|
||||||
|
compatibility_level = 1,
|
||||||
|
repo_name = "SwiftLint",
|
||||||
|
)
|
||||||
|
|
||||||
|
bazel_dep(name = "bazel_skylib", version = "1.4.1", dev_dependency = True)
|
||||||
|
bazel_dep(name = "platforms", version = "0.0.6")
|
||||||
|
bazel_dep(name = "rules_apple", version = "2.3.0", repo_name = "build_bazel_rules_apple")
|
||||||
|
bazel_dep(name = "rules_swift", version = "1.8.0", repo_name = "build_bazel_rules_swift")
|
||||||
|
bazel_dep(name = "rules_xcodeproj", version = "1.7.0")
|
||||||
|
bazel_dep(name = "sourcekitten", version = "0.34.1", repo_name = "com_github_jpsim_sourcekitten")
|
||||||
|
bazel_dep(name = "swift_argument_parser", version = "1.2.1", repo_name = "sourcekitten_com_github_apple_swift_argument_parser")
|
||||||
|
bazel_dep(name = "yams", version = "5.0.5", repo_name = "sourcekitten_com_github_jpsim_yams")
|
||||||
|
|
||||||
|
swiftlint_repos = use_extension("//bazel:repos.bzl", "swiftlint_repos_bzlmod")
|
||||||
|
use_repo(
|
||||||
|
swiftlint_repos,
|
||||||
|
"SwiftSyntax",
|
||||||
|
"com_github_johnsundell_collectionconcurrencykit",
|
||||||
|
"com_github_krzyzanowskim_cryptoswift",
|
||||||
|
"swiftlint_com_github_scottrhoyt_swifty_text_table",
|
||||||
|
)
|
||||||
|
|
||||||
|
extra_rules = use_extension("//bazel:extensions.bzl", "extra_rules")
|
||||||
|
use_repo(extra_rules, "swiftlint_extra_rules")
|
45
Makefile
45
Makefile
|
@ -30,14 +30,18 @@ VERSION_STRING=$(shell ./tools/get-version)
|
||||||
|
|
||||||
all: build
|
all: build
|
||||||
|
|
||||||
sourcery: Source/SwiftLintFramework/Models/PrimaryRuleList.swift Tests/GeneratedTests/GeneratedTests.swift
|
sourcery: Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift Source/SwiftLintCore/Models/ReportersList.swift Tests/GeneratedTests/GeneratedTests.swift
|
||||||
|
|
||||||
Source/SwiftLintFramework/Models/PrimaryRuleList.swift: Source/SwiftLintFramework/Rules/**/*.swift .sourcery/PrimaryRuleList.stencil
|
Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift: Source/SwiftLintBuiltInRules/Rules/**/*.swift .sourcery/BuiltInRules.stencil
|
||||||
sourcery --sources Source/SwiftLintFramework/Rules --templates .sourcery/PrimaryRuleList.stencil --output .sourcery
|
./tools/sourcery --sources Source/SwiftLintBuiltInRules/Rules --templates .sourcery/BuiltInRules.stencil --output .sourcery
|
||||||
mv .sourcery/PrimaryRuleList.generated.swift Source/SwiftLintFramework/Models/PrimaryRuleList.swift
|
mv .sourcery/BuiltInRules.generated.swift Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift
|
||||||
|
|
||||||
Tests/GeneratedTests/GeneratedTests.swift: Source/SwiftLintFramework/Rules/**/*.swift .sourcery/GeneratedTests.stencil
|
Source/SwiftLintCore/Models/ReportersList.swift: Source/SwiftLintCore/Reporters/*.swift .sourcery/ReportersList.stencil
|
||||||
sourcery --sources Source/SwiftLintFramework/Rules --templates .sourcery/GeneratedTests.stencil --output .sourcery
|
./tools/sourcery --sources Source/SwiftLintCore/Reporters --templates .sourcery/ReportersList.stencil --output .sourcery
|
||||||
|
mv .sourcery/ReportersList.generated.swift Source/SwiftLintCore/Models/ReportersList.swift
|
||||||
|
|
||||||
|
Tests/GeneratedTests/GeneratedTests.swift: Source/SwiftLint*/Rules/**/*.swift .sourcery/GeneratedTests.stencil
|
||||||
|
./tools/sourcery --sources Source/SwiftLintCore/Rules --sources Source/SwiftLintBuiltInRules/Rules --templates .sourcery/GeneratedTests.stencil --output .sourcery
|
||||||
mv .sourcery/GeneratedTests.generated.swift Tests/GeneratedTests/GeneratedTests.swift
|
mv .sourcery/GeneratedTests.generated.swift Tests/GeneratedTests/GeneratedTests.swift
|
||||||
|
|
||||||
test: clean_xcode
|
test: clean_xcode
|
||||||
|
@ -65,7 +69,7 @@ clean:
|
||||||
clean_xcode:
|
clean_xcode:
|
||||||
$(BUILD_TOOL) $(XCODEFLAGS) -configuration Test clean
|
$(BUILD_TOOL) $(XCODEFLAGS) -configuration Test clean
|
||||||
|
|
||||||
build: clean
|
build:
|
||||||
mkdir -p "$(SWIFTLINT_EXECUTABLE_PARENT)"
|
mkdir -p "$(SWIFTLINT_EXECUTABLE_PARENT)"
|
||||||
bazel build --config release universal_swiftlint
|
bazel build --config release universal_swiftlint
|
||||||
$(eval SWIFTLINT_BINARY := $(shell bazel cquery --config release --output=files universal_swiftlint))
|
$(eval SWIFTLINT_BINARY := $(shell bazel cquery --config release --output=files universal_swiftlint))
|
||||||
|
@ -113,10 +117,11 @@ zip_linux: docker_image
|
||||||
|
|
||||||
zip_linux_release:
|
zip_linux_release:
|
||||||
$(eval TMP_FOLDER := $(shell mktemp -d))
|
$(eval TMP_FOLDER := $(shell mktemp -d))
|
||||||
docker run "ghcr.io/realm/swiftlint:$(VERSION_STRING)" cat /usr/bin/swiftlint > "$(TMP_FOLDER)/swiftlint"
|
docker run --platform linux/amd64 "ghcr.io/realm/swiftlint:$(VERSION_STRING)" cat /usr/bin/swiftlint > "$(TMP_FOLDER)/swiftlint"
|
||||||
chmod +x "$(TMP_FOLDER)/swiftlint"
|
chmod +x "$(TMP_FOLDER)/swiftlint"
|
||||||
cp -f "$(LICENSE_PATH)" "$(TMP_FOLDER)"
|
cp -f "$(LICENSE_PATH)" "$(TMP_FOLDER)"
|
||||||
(cd "$(TMP_FOLDER)"; zip -yr - "swiftlint" "LICENSE") > "./swiftlint_linux.zip"
|
(cd "$(TMP_FOLDER)"; zip -yr - "swiftlint" "LICENSE") > "./swiftlint_linux.zip"
|
||||||
|
gh release upload "$(VERSION_STRING)" swiftlint_linux.zip
|
||||||
|
|
||||||
package: build
|
package: build
|
||||||
$(eval PACKAGE_ROOT := $(shell mktemp -d))
|
$(eval PACKAGE_ROOT := $(shell mktemp -d))
|
||||||
|
@ -132,13 +137,11 @@ bazel_release:
|
||||||
bazel build :release
|
bazel build :release
|
||||||
mv bazel-bin/bazel.tar.gz bazel-bin/bazel.tar.gz.sha256 .
|
mv bazel-bin/bazel.tar.gz bazel-bin/bazel.tar.gz.sha256 .
|
||||||
|
|
||||||
release: clean bazel_release package portable_zip spm_artifactbundle_macos zip_linux_release
|
|
||||||
|
|
||||||
docker_image:
|
docker_image:
|
||||||
docker build --platform linux/amd64 --force-rm --tag swiftlint .
|
docker build --platform linux/amd64 --force-rm --tag swiftlint .
|
||||||
|
|
||||||
docker_test:
|
docker_test:
|
||||||
docker run -v `pwd`:`pwd` -w `pwd` --name swiftlint --rm swift:5.6-focal swift test --parallel
|
docker run -v `pwd`:`pwd` -w `pwd` --name swiftlint --rm swift:5.7-focal swift test --parallel
|
||||||
|
|
||||||
docker_htop:
|
docker_htop:
|
||||||
docker run --platform linux/amd64 -it --rm --pid=container:swiftlint terencewestphal/htop || reset
|
docker run --platform linux/amd64 -it --rm --pid=container:swiftlint terencewestphal/htop || reset
|
||||||
|
@ -149,8 +152,8 @@ display_compilation_time:
|
||||||
|
|
||||||
publish:
|
publish:
|
||||||
brew update && brew bump-formula-pr --tag=$(shell git describe --tags) --revision=$(shell git rev-parse HEAD) swiftlint
|
brew update && brew bump-formula-pr --tag=$(shell git describe --tags) --revision=$(shell git rev-parse HEAD) swiftlint
|
||||||
# Workaround for https://github.com/CocoaPods/CocoaPods/issues/11185
|
bundle install
|
||||||
arch -arch x86_64 pod trunk push SwiftLint.podspec
|
bundle exec pod trunk push SwiftLint.podspec
|
||||||
|
|
||||||
docs:
|
docs:
|
||||||
swift run swiftlint generate-docs
|
swift run swiftlint generate-docs
|
||||||
|
@ -160,18 +163,30 @@ docs:
|
||||||
get_version:
|
get_version:
|
||||||
@echo "$(VERSION_STRING)"
|
@echo "$(VERSION_STRING)"
|
||||||
|
|
||||||
push_version:
|
release:
|
||||||
ifneq ($(strip $(shell git status --untracked-files=no --porcelain 2>/dev/null)),)
|
ifneq ($(strip $(shell git status --untracked-files=no --porcelain 2>/dev/null)),)
|
||||||
$(error git state is not clean)
|
$(error git state is not clean)
|
||||||
endif
|
endif
|
||||||
$(eval NEW_VERSION_AND_NAME := $(filter-out $@,$(MAKECMDGOALS)))
|
$(eval NEW_VERSION_AND_NAME := $(filter-out $@,$(MAKECMDGOALS)))
|
||||||
$(eval NEW_VERSION := $(shell echo $(NEW_VERSION_AND_NAME) | sed 's/:.*//' ))
|
$(eval NEW_VERSION := $(shell echo $(NEW_VERSION_AND_NAME) | sed 's/:.*//' ))
|
||||||
@sed -i '' 's/## Main/## $(NEW_VERSION_AND_NAME)/g' CHANGELOG.md
|
@sed -i '' 's/## Main/## $(NEW_VERSION_AND_NAME)/g' CHANGELOG.md
|
||||||
@sed 's/__VERSION__/$(NEW_VERSION)/g' tools/Version.swift.template > Source/SwiftLintFramework/Models/Version.swift
|
@sed 's/__VERSION__/$(NEW_VERSION)/g' tools/Version.swift.template > Source/SwiftLintCore/Models/Version.swift
|
||||||
|
@sed -e '3s/.*/ version = "$(NEW_VERSION)",/' -i '' MODULE.bazel
|
||||||
|
make clean
|
||||||
|
make package
|
||||||
|
make bazel_release
|
||||||
|
make portable_zip
|
||||||
|
make spm_artifactbundle_macos
|
||||||
|
./tools/update-artifact-bundle.sh "$(NEW_VERSION)"
|
||||||
git commit -a -m "release $(NEW_VERSION)"
|
git commit -a -m "release $(NEW_VERSION)"
|
||||||
git tag -a $(NEW_VERSION) -m "$(NEW_VERSION_AND_NAME)"
|
git tag -a $(NEW_VERSION) -m "$(NEW_VERSION_AND_NAME)"
|
||||||
git push origin HEAD
|
git push origin HEAD
|
||||||
git push origin $(NEW_VERSION)
|
git push origin $(NEW_VERSION)
|
||||||
|
./tools/create-github-release.sh "$(NEW_VERSION)"
|
||||||
|
make publish
|
||||||
|
./tools/add-new-changelog-section.sh
|
||||||
|
git commit -a -m "Add new changelog section"
|
||||||
|
git push origin HEAD
|
||||||
|
|
||||||
%:
|
%:
|
||||||
@:
|
@:
|
||||||
|
|
|
@ -9,13 +9,22 @@
|
||||||
"version" : "0.2.0"
|
"version" : "0.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"identity" : "cryptoswift",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/krzyzanowskim/CryptoSwift.git",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "32f641cf24fc7abc1c591a2025e9f2f572648b0f",
|
||||||
|
"version" : "1.7.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"identity" : "sourcekitten",
|
"identity" : "sourcekitten",
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "https://github.com/jpsim/SourceKitten.git",
|
"location" : "https://github.com/jpsim/SourceKitten.git",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "fc12c0f182c5cf80781dd933b17a82eb98bd7c61",
|
"revision" : "b6dc09ee51dfb0c66e042d2328c017483a1a5d56",
|
||||||
"version" : "0.33.1"
|
"version" : "0.34.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -23,8 +32,8 @@
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "https://github.com/apple/swift-argument-parser.git",
|
"location" : "https://github.com/apple/swift-argument-parser.git",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "fddd1c00396eed152c45a46bea9f47b98e59301d",
|
"revision" : "4ad606ba5d7673ea60679a61ff867cc1ff8c8e86",
|
||||||
"version" : "1.2.0"
|
"version" : "1.2.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -32,7 +41,8 @@
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "https://github.com/apple/swift-syntax.git",
|
"location" : "https://github.com/apple/swift-syntax.git",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "a82041008d2c678a97407fbd0ce420d3ab047538"
|
"revision" : "165fc6d22394c1168ff76ab5d951245971ef07e5",
|
||||||
|
"version" : "509.0.0-swift-DEVELOPMENT-SNAPSHOT-2023-06-05-a"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -58,8 +68,8 @@
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "https://github.com/jpsim/Yams.git",
|
"location" : "https://github.com/jpsim/Yams.git",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "01835dc202670b5bb90d07f3eae41867e9ed29f6",
|
"revision" : "f47ba4838c30dbd59998a4e4c87ab620ff959e8a",
|
||||||
"version" : "5.0.1"
|
"version" : "5.0.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,23 +1,6 @@
|
||||||
// swift-tools-version:5.7
|
// swift-tools-version:5.7
|
||||||
import PackageDescription
|
import PackageDescription
|
||||||
|
|
||||||
#if os(macOS)
|
|
||||||
private let addCryptoSwift = false
|
|
||||||
#else
|
|
||||||
private let addCryptoSwift = true
|
|
||||||
#endif
|
|
||||||
|
|
||||||
let frameworkDependencies: [Target.Dependency] = [
|
|
||||||
.product(name: "IDEUtils", package: "swift-syntax"),
|
|
||||||
.product(name: "SourceKittenFramework", package: "SourceKitten"),
|
|
||||||
.product(name: "SwiftSyntax", package: "swift-syntax"),
|
|
||||||
.product(name: "SwiftSyntaxBuilder", package: "swift-syntax"),
|
|
||||||
.product(name: "SwiftParser", package: "swift-syntax"),
|
|
||||||
.product(name: "SwiftOperators", package: "swift-syntax"),
|
|
||||||
"Yams",
|
|
||||||
]
|
|
||||||
+ (addCryptoSwift ? ["CryptoSwift"] : [])
|
|
||||||
|
|
||||||
let package = Package(
|
let package = Package(
|
||||||
name: "SwiftLint",
|
name: "SwiftLint",
|
||||||
platforms: [.macOS(.v12)],
|
platforms: [.macOS(.v12)],
|
||||||
|
@ -27,19 +10,21 @@ let package = Package(
|
||||||
.plugin(name: "SwiftLintPlugin", targets: ["SwiftLintPlugin"])
|
.plugin(name: "SwiftLintPlugin", targets: ["SwiftLintPlugin"])
|
||||||
],
|
],
|
||||||
dependencies: [
|
dependencies: [
|
||||||
.package(url: "https://github.com/apple/swift-argument-parser.git", .upToNextMinor(from: "1.2.0")),
|
.package(url: "https://github.com/apple/swift-argument-parser.git", .upToNextMinor(from: "1.2.1")),
|
||||||
.package(url: "https://github.com/apple/swift-syntax.git", revision: "a82041008d2c678a97407fbd0ce420d3ab047538"),
|
.package(url: "https://github.com/apple/swift-syntax.git", exact: "509.0.0-swift-DEVELOPMENT-SNAPSHOT-2023-06-05-a"),
|
||||||
.package(url: "https://github.com/jpsim/SourceKitten.git", .upToNextMinor(from: "0.33.1")),
|
.package(url: "https://github.com/jpsim/SourceKitten.git", .upToNextMinor(from: "0.34.1")),
|
||||||
.package(url: "https://github.com/jpsim/Yams.git", from: "5.0.1"),
|
.package(url: "https://github.com/jpsim/Yams.git", from: "5.0.5"),
|
||||||
.package(url: "https://github.com/scottrhoyt/SwiftyTextTable.git", from: "0.9.0"),
|
.package(url: "https://github.com/scottrhoyt/SwiftyTextTable.git", from: "0.9.0"),
|
||||||
.package(url: "https://github.com/JohnSundell/CollectionConcurrencyKit.git", from: "0.2.0")
|
.package(url: "https://github.com/JohnSundell/CollectionConcurrencyKit.git", from: "0.2.0"),
|
||||||
] + (addCryptoSwift ? [.package(url: "https://github.com/krzyzanowskim/CryptoSwift.git", .upToNextMinor(from: "1.6.0"))] : []),
|
.package(url: "https://github.com/krzyzanowskim/CryptoSwift.git", .upToNextMinor(from: "1.7.2"))
|
||||||
|
],
|
||||||
targets: [
|
targets: [
|
||||||
.plugin(
|
.plugin(
|
||||||
name: "SwiftLintPlugin",
|
name: "SwiftLintPlugin",
|
||||||
capability: .buildTool(),
|
capability: .buildTool(),
|
||||||
dependencies: [
|
dependencies: [
|
||||||
.target(name: "swiftlint")
|
.target(name: "SwiftLintBinary", condition: .when(platforms: [.macOS])),
|
||||||
|
.target(name: "swiftlint", condition: .when(platforms: [.linux]))
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
.executableTarget(
|
.executableTarget(
|
||||||
|
@ -58,14 +43,43 @@ let package = Package(
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
.target(
|
.target(
|
||||||
name: "SwiftLintFramework",
|
name: "SwiftLintCore",
|
||||||
dependencies: frameworkDependencies
|
dependencies: [
|
||||||
|
.product(name: "CryptoSwift", package: "CryptoSwift", condition: .when(platforms: [.linux])),
|
||||||
|
.target(name: "DyldWarningWorkaround", condition: .when(platforms: [.macOS])),
|
||||||
|
.product(name: "SourceKittenFramework", package: "SourceKitten"),
|
||||||
|
.product(name: "SwiftIDEUtils", package: "swift-syntax"),
|
||||||
|
.product(name: "SwiftOperators", package: "swift-syntax"),
|
||||||
|
.product(name: "SwiftParser", package: "swift-syntax"),
|
||||||
|
.product(name: "SwiftSyntax", package: "swift-syntax"),
|
||||||
|
.product(name: "SwiftSyntaxBuilder", package: "swift-syntax"),
|
||||||
|
.product(name: "SwiftyTextTable", package: "SwiftyTextTable"),
|
||||||
|
.product(name: "Yams", package: "Yams"),
|
||||||
|
]
|
||||||
),
|
),
|
||||||
.testTarget(
|
.target(
|
||||||
|
name: "SwiftLintBuiltInRules",
|
||||||
|
dependencies: ["SwiftLintCore"]
|
||||||
|
),
|
||||||
|
.target(
|
||||||
|
name: "SwiftLintExtraRules",
|
||||||
|
dependencies: ["SwiftLintCore"]
|
||||||
|
),
|
||||||
|
.target(
|
||||||
|
name: "SwiftLintFramework",
|
||||||
|
dependencies: [
|
||||||
|
"SwiftLintBuiltInRules",
|
||||||
|
"SwiftLintCore",
|
||||||
|
"SwiftLintExtraRules"
|
||||||
|
]
|
||||||
|
),
|
||||||
|
.target(name: "DyldWarningWorkaround"),
|
||||||
|
.target(
|
||||||
name: "SwiftLintTestHelpers",
|
name: "SwiftLintTestHelpers",
|
||||||
dependencies: [
|
dependencies: [
|
||||||
"SwiftLintFramework"
|
"SwiftLintFramework"
|
||||||
]
|
],
|
||||||
|
path: "Tests/SwiftLintTestHelpers"
|
||||||
),
|
),
|
||||||
.testTarget(
|
.testTarget(
|
||||||
name: "SwiftLintFrameworkTests",
|
name: "SwiftLintFrameworkTests",
|
||||||
|
@ -98,5 +112,10 @@ let package = Package(
|
||||||
"SwiftLintTestHelpers"
|
"SwiftLintTestHelpers"
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
|
.binaryTarget(
|
||||||
|
name: "SwiftLintBinary",
|
||||||
|
url: "https://github.com/realm/SwiftLint/releases/download/0.52.2/SwiftLintBinary-macos.artifactbundle.zip",
|
||||||
|
checksum: "89651e1c87fb62faf076ef785a5b1af7f43570b2b74c6773526e0d5114e0578e"
|
||||||
|
)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,16 +1,42 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import PackagePlugin
|
import PackagePlugin
|
||||||
|
|
||||||
|
#if os(Linux)
|
||||||
|
import Glibc
|
||||||
|
#else
|
||||||
|
import Darwin
|
||||||
|
#endif
|
||||||
|
|
||||||
extension Path {
|
extension Path {
|
||||||
/// Scans the receiver, then all of its parents looking for a configuration file with the name ".swiftlint.yml".
|
/// Scans the receiver, then all of its parents looking for a configuration file with the name ".swiftlint.yml".
|
||||||
///
|
///
|
||||||
/// - returns: Path to the configuration file, or nil if one cannot be found.
|
/// - returns: Path to the configuration file, or nil if one cannot be found.
|
||||||
func firstConfigurationFileInParentDirectories() -> Path? {
|
func firstConfigurationFileInParentDirectories() -> Path? {
|
||||||
let defaultConfigurationFileName = ".swiftlint.yml"
|
let defaultConfigurationFileName = ".swiftlint.yml"
|
||||||
let proposedDirectory = sequence(first: self, next: { $0.removingLastComponent() }).first { path in
|
let proposedDirectory = sequence(
|
||||||
|
first: self,
|
||||||
|
next: { path in
|
||||||
|
guard path.stem.count > 1 else {
|
||||||
|
// Check we're not at the root of this filesystem, as `removingLastComponent()`
|
||||||
|
// will continually return the root from itself.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return path.removingLastComponent()
|
||||||
|
}
|
||||||
|
).first { path in
|
||||||
let potentialConfigurationFile = path.appending(subpath: defaultConfigurationFileName)
|
let potentialConfigurationFile = path.appending(subpath: defaultConfigurationFileName)
|
||||||
return FileManager.default.isReadableFile(atPath: potentialConfigurationFile.string)
|
return potentialConfigurationFile.isAccessible()
|
||||||
}
|
}
|
||||||
return proposedDirectory?.appending(subpath: defaultConfigurationFileName)
|
return proposedDirectory?.appending(subpath: defaultConfigurationFileName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Safe way to check if the file is accessible from within the current process sandbox.
|
||||||
|
private func isAccessible() -> Bool {
|
||||||
|
let result = string.withCString { pointer in
|
||||||
|
access(pointer, R_OK)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result == 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,45 +3,55 @@ import PackagePlugin
|
||||||
|
|
||||||
@main
|
@main
|
||||||
struct SwiftLintPlugin: BuildToolPlugin {
|
struct SwiftLintPlugin: BuildToolPlugin {
|
||||||
func createBuildCommands(
|
func createBuildCommands(context: PluginContext, target: Target) async throws -> [Command] {
|
||||||
context: PackagePlugin.PluginContext,
|
|
||||||
target: PackagePlugin.Target
|
|
||||||
) async throws -> [PackagePlugin.Command] {
|
|
||||||
guard let sourceTarget = target as? SourceModuleTarget else {
|
guard let sourceTarget = target as? SourceModuleTarget else {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
return createBuildCommands(
|
||||||
|
inputFiles: sourceTarget.sourceFiles(withSuffix: "swift").map(\.path),
|
||||||
|
packageDirectory: context.package.directory,
|
||||||
|
workingDirectory: context.pluginWorkDirectory,
|
||||||
|
tool: try context.tool(named: "swiftlint")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
let inputFilePaths = sourceTarget.sourceFiles(withSuffix: "swift")
|
private func createBuildCommands(
|
||||||
.map(\.path)
|
inputFiles: [Path],
|
||||||
|
packageDirectory: Path,
|
||||||
guard inputFilePaths.isEmpty == false else {
|
workingDirectory: Path,
|
||||||
|
tool: PluginContext.Tool
|
||||||
|
) -> [Command] {
|
||||||
|
if inputFiles.isEmpty {
|
||||||
// Don't lint anything if there are no Swift source files in this target
|
// Don't lint anything if there are no Swift source files in this target
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
let swiftlint = try context.tool(named: "swiftlint")
|
var arguments = [
|
||||||
var arguments: [String] = [
|
|
||||||
"lint",
|
"lint",
|
||||||
"--cache-path", "\(context.pluginWorkDirectory)"
|
"--quiet",
|
||||||
|
// We always pass all of the Swift source files in the target to the tool,
|
||||||
|
// so we need to ensure that any exclusion rules in the configuration are
|
||||||
|
// respected.
|
||||||
|
"--force-exclude",
|
||||||
|
"--cache-path", "\(workingDirectory)"
|
||||||
]
|
]
|
||||||
|
|
||||||
// Manually look for configuration files, to avoid issues when the plugin does not execute our tool from the
|
// Manually look for configuration files, to avoid issues when the plugin does not execute our tool from the
|
||||||
// package source directory.
|
// package source directory.
|
||||||
if let configuration = context.package.directory.firstConfigurationFileInParentDirectories() {
|
if let configuration = packageDirectory.firstConfigurationFileInParentDirectories() {
|
||||||
arguments.append(contentsOf: [
|
arguments.append(contentsOf: ["--config", "\(configuration.string)"])
|
||||||
"--config", "\(configuration.string)"
|
|
||||||
])
|
|
||||||
}
|
}
|
||||||
|
arguments += inputFiles.map(\.string)
|
||||||
|
|
||||||
arguments += inputFilePaths.map(\.string)
|
// We are not producing output files and this is needed only to not include cache files into bundle
|
||||||
|
let outputFilesDirectory = workingDirectory.appending("Output")
|
||||||
|
|
||||||
return [
|
return [
|
||||||
.buildCommand(
|
.prebuildCommand(
|
||||||
displayName: "SwiftLint",
|
displayName: "SwiftLint",
|
||||||
executable: swiftlint.path,
|
executable: tool.path,
|
||||||
arguments: arguments,
|
arguments: arguments,
|
||||||
inputFiles: inputFilePaths,
|
outputFilesDirectory: outputFilesDirectory
|
||||||
outputFiles: [context.pluginWorkDirectory]
|
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -51,44 +61,16 @@ struct SwiftLintPlugin: BuildToolPlugin {
|
||||||
import XcodeProjectPlugin
|
import XcodeProjectPlugin
|
||||||
|
|
||||||
extension SwiftLintPlugin: XcodeBuildToolPlugin {
|
extension SwiftLintPlugin: XcodeBuildToolPlugin {
|
||||||
func createBuildCommands(
|
func createBuildCommands(context: XcodePluginContext, target: XcodeTarget) throws -> [Command] {
|
||||||
context: XcodePluginContext,
|
|
||||||
target: XcodeTarget
|
|
||||||
) throws -> [Command] {
|
|
||||||
let inputFilePaths = target.inputFiles
|
let inputFilePaths = target.inputFiles
|
||||||
.filter { $0.type == .source && $0.path.extension == "swift" }
|
.filter { $0.type == .source && $0.path.extension == "swift" }
|
||||||
.map(\.path)
|
.map(\.path)
|
||||||
|
return createBuildCommands(
|
||||||
guard inputFilePaths.isEmpty == false else {
|
inputFiles: inputFilePaths,
|
||||||
// Don't lint anything if there are no Swift source files in this target
|
packageDirectory: context.xcodeProject.directory,
|
||||||
return []
|
workingDirectory: context.pluginWorkDirectory,
|
||||||
}
|
tool: try context.tool(named: "swiftlint")
|
||||||
|
)
|
||||||
let swiftlint = try context.tool(named: "swiftlint")
|
|
||||||
var arguments: [String] = [
|
|
||||||
"lint",
|
|
||||||
"--cache-path", "\(context.pluginWorkDirectory)"
|
|
||||||
]
|
|
||||||
|
|
||||||
// Xcode build tool plugins don't seem to run from the project source directory, so our auto-discovery of
|
|
||||||
// configuration files doesn't work. We approximate it here.
|
|
||||||
if let configuration = context.xcodeProject.directory.firstConfigurationFileInParentDirectories() {
|
|
||||||
arguments.append(contentsOf: [
|
|
||||||
"--config", "\(configuration.string)"
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
arguments += inputFilePaths.map(\.string)
|
|
||||||
|
|
||||||
return [
|
|
||||||
.buildCommand(
|
|
||||||
displayName: "SwiftLint",
|
|
||||||
executable: swiftlint.path,
|
|
||||||
arguments: arguments,
|
|
||||||
inputFiles: inputFilePaths,
|
|
||||||
outputFiles: [context.pluginWorkDirectory]
|
|
||||||
)
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
87
README.md
87
README.md
|
@ -1,6 +1,6 @@
|
||||||
# SwiftLint
|
# SwiftLint
|
||||||
|
|
||||||
A tool to enforce Swift style and conventions, loosely based on the now archived [GitHub Swift Style Guide](https://github.com/github/swift-style-guide). SwiftLint enforces the style guide rules that are generally accepted by the Swift community. These rules are well described in popular style guides like [Ray Wenderlich's Swift Style Guide](https://github.com/raywenderlich/swift-style-guide).
|
A tool to enforce Swift style and conventions, loosely based on the now archived [GitHub Swift Style Guide](https://github.com/github/swift-style-guide). SwiftLint enforces the style guide rules that are generally accepted by the Swift community. These rules are well described in popular style guides like [Kodeco's Swift Style Guide](https://github.com/kodecocodes/swift-style-guide).
|
||||||
|
|
||||||
SwiftLint hooks into [Clang](http://clang.llvm.org) and
|
SwiftLint hooks into [Clang](http://clang.llvm.org) and
|
||||||
[SourceKit](http://www.jpsim.com/uncovering-sourcekit) to use the
|
[SourceKit](http://www.jpsim.com/uncovering-sourcekit) to use the
|
||||||
|
@ -64,13 +64,19 @@ You can also build and install from source by cloning this project and running
|
||||||
|
|
||||||
### Using Bazel
|
### Using Bazel
|
||||||
|
|
||||||
Put this in your `WORKSPACE`:
|
Put this in your `MODULE.bazel`:
|
||||||
|
|
||||||
|
```bzl
|
||||||
|
bazel_dep(name = "swiftlint", version = "0.50.4", repo_name = "SwiftLint")
|
||||||
|
```
|
||||||
|
|
||||||
|
Or put this in your `WORKSPACE`:
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
|
|
||||||
<summary>WORKSPACE</summary>
|
<summary>WORKSPACE</summary>
|
||||||
|
|
||||||
```python
|
```bzl
|
||||||
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
|
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
|
||||||
|
|
||||||
http_archive(
|
http_archive(
|
||||||
|
@ -152,7 +158,10 @@ folder by default. To instruct Xcode where to find SwiftLint, you can either add
|
||||||
`/opt/homebrew/bin` to the `PATH` environment variable in your build phase
|
`/opt/homebrew/bin` to the `PATH` environment variable in your build phase
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
export PATH="$PATH:/opt/homebrew/bin"
|
if [[ "$(uname -m)" == arm64 ]]; then
|
||||||
|
export PATH="/opt/homebrew/bin:$PATH"
|
||||||
|
fi
|
||||||
|
|
||||||
if which swiftlint > /dev/null; then
|
if which swiftlint > /dev/null; then
|
||||||
swiftlint
|
swiftlint
|
||||||
else
|
else
|
||||||
|
@ -194,7 +203,7 @@ there is currently no way to pass any additional options to the SwiftLint execut
|
||||||
|
|
||||||
#### Xcode
|
#### Xcode
|
||||||
|
|
||||||
You can integrate SwiftLint as a Xcode Build Tool Plug-in if you're working
|
You can integrate SwiftLint as an Xcode Build Tool Plug-in if you're working
|
||||||
with a project in Xcode.
|
with a project in Xcode.
|
||||||
|
|
||||||
Add SwiftLint as a package dependency to your project without linking any of the
|
Add SwiftLint as a package dependency to your project without linking any of the
|
||||||
|
@ -206,6 +215,15 @@ Select `SwiftLintPlugin` from the list and add it to the project.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
For unattended use (e.g. on CI), you can disable the package validation dialog by
|
||||||
|
|
||||||
|
* individually passing `-skipPackagePluginValidation` to `xcodebuild` or
|
||||||
|
* globally setting `defaults write com.apple.dt.Xcode IDESkipPackagePluginFingerprintValidatation -bool YES`
|
||||||
|
for that user.
|
||||||
|
|
||||||
|
_Note: This implicitly trusts all Xcode package plugins and bypasses Xcode's package validation
|
||||||
|
dialogs, which has security implications._
|
||||||
|
|
||||||
#### Swift Package
|
#### Swift Package
|
||||||
|
|
||||||
You can integrate SwiftLint as a Swift Package Manager Plug-in if you're working with
|
You can integrate SwiftLint as a Swift Package Manager Plug-in if you're working with
|
||||||
|
@ -221,14 +239,6 @@ Add SwiftLint to a target using the `plugins` parameter.
|
||||||
),
|
),
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
### AppCode
|
|
||||||
|
|
||||||
To integrate SwiftLint with AppCode, install
|
|
||||||
[this plugin](https://plugins.jetbrains.com/plugin/9175) and configure
|
|
||||||
SwiftLint's installed path in the plugin's preferences.
|
|
||||||
The `fix` action is available via `⌥⏎`.
|
|
||||||
|
|
||||||
### Visual Studio Code
|
### Visual Studio Code
|
||||||
|
|
||||||
To integrate SwiftLint with [vscode](https://code.visualstudio.com), install the
|
To integrate SwiftLint with [vscode](https://code.visualstudio.com), install the
|
||||||
|
@ -298,6 +308,7 @@ SUBCOMMANDS:
|
||||||
docs Open SwiftLint documentation website in the default web browser
|
docs Open SwiftLint documentation website in the default web browser
|
||||||
generate-docs Generates markdown documentation for all rules
|
generate-docs Generates markdown documentation for all rules
|
||||||
lint (default) Print lint warnings and errors
|
lint (default) Print lint warnings and errors
|
||||||
|
reporters Display the list of reporters and their identifiers
|
||||||
rules Display the list of rules and their identifiers
|
rules Display the list of rules and their identifiers
|
||||||
version Display the current version of SwiftLint
|
version Display the current version of SwiftLint
|
||||||
|
|
||||||
|
@ -365,12 +376,21 @@ Once [installed](https://pre-commit.com/#install), add this to the
|
||||||
```yaml
|
```yaml
|
||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/realm/SwiftLint
|
- repo: https://github.com/realm/SwiftLint
|
||||||
rev: 0.44.0
|
rev: 0.50.3
|
||||||
hooks:
|
hooks:
|
||||||
- id: swiftlint
|
- id: swiftlint
|
||||||
```
|
```
|
||||||
|
|
||||||
Adjust `rev` to the SwiftLint version of your choice.
|
Adjust `rev` to the SwiftLint version of your choice. `pre-commit autoupdate` can be used to update to the current version.
|
||||||
|
|
||||||
|
SwiftLint can be configured using `entry` to apply fixes and fail on errors:
|
||||||
|
```yaml
|
||||||
|
- repo: https://github.com/realm/SwiftLint
|
||||||
|
rev: 0.50.3
|
||||||
|
hooks:
|
||||||
|
- id: swiftlint
|
||||||
|
entry: swiftlint --fix --strict
|
||||||
|
```
|
||||||
|
|
||||||
## Rules
|
## Rules
|
||||||
|
|
||||||
|
@ -381,7 +401,7 @@ continues to contribute more over time.
|
||||||
You can find an updated list of rules and more information about them
|
You can find an updated list of rules and more information about them
|
||||||
[here](https://realm.github.io/SwiftLint/rule-directory.html).
|
[here](https://realm.github.io/SwiftLint/rule-directory.html).
|
||||||
|
|
||||||
You can also check [Source/SwiftLintFramework/Rules](https://github.com/realm/SwiftLint/tree/main/Source/SwiftLintFramework/Rules)
|
You can also check [Source/SwiftLintBuiltInRules/Rules](https://github.com/realm/SwiftLint/tree/main/Source/SwiftLintBuiltInRules/Rules)
|
||||||
directory to see their implementation.
|
directory to see their implementation.
|
||||||
|
|
||||||
### Opt-In Rules
|
### Opt-In Rules
|
||||||
|
@ -459,7 +479,9 @@ run SwiftLint from. The following parameters can be configured:
|
||||||
Rule inclusion:
|
Rule inclusion:
|
||||||
|
|
||||||
* `disabled_rules`: Disable rules from the default enabled set.
|
* `disabled_rules`: Disable rules from the default enabled set.
|
||||||
* `opt_in_rules`: Enable rules that are not part of the default set.
|
* `opt_in_rules`: Enable rules that are not part of the default set. The
|
||||||
|
special `all` identifier will enable all opt in linter rules, except the ones
|
||||||
|
listed in `disabled_rules`.
|
||||||
* `only_rules`: Only the rules specified in this list will be enabled.
|
* `only_rules`: Only the rules specified in this list will be enabled.
|
||||||
Cannot be specified alongside `disabled_rules` or `opt_in_rules`.
|
Cannot be specified alongside `disabled_rules` or `opt_in_rules`.
|
||||||
* `analyzer_rules`: This is an entirely separate list of rules that are only
|
* `analyzer_rules`: This is an entirely separate list of rules that are only
|
||||||
|
@ -481,6 +503,9 @@ opt_in_rules: # some rules are turned off by default, so you need to opt-in
|
||||||
# - empty_parameters
|
# - empty_parameters
|
||||||
# - vertical_whitespace
|
# - vertical_whitespace
|
||||||
|
|
||||||
|
analyzer_rules: # Rules run by `swiftlint analyze`
|
||||||
|
- explicit_self
|
||||||
|
|
||||||
included: # paths to include during linting. `--path` is ignored if present.
|
included: # paths to include during linting. `--path` is ignored if present.
|
||||||
- Source
|
- Source
|
||||||
excluded: # paths to ignore during linting. Takes precedence over `included`.
|
excluded: # paths to ignore during linting. Takes precedence over `included`.
|
||||||
|
@ -489,8 +514,9 @@ excluded: # paths to ignore during linting. Takes precedence over `included`.
|
||||||
- Source/ExcludedFolder
|
- Source/ExcludedFolder
|
||||||
- Source/ExcludedFile.swift
|
- Source/ExcludedFile.swift
|
||||||
- Source/*/ExcludedFile.swift # Exclude files with a wildcard
|
- Source/*/ExcludedFile.swift # Exclude files with a wildcard
|
||||||
analyzer_rules: # Rules run by `swiftlint analyze`
|
|
||||||
- explicit_self
|
# If true, SwiftLint will not fail if no lintable files are found.
|
||||||
|
allow_zero_lintable_files: false
|
||||||
|
|
||||||
# configurable rules can be customized from this configuration file
|
# configurable rules can be customized from this configuration file
|
||||||
# binary rules can set their severity level
|
# binary rules can set their severity level
|
||||||
|
@ -524,13 +550,29 @@ identifier_name:
|
||||||
- id
|
- id
|
||||||
- URL
|
- URL
|
||||||
- GlobalAPIKey
|
- GlobalAPIKey
|
||||||
reporter: "xcode" # reporter type (xcode, json, csv, checkstyle, codeclimate, junit, html, emoji, sonarqube, markdown, github-actions-logging)
|
reporter: "xcode" # reporter type (xcode, json, csv, checkstyle, codeclimate, junit, html, emoji, sonarqube, markdown, github-actions-logging, summary)
|
||||||
```
|
```
|
||||||
|
|
||||||
You can also use environment variables in your configuration file,
|
You can also use environment variables in your configuration file,
|
||||||
by using `${SOME_VARIABLE}` in a string.
|
by using `${SOME_VARIABLE}` in a string.
|
||||||
|
|
||||||
#### Defining Custom Rules
|
### Defining Custom Rules
|
||||||
|
|
||||||
|
In addition to the rules that the main SwiftLint project ships with, SwiftLint
|
||||||
|
can also run two types of custom rules that you can define yourself in your own
|
||||||
|
projects:
|
||||||
|
|
||||||
|
#### 1. Swift Custom Rules
|
||||||
|
|
||||||
|
These rules are written the same way as the Swift-based rules that ship with
|
||||||
|
SwiftLint so they're fast, accurate, can leverage SwiftSyntax, can be unit
|
||||||
|
tested, and more.
|
||||||
|
|
||||||
|
Using these requires building SwiftLint with Bazel as described in
|
||||||
|
[this video](https://vimeo.com/820572803) or its associated code in
|
||||||
|
[github.com/jpsim/swiftlint-bazel-example](https://github.com/jpsim/swiftlint-bazel-example).
|
||||||
|
|
||||||
|
#### 2. Regex Custom Rules
|
||||||
|
|
||||||
You can define custom regex-based rules in your configuration file using the
|
You can define custom regex-based rules in your configuration file using the
|
||||||
following syntax:
|
following syntax:
|
||||||
|
@ -595,6 +637,9 @@ which match to `keyword` and `identifier` in the above list.
|
||||||
If using custom rules in combination with `only_rules`, make sure to add
|
If using custom rules in combination with `only_rules`, make sure to add
|
||||||
`custom_rules` as an item under `only_rules`.
|
`custom_rules` as an item under `only_rules`.
|
||||||
|
|
||||||
|
Unlike Swift custom rules, you can use official SwiftLint builds
|
||||||
|
(e.g. from Homebrew) to run regex custom rules.
|
||||||
|
|
||||||
### Auto-correct
|
### Auto-correct
|
||||||
|
|
||||||
SwiftLint can automatically correct certain violations. Files on disk are
|
SwiftLint can automatically correct certain violations. Files on disk are
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# SwiftLint
|
# SwiftLint
|
||||||
|
|
||||||
SwiftLint 是一个用于强制检查 Swift 代码风格和规定的一个工具,基本上以 [Ray Wenderlich's Swift 代码风格指南](https://github.com/raywenderlich/swift-style-guide)为基础。
|
SwiftLint 是一个用于强制检查 Swift 代码风格和规定的一个工具,基本上以 [Kodeco's Swift 代码风格指南](https://github.com/kodecocodes/swift-style-guide)为基础。
|
||||||
|
|
||||||
SwiftLint Hook 了 [Clang](http://clang.llvm.org) 和 [SourceKit](http://www.jpsim.com/uncovering-sourcekit) 从而能够使用 [AST](http://clang.llvm.org/docs/IntroductionToTheClangAST.html) 来表示源代码文件的更多精确结果。
|
SwiftLint Hook 了 [Clang](http://clang.llvm.org) 和 [SourceKit](http://www.jpsim.com/uncovering-sourcekit) 从而能够使用 [AST](http://clang.llvm.org/docs/IntroductionToTheClangAST.html) 来表示源代码文件的更多精确结果。
|
||||||
|
|
||||||
|
@ -167,7 +167,7 @@ SwiftLint 已经包含了超过 75 条规则,并且我们希望 Swift 社区
|
||||||
|
|
||||||
你可以在 [Rule Directory](https://realm.github.io/SwiftLint/rule-directory.html) 找到规则的更新列表和更多信息。
|
你可以在 [Rule Directory](https://realm.github.io/SwiftLint/rule-directory.html) 找到规则的更新列表和更多信息。
|
||||||
|
|
||||||
你也可以检视 [Source/SwiftLintFramework/Rules](Source/SwiftLintFramework/Rules) 目录来查看它们的实现。
|
你也可以检视 [Source/SwiftLintBuiltInRules/Rules](Source/SwiftLintBuiltInRules/Rules) 目录来查看它们的实现。
|
||||||
|
|
||||||
`opt_in_rules` 默认是关闭的(即,你需要在你的配置文件中明确地打开它们)。
|
`opt_in_rules` 默认是关闭的(即,你需要在你的配置文件中明确地打开它们)。
|
||||||
|
|
||||||
|
|
11
README_KR.md
11
README_KR.md
|
@ -1,6 +1,6 @@
|
||||||
# SwiftLint
|
# SwiftLint
|
||||||
|
|
||||||
SwiftLint는 스위프트 스타일 및 컨벤션을 강제하기 위한 도구로, [Ray Wenderlich 스위프트 스타일 가이드](https://github.com/raywenderlich/swift-style-guide)에 대략적인 기반을 두고 있습니다.
|
SwiftLint는 스위프트 스타일 및 컨벤션을 강제하기 위한 도구로, [Kodeco 스위프트 스타일 가이드](https://github.com/kodecocodes/swift-style-guide)에 대략적인 기반을 두고 있습니다.
|
||||||
|
|
||||||
SwiftLint는 좀 더 정확한 결과를 위해 [Clang](http://clang.llvm.org)과 [SourceKit](http://www.jpsim.com/uncovering-sourcekit)에 연결하여 소스 파일의 [AST](http://clang.llvm.org/docs/IntroductionToTheClangAST.html) 표현을 사용합니다.
|
SwiftLint는 좀 더 정확한 결과를 위해 [Clang](http://clang.llvm.org)과 [SourceKit](http://www.jpsim.com/uncovering-sourcekit)에 연결하여 소스 파일의 [AST](http://clang.llvm.org/docs/IntroductionToTheClangAST.html) 표현을 사용합니다.
|
||||||
|
|
||||||
|
@ -75,7 +75,10 @@ fi
|
||||||
그 이유는, 애플 실리콘 기반 맥에서 Homebrew는 기본적으로 바이너리들을 `/opt/homebrew/bin`에 저장하기 때문입니다. SwiftLint가 어디 있는지 찾는 것을 Xcode에 알려주기 위해, build phase에서 `/opt/homebrew/bin`를 `PATH` 환경 변수에 동시에 추가하여야 합니다.
|
그 이유는, 애플 실리콘 기반 맥에서 Homebrew는 기본적으로 바이너리들을 `/opt/homebrew/bin`에 저장하기 때문입니다. SwiftLint가 어디 있는지 찾는 것을 Xcode에 알려주기 위해, build phase에서 `/opt/homebrew/bin`를 `PATH` 환경 변수에 동시에 추가하여야 합니다.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
export PATH="$PATH:/opt/homebrew/bin"
|
if [[ "$(uname -m)" == arm64 ]]; then
|
||||||
|
export PATH="/opt/homebrew/bin:$PATH"
|
||||||
|
fi
|
||||||
|
|
||||||
if which swiftlint > /dev/null; then
|
if which swiftlint > /dev/null; then
|
||||||
swiftlint
|
swiftlint
|
||||||
else
|
else
|
||||||
|
@ -180,9 +183,9 @@ $ TOOLCHAINS=com.apple.dt.toolchain.Swift_2_3 swiftlint autocorrect
|
||||||
|
|
||||||
## 룰
|
## 룰
|
||||||
|
|
||||||
SwiftLint에는 75개가 넘는 룰들이 있고, 스위프트 커뮤니티(바로 여러분들!)는 이를 지속적으로 발전시켜 가고 있습니다. [풀 리퀘스트](CONTRIBUTING.md)는 언제나 환영입니다.
|
SwiftLint에는 200개가 넘는 룰들이 있고, 스위프트 커뮤니티(바로 여러분들!)는 이를 지속적으로 발전시켜 가고 있습니다. [풀 리퀘스트](CONTRIBUTING.md)는 언제나 환영입니다.
|
||||||
|
|
||||||
현재 구현된 룰 전체를 확인하려면 [Source/SwiftLintFramework/Rules](Source/SwiftLintFramework/Rules)를 살펴보세요.
|
현재 구현된 룰 전체를 확인하려면 [Source/SwiftLintBuiltInRules/Rules](Source/SwiftLintBuiltInRules/Rules)를 살펴보세요.
|
||||||
|
|
||||||
`opt_in_rules`는 기본적으로 비활성화되어 있습니다. (즉, 설정 파일에서 명시적으로 해당 룰을 활성화해야 합니다.)
|
`opt_in_rules`는 기본적으로 비활성화되어 있습니다. (즉, 설정 파일에서 명시적으로 해당 룰을 활성화해야 합니다.)
|
||||||
|
|
||||||
|
|
17
Releasing.md
17
Releasing.md
|
@ -7,19 +7,8 @@ For SwiftLint contributors, follow these steps to cut a release:
|
||||||
* FabricSoftenerRule
|
* FabricSoftenerRule
|
||||||
* Top Loading
|
* Top Loading
|
||||||
* Fresh Out Of The Dryer
|
* Fresh Out Of The Dryer
|
||||||
1. Push new version: `make push_version "0.2.0: Tumble Dry"`
|
|
||||||
1. Make sure you have the latest stable Xcode version installed and
|
1. Make sure you have the latest stable Xcode version installed and
|
||||||
`xcode-select`ed.
|
`xcode-select`ed
|
||||||
1. Create the pkg installer, framework zip, portable zip,
|
1. Release new version: `make release "0.2.0: Tumble Dry"`
|
||||||
macos artifactbundle zip, and Linux zip:
|
1. Wait for the Docker CI job to finish then run: `make zip_linux_release`
|
||||||
`make release`
|
|
||||||
1. Create a GitHub release: https://github.com/realm/SwiftLint/releases/new
|
|
||||||
* Specify the tag you just pushed from the dropdown.
|
|
||||||
* Set the release title to the new version number & release name.
|
|
||||||
* Add the changelog section to the release description text box.
|
|
||||||
* Upload the bazel tarball & SHA-256 signature, pkg installer,
|
|
||||||
framework zip, portable zip, macos artifactbundle zip, and Linux zip
|
|
||||||
you just built to the GitHub release binaries.
|
|
||||||
* Click "Publish release".
|
|
||||||
1. Publish to Homebrew and CocoaPods trunk: `make publish`
|
|
||||||
1. Celebrate. :tada:
|
1. Celebrate. :tada:
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
#ifdef __APPLE__
|
||||||
|
|
||||||
|
#include "objc_dupclass.h"
|
||||||
|
|
||||||
|
OBJC_DUPCLASS(_TtC11SwiftSyntax11SyntaxArena);
|
||||||
|
OBJC_DUPCLASS(_TtC11SwiftSyntax13SyntaxVisitor);
|
||||||
|
OBJC_DUPCLASS(_TtC11SwiftSyntax14SyntaxRewriter);
|
||||||
|
OBJC_DUPCLASS(_TtC11SwiftSyntax16BumpPtrAllocator);
|
||||||
|
OBJC_DUPCLASS(_TtC11SwiftSyntax16SyntaxAnyVisitor);
|
||||||
|
OBJC_DUPCLASS(_TtC11SwiftSyntax18ParsingSyntaxArena);
|
||||||
|
OBJC_DUPCLASS(_TtC11SwiftSyntax23SourceLocationConverter);
|
||||||
|
OBJC_DUPCLASS(_TtC11SwiftSyntax26IncrementalParseTransition);
|
||||||
|
OBJC_DUPCLASS(_TtC11SwiftSyntax35IncrementalParseReusedNodeCollector);
|
||||||
|
|
||||||
|
#endif // __APPLE__
|
|
@ -0,0 +1,19 @@
|
||||||
|
// https://github.com/keith/objc_dupclass
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// TODO: This isn't entirely accurate, but I'm not sure how to more accurately determine
|
||||||
|
#if (defined(__arm64__) || defined(DUPCLASS_FORCE_DATA_CONST)) && !defined(DUPCLASS_FORCE_DATA)
|
||||||
|
#define SECTION "__DATA_CONST"
|
||||||
|
#else
|
||||||
|
#define SECTION "__DATA"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Struct layout from https://github.com/apple-oss-distributions/objc4/blob/8701d5672d3fd3cd817aeb84db1077aafe1a1604/runtime/objc-abi.h#L175-L183
|
||||||
|
#define OBJC_DUPCLASS(kclass) \
|
||||||
|
__attribute__((used)) __attribute__((visibility("hidden"))) \
|
||||||
|
static struct { uint32_t version; uint32_t flags; const char name[64]; } \
|
||||||
|
const __duplicate_class_##kclass = { 0, 0, #kclass }; \
|
||||||
|
\
|
||||||
|
__attribute__((used)) __attribute__((visibility("hidden"))) \
|
||||||
|
__attribute__((section (SECTION",__objc_dupclass"))) \
|
||||||
|
const void* __set___objc_dupclass_sym___duplicate_class_##kclass = &__duplicate_class_##kclass
|
|
@ -0,0 +1 @@
|
||||||
|
@_exported import SwiftLintCore
|
|
@ -66,7 +66,7 @@ extension SourceKittenDictionary {
|
||||||
/// arguments appears in the view's substructure.
|
/// arguments appears in the view's substructure.
|
||||||
func hasModifier(anyOf modifiers: [SwiftUIModifier], in file: SwiftLintFile) -> Bool {
|
func hasModifier(anyOf modifiers: [SwiftUIModifier], in file: SwiftLintFile) -> Bool {
|
||||||
// SwiftUI ViewModifiers are treated as `call` expressions, and we make sure we can get the expression's name.
|
// SwiftUI ViewModifiers are treated as `call` expressions, and we make sure we can get the expression's name.
|
||||||
guard expressionKind == .call, let name = name else {
|
guard expressionKind == .call, let name else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,7 +169,7 @@ extension SourceKittenDictionary {
|
||||||
|
|
||||||
/// Helper to get the value of an argument.
|
/// Helper to get the value of an argument.
|
||||||
func getArgumentValue(in file: SwiftLintFile) -> String? {
|
func getArgumentValue(in file: SwiftLintFile) -> String? {
|
||||||
guard expressionKind == .argument, let bodyByteRange = bodyByteRange else {
|
guard expressionKind == .argument, let bodyByteRange else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,7 +178,7 @@ extension SourceKittenDictionary {
|
||||||
|
|
||||||
/// Helper to get the value of a single unnamed argument to a function call.
|
/// Helper to get the value of a single unnamed argument to a function call.
|
||||||
func getSingleUnnamedArgumentValue(in file: SwiftLintFile) -> String? {
|
func getSingleUnnamedArgumentValue(in file: SwiftLintFile) -> String? {
|
||||||
guard expressionKind == .call, let bodyByteRange = bodyByteRange else {
|
guard expressionKind == .call, let bodyByteRange else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,7 +62,7 @@ enum LegacyFunctionRuleHelper {
|
||||||
|
|
||||||
correctionPositions.append(node.positionAfterSkippingLeadingTrivia)
|
correctionPositions.append(node.positionAfterSkippingLeadingTrivia)
|
||||||
|
|
||||||
let trimmedArguments = node.argumentList.map { $0.trimmed() }
|
let trimmedArguments = node.argumentList.map { $0.trimmingTrailingComma() }
|
||||||
let rewriteStrategy = legacyFunctions[funcName]
|
let rewriteStrategy = legacyFunctions[funcName]
|
||||||
|
|
||||||
let expr: ExprSyntax
|
let expr: ExprSyntax
|
||||||
|
@ -70,20 +70,20 @@ enum LegacyFunctionRuleHelper {
|
||||||
case .equal:
|
case .equal:
|
||||||
expr = "\(trimmedArguments[0]) == \(trimmedArguments[1])"
|
expr = "\(trimmedArguments[0]) == \(trimmedArguments[1])"
|
||||||
case let .property(name: propertyName):
|
case let .property(name: propertyName):
|
||||||
expr = "\(trimmedArguments[0]).\(propertyName)"
|
expr = "\(trimmedArguments[0]).\(raw: propertyName)"
|
||||||
case let .function(name: functionName, argumentLabels: argumentLabels, reversed: reversed):
|
case let .function(name: functionName, argumentLabels: argumentLabels, reversed: reversed):
|
||||||
let arguments = reversed ? trimmedArguments.reversed() : trimmedArguments
|
let arguments = reversed ? trimmedArguments.reversed() : trimmedArguments
|
||||||
let params = zip(argumentLabels, arguments.dropFirst())
|
let params = zip(argumentLabels, arguments.dropFirst())
|
||||||
.map { $0.isEmpty ? "\($1)" : "\($0): \($1)" }
|
.map { $0.isEmpty ? "\($1)" : "\($0): \($1)" }
|
||||||
.joined(separator: ", ")
|
.joined(separator: ", ")
|
||||||
expr = "\(arguments[0]).\(functionName)(\(params))"
|
expr = "\(arguments[0]).\(raw: functionName)(\(raw: params))"
|
||||||
case .none:
|
case .none:
|
||||||
return super.visit(node)
|
return super.visit(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
return expr
|
return expr
|
||||||
.withLeadingTrivia(node.leadingTrivia ?? .zero)
|
.with(\.leadingTrivia, node.leadingTrivia)
|
||||||
.withTrailingTrivia(node.trailingTrivia ?? .zero)
|
.with(\.trailingTrivia, node.trailingTrivia)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,10 +103,7 @@ private extension FunctionCallExprSyntax {
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension TupleExprElementSyntax {
|
private extension TupleExprElementSyntax {
|
||||||
func trimmed() -> TupleExprElementSyntax {
|
func trimmingTrailingComma() -> TupleExprElementSyntax {
|
||||||
self
|
self.trimmed.with(\.trailingComma, nil).trimmed
|
||||||
.withoutTrivia()
|
|
||||||
.withTrailingComma(nil)
|
|
||||||
.withoutTrivia()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,7 +1,8 @@
|
||||||
// Generated using Sourcery 1.9.0 — https://github.com/krzysztofzablocki/Sourcery
|
// Generated using Sourcery 2.0.2 — https://github.com/krzysztofzablocki/Sourcery
|
||||||
// DO NOT EDIT
|
// DO NOT EDIT
|
||||||
|
|
||||||
/// The rule list containing all available rules built into SwiftLint.
|
/// The rule list containing all available rules built into SwiftLint.
|
||||||
public let primaryRuleList = RuleList(rules: [
|
public let builtInRules: [Rule.Type] = [
|
||||||
AccessibilityLabelForImageRule.self,
|
AccessibilityLabelForImageRule.self,
|
||||||
AccessibilityTraitForButtonRule.self,
|
AccessibilityTraitForButtonRule.self,
|
||||||
AnonymousArgumentInMultilineClosureRule.self,
|
AnonymousArgumentInMultilineClosureRule.self,
|
||||||
|
@ -9,6 +10,7 @@ public let primaryRuleList = RuleList(rules: [
|
||||||
ArrayInitRule.self,
|
ArrayInitRule.self,
|
||||||
AttributesRule.self,
|
AttributesRule.self,
|
||||||
BalancedXCTestLifecycleRule.self,
|
BalancedXCTestLifecycleRule.self,
|
||||||
|
BlanketDisableCommandRule.self,
|
||||||
BlockBasedKVORule.self,
|
BlockBasedKVORule.self,
|
||||||
CaptureVariableRule.self,
|
CaptureVariableRule.self,
|
||||||
ClassDelegateProtocolRule.self,
|
ClassDelegateProtocolRule.self,
|
||||||
|
@ -31,9 +33,9 @@ public let primaryRuleList = RuleList(rules: [
|
||||||
ContainsOverRangeNilComparisonRule.self,
|
ContainsOverRangeNilComparisonRule.self,
|
||||||
ControlStatementRule.self,
|
ControlStatementRule.self,
|
||||||
ConvenienceTypeRule.self,
|
ConvenienceTypeRule.self,
|
||||||
CustomRules.self,
|
|
||||||
CyclomaticComplexityRule.self,
|
CyclomaticComplexityRule.self,
|
||||||
DeploymentTargetRule.self,
|
DeploymentTargetRule.self,
|
||||||
|
DirectReturnRule.self,
|
||||||
DiscardedNotificationCenterObserverRule.self,
|
DiscardedNotificationCenterObserverRule.self,
|
||||||
DiscouragedAssertRule.self,
|
DiscouragedAssertRule.self,
|
||||||
DiscouragedDirectInitRule.self,
|
DiscouragedDirectInitRule.self,
|
||||||
|
@ -41,6 +43,7 @@ public let primaryRuleList = RuleList(rules: [
|
||||||
DiscouragedObjectLiteralRule.self,
|
DiscouragedObjectLiteralRule.self,
|
||||||
DiscouragedOptionalBooleanRule.self,
|
DiscouragedOptionalBooleanRule.self,
|
||||||
DiscouragedOptionalCollectionRule.self,
|
DiscouragedOptionalCollectionRule.self,
|
||||||
|
DuplicateConditionsRule.self,
|
||||||
DuplicateEnumCasesRule.self,
|
DuplicateEnumCasesRule.self,
|
||||||
DuplicateImportsRule.self,
|
DuplicateImportsRule.self,
|
||||||
DuplicatedKeyInDictionaryLiteralRule.self,
|
DuplicatedKeyInDictionaryLiteralRule.self,
|
||||||
|
@ -87,6 +90,7 @@ public let primaryRuleList = RuleList(rules: [
|
||||||
InclusiveLanguageRule.self,
|
InclusiveLanguageRule.self,
|
||||||
IndentationWidthRule.self,
|
IndentationWidthRule.self,
|
||||||
InertDeferRule.self,
|
InertDeferRule.self,
|
||||||
|
InvalidSwiftLintCommandRule.self,
|
||||||
IsDisjointRule.self,
|
IsDisjointRule.self,
|
||||||
JoinedDefaultParameterRule.self,
|
JoinedDefaultParameterRule.self,
|
||||||
LargeTupleRule.self,
|
LargeTupleRule.self,
|
||||||
|
@ -102,7 +106,7 @@ public let primaryRuleList = RuleList(rules: [
|
||||||
LegacyRandomRule.self,
|
LegacyRandomRule.self,
|
||||||
LetVarWhitespaceRule.self,
|
LetVarWhitespaceRule.self,
|
||||||
LineLengthRule.self,
|
LineLengthRule.self,
|
||||||
LiteralExpressionEndIdentationRule.self,
|
LiteralExpressionEndIndentationRule.self,
|
||||||
LocalDocCommentRule.self,
|
LocalDocCommentRule.self,
|
||||||
LowerACLThanParentRule.self,
|
LowerACLThanParentRule.self,
|
||||||
MarkRule.self,
|
MarkRule.self,
|
||||||
|
@ -137,6 +141,7 @@ public let primaryRuleList = RuleList(rules: [
|
||||||
OverriddenSuperCallRule.self,
|
OverriddenSuperCallRule.self,
|
||||||
OverrideInExtensionRule.self,
|
OverrideInExtensionRule.self,
|
||||||
PatternMatchingKeywordsRule.self,
|
PatternMatchingKeywordsRule.self,
|
||||||
|
PeriodSpacingRule.self,
|
||||||
PreferNimbleRule.self,
|
PreferNimbleRule.self,
|
||||||
PreferSelfInStaticReferencesRule.self,
|
PreferSelfInStaticReferencesRule.self,
|
||||||
PreferSelfTypeOverTypeOfSelfRule.self,
|
PreferSelfTypeOverTypeOfSelfRule.self,
|
||||||
|
@ -160,6 +165,7 @@ public let primaryRuleList = RuleList(rules: [
|
||||||
RedundantNilCoalescingRule.self,
|
RedundantNilCoalescingRule.self,
|
||||||
RedundantObjcAttributeRule.self,
|
RedundantObjcAttributeRule.self,
|
||||||
RedundantOptionalInitializationRule.self,
|
RedundantOptionalInitializationRule.self,
|
||||||
|
RedundantSelfInClosureRule.self,
|
||||||
RedundantSetAccessControlRule.self,
|
RedundantSetAccessControlRule.self,
|
||||||
RedundantStringEnumValueRule.self,
|
RedundantStringEnumValueRule.self,
|
||||||
RedundantTypeAnnotationRule.self,
|
RedundantTypeAnnotationRule.self,
|
||||||
|
@ -173,13 +179,14 @@ public let primaryRuleList = RuleList(rules: [
|
||||||
ShorthandOperatorRule.self,
|
ShorthandOperatorRule.self,
|
||||||
ShorthandOptionalBindingRule.self,
|
ShorthandOptionalBindingRule.self,
|
||||||
SingleTestClassRule.self,
|
SingleTestClassRule.self,
|
||||||
|
SortedEnumCasesRule.self,
|
||||||
SortedFirstLastRule.self,
|
SortedFirstLastRule.self,
|
||||||
SortedImportsRule.self,
|
SortedImportsRule.self,
|
||||||
StatementPositionRule.self,
|
StatementPositionRule.self,
|
||||||
StaticOperatorRule.self,
|
StaticOperatorRule.self,
|
||||||
StrictFilePrivateRule.self,
|
StrictFilePrivateRule.self,
|
||||||
StrongIBOutletRule.self,
|
StrongIBOutletRule.self,
|
||||||
SuperfluousDisableCommandRule.self,
|
SuperfluousElseRule.self,
|
||||||
SwitchCaseAlignmentRule.self,
|
SwitchCaseAlignmentRule.self,
|
||||||
SwitchCaseOnNewlineRule.self,
|
SwitchCaseOnNewlineRule.self,
|
||||||
SyntacticSugarRule.self,
|
SyntacticSugarRule.self,
|
||||||
|
@ -197,6 +204,7 @@ public let primaryRuleList = RuleList(rules: [
|
||||||
TypesafeArrayInitRule.self,
|
TypesafeArrayInitRule.self,
|
||||||
UnavailableConditionRule.self,
|
UnavailableConditionRule.self,
|
||||||
UnavailableFunctionRule.self,
|
UnavailableFunctionRule.self,
|
||||||
|
UnhandledThrowingTaskRule.self,
|
||||||
UnneededBreakInSwitchRule.self,
|
UnneededBreakInSwitchRule.self,
|
||||||
UnneededParenthesesInClosureArgumentRule.self,
|
UnneededParenthesesInClosureArgumentRule.self,
|
||||||
UnownedVariableCaptureRule.self,
|
UnownedVariableCaptureRule.self,
|
||||||
|
@ -222,4 +230,4 @@ public let primaryRuleList = RuleList(rules: [
|
||||||
XCTFailMessageRule.self,
|
XCTFailMessageRule.self,
|
||||||
XCTSpecificMatcherRule.self,
|
XCTSpecificMatcherRule.self,
|
||||||
YodaConditionRule.self
|
YodaConditionRule.self
|
||||||
] + extraRules())
|
]
|
|
@ -23,7 +23,7 @@ enum ImportUsage {
|
||||||
case .unused:
|
case .unused:
|
||||||
return nil
|
return nil
|
||||||
case .missing(let module):
|
case .missing(let module):
|
||||||
return "Missing import for referenced module '\(module)'."
|
return "Missing import for referenced module '\(module)'"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,9 +1,7 @@
|
||||||
import SwiftSyntax
|
import SwiftSyntax
|
||||||
|
|
||||||
struct AnonymousArgumentInMultilineClosureRule: SwiftSyntaxRule, OptInRule, ConfigurationProviderRule {
|
struct AnonymousArgumentInMultilineClosureRule: SwiftSyntaxRule, OptInRule, ConfigurationProviderRule {
|
||||||
var configuration = SeverityConfiguration(.warning)
|
var configuration = SeverityConfiguration<Self>(.warning)
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "anonymous_argument_in_multiline_closure",
|
identifier: "anonymous_argument_in_multiline_closure",
|
||||||
|
@ -50,12 +48,7 @@ private extension AnonymousArgumentInMultilineClosureRule {
|
||||||
override func visit(_ node: ClosureExprSyntax) -> SyntaxVisitorContinueKind {
|
override func visit(_ node: ClosureExprSyntax) -> SyntaxVisitorContinueKind {
|
||||||
let startLocation = locationConverter.location(for: node.leftBrace.positionAfterSkippingLeadingTrivia)
|
let startLocation = locationConverter.location(for: node.leftBrace.positionAfterSkippingLeadingTrivia)
|
||||||
let endLocation = locationConverter.location(for: node.rightBrace.endPositionBeforeTrailingTrivia)
|
let endLocation = locationConverter.location(for: node.rightBrace.endPositionBeforeTrailingTrivia)
|
||||||
|
return startLocation.line == endLocation.line ? .skipChildren : .visitChildren
|
||||||
guard let startLine = startLocation.line, let endLine = endLocation.line, startLine != endLine else {
|
|
||||||
return .skipChildren
|
|
||||||
}
|
|
||||||
|
|
||||||
return .visitChildren
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override func visitPost(_ node: IdentifierExprSyntax) {
|
override func visitPost(_ node: IdentifierExprSyntax) {
|
|
@ -1,14 +1,12 @@
|
||||||
import SwiftSyntax
|
import SwiftSyntax
|
||||||
|
|
||||||
struct BlockBasedKVORule: SwiftSyntaxRule, ConfigurationProviderRule {
|
struct BlockBasedKVORule: SwiftSyntaxRule, ConfigurationProviderRule {
|
||||||
var configuration = SeverityConfiguration(.warning)
|
var configuration = SeverityConfiguration<Self>(.warning)
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "block_based_kvo",
|
identifier: "block_based_kvo",
|
||||||
name: "Block Based KVO",
|
name: "Block Based KVO",
|
||||||
description: "Prefer the new block based KVO API with keypaths when using Swift 3.2 or later.",
|
description: "Prefer the new block based KVO API with keypaths when using Swift 3.2 or later",
|
||||||
kind: .idiomatic,
|
kind: .idiomatic,
|
||||||
nonTriggeringExamples: [
|
nonTriggeringExamples: [
|
||||||
Example(#"""
|
Example(#"""
|
||||||
|
@ -47,13 +45,13 @@ private extension BlockBasedKVORule {
|
||||||
case let parameterList = node.signature.input.parameterList,
|
case let parameterList = node.signature.input.parameterList,
|
||||||
parameterList.count == 4,
|
parameterList.count == 4,
|
||||||
node.identifier.text == "observeValue",
|
node.identifier.text == "observeValue",
|
||||||
parameterList.compactMap(\.firstName?.text) == ["forKeyPath", "of", "change", "context"]
|
parameterList.map(\.firstName.text) == ["forKeyPath", "of", "change", "context"]
|
||||||
else {
|
else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let types = parameterList
|
let types = parameterList
|
||||||
.compactMap { $0.type?.withoutTrivia().description.replacingOccurrences(of: " ", with: "") }
|
.map { $0.type.trimmedDescription.replacingOccurrences(of: " ", with: "") }
|
||||||
let firstTypes = ["String?", "Any?", "[NSKeyValueChangeKey:Any]?", "UnsafeMutableRawPointer?"]
|
let firstTypes = ["String?", "Any?", "[NSKeyValueChangeKey:Any]?", "UnsafeMutableRawPointer?"]
|
||||||
let secondTypes = ["String?", "Any?", "Dictionary<NSKeyValueChangeKey,Any>?", "UnsafeMutableRawPointer?"]
|
let secondTypes = ["String?", "Any?", "Dictionary<NSKeyValueChangeKey,Any>?", "UnsafeMutableRawPointer?"]
|
||||||
if types == firstTypes || types == secondTypes {
|
if types == firstTypes || types == secondTypes {
|
|
@ -1,15 +1,13 @@
|
||||||
import SwiftSyntax
|
import SwiftSyntax
|
||||||
|
|
||||||
struct ConvenienceTypeRule: SwiftSyntaxRule, OptInRule, ConfigurationProviderRule {
|
struct ConvenienceTypeRule: SwiftSyntaxRule, OptInRule, ConfigurationProviderRule {
|
||||||
var configuration = SeverityConfiguration(.warning)
|
var configuration = SeverityConfiguration<Self>(.warning)
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "convenience_type",
|
identifier: "convenience_type",
|
||||||
name: "Convenience Type",
|
name: "Convenience Type",
|
||||||
description: "Types used for hosting only static members should be implemented as a caseless enum " +
|
description: "Types used for hosting only static members should be implemented as a caseless enum " +
|
||||||
"to avoid instantiation.",
|
"to avoid instantiation",
|
||||||
kind: .idiomatic,
|
kind: .idiomatic,
|
||||||
nonTriggeringExamples: [
|
nonTriggeringExamples: [
|
||||||
Example("""
|
Example("""
|
||||||
|
@ -127,13 +125,21 @@ private extension ConvenienceTypeRule {
|
||||||
override var skippableDeclarations: [DeclSyntaxProtocol.Type] { [ProtocolDeclSyntax.self] }
|
override var skippableDeclarations: [DeclSyntaxProtocol.Type] { [ProtocolDeclSyntax.self] }
|
||||||
|
|
||||||
override func visitPost(_ node: StructDeclSyntax) {
|
override func visitPost(_ node: StructDeclSyntax) {
|
||||||
if hasViolation(inheritance: node.inheritanceClause, attributes: node.attributes, members: node.members) {
|
if hasViolation(
|
||||||
|
inheritance: node.inheritanceClause,
|
||||||
|
attributes: node.attributes,
|
||||||
|
members: node.memberBlock
|
||||||
|
) {
|
||||||
violations.append(node.structKeyword.positionAfterSkippingLeadingTrivia)
|
violations.append(node.structKeyword.positionAfterSkippingLeadingTrivia)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func visitPost(_ node: ClassDeclSyntax) {
|
override func visitPost(_ node: ClassDeclSyntax) {
|
||||||
if hasViolation(inheritance: node.inheritanceClause, attributes: node.attributes, members: node.members) {
|
if hasViolation(
|
||||||
|
inheritance: node.inheritanceClause,
|
||||||
|
attributes: node.attributes,
|
||||||
|
members: node.memberBlock
|
||||||
|
) {
|
||||||
violations.append(node.classKeyword.positionAfterSkippingLeadingTrivia)
|
violations.append(node.classKeyword.positionAfterSkippingLeadingTrivia)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -198,15 +204,11 @@ private extension TypeInheritanceClauseSyntax? {
|
||||||
|
|
||||||
private extension AttributeListSyntax? {
|
private extension AttributeListSyntax? {
|
||||||
var containsObjcMembers: Bool {
|
var containsObjcMembers: Bool {
|
||||||
self?.contains { elem in
|
contains(attributeNamed: "objcMembers")
|
||||||
elem.as(AttributeSyntax.self)?.attributeName.tokenKind == .identifier("objcMembers")
|
|
||||||
} ?? false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var containsObjc: Bool {
|
var containsObjc: Bool {
|
||||||
self?.contains { elem in
|
contains(attributeNamed: "objc")
|
||||||
elem.as(AttributeSyntax.self)?.attributeName.tokenKind == .contextualKeyword("objc")
|
|
||||||
} ?? false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,12 +220,12 @@ private extension AttributeListSyntax? {
|
||||||
|
|
||||||
return attrs.contains { elem in
|
return attrs.contains { elem in
|
||||||
guard let attr = elem.as(AttributeSyntax.self),
|
guard let attr = elem.as(AttributeSyntax.self),
|
||||||
let arguments = attr.argument?.as(AvailabilitySpecListSyntax.self) else {
|
let arguments = attr.argument?.as(AvailabilitySpecListSyntax.self) else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return attr.attributeName.tokenKind == .contextualKeyword("available") && arguments.contains { arg in
|
return attr.attributeNameText == "available" && arguments.contains { arg in
|
||||||
arg.entry.as(TokenSyntax.self)?.tokenKind == .contextualKeyword("unavailable")
|
arg.entry.as(TokenSyntax.self)?.tokenKind.isUnavailableKeyword == true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,9 +1,7 @@
|
||||||
import SwiftSyntax
|
import SwiftSyntax
|
||||||
|
|
||||||
struct DiscouragedAssertRule: SwiftSyntaxRule, OptInRule, ConfigurationProviderRule {
|
struct DiscouragedAssertRule: SwiftSyntaxRule, OptInRule, ConfigurationProviderRule {
|
||||||
// MARK: - Properties
|
var configuration = SeverityConfiguration<Self>(.warning)
|
||||||
|
|
||||||
var configuration = SeverityConfiguration(.warning)
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "discouraged_assert",
|
identifier: "discouraged_assert",
|
||||||
|
@ -25,12 +23,6 @@ struct DiscouragedAssertRule: SwiftSyntaxRule, OptInRule, ConfigurationProviderR
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
// MARK: - Life cycle
|
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
// MARK: - Public
|
|
||||||
|
|
||||||
func makeVisitor(file: SwiftLintFile) -> ViolationsSyntaxVisitor {
|
func makeVisitor(file: SwiftLintFile) -> ViolationsSyntaxVisitor {
|
||||||
Visitor(viewMode: .sourceAccurate)
|
Visitor(viewMode: .sourceAccurate)
|
||||||
}
|
}
|
||||||
|
@ -39,11 +31,11 @@ struct DiscouragedAssertRule: SwiftSyntaxRule, OptInRule, ConfigurationProviderR
|
||||||
private extension DiscouragedAssertRule {
|
private extension DiscouragedAssertRule {
|
||||||
final class Visitor: ViolationsSyntaxVisitor {
|
final class Visitor: ViolationsSyntaxVisitor {
|
||||||
override func visitPost(_ node: FunctionCallExprSyntax) {
|
override func visitPost(_ node: FunctionCallExprSyntax) {
|
||||||
guard node.calledExpression.as(IdentifierExprSyntax.self)?.identifier.withoutTrivia().text == "assert",
|
guard node.calledExpression.as(IdentifierExprSyntax.self)?.identifier.text == "assert",
|
||||||
let firstArg = node.argumentList.first,
|
let firstArg = node.argumentList.first,
|
||||||
firstArg.label == nil,
|
firstArg.label == nil,
|
||||||
let boolExpr = firstArg.expression.as(BooleanLiteralExprSyntax.self),
|
let boolExpr = firstArg.expression.as(BooleanLiteralExprSyntax.self),
|
||||||
boolExpr.booleanLiteral.tokenKind == .falseKeyword else {
|
boolExpr.booleanLiteral.tokenKind == .keyword(.false) else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
import SwiftSyntax
|
import SwiftSyntax
|
||||||
|
|
||||||
struct DiscouragedNoneNameRule: SwiftSyntaxRule, OptInRule, ConfigurationProviderRule {
|
struct DiscouragedNoneNameRule: SwiftSyntaxRule, OptInRule, ConfigurationProviderRule {
|
||||||
var configuration = SeverityConfiguration(.warning)
|
var configuration = SeverityConfiguration<Self>(.warning)
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static var description = RuleDescription(
|
static var description = RuleDescription(
|
||||||
identifier: "discouraged_none_name",
|
identifier: "discouraged_none_name",
|
||||||
name: "Discouraged None Name",
|
name: "Discouraged None Name",
|
||||||
description: "Discourages name cases/static members `none`, which can conflict with `Optional<T>.none`.",
|
description: "Enum cases and static members named `none` are discouraged as they can conflict with " +
|
||||||
|
"`Optional<T>.none`.",
|
||||||
kind: .idiomatic,
|
kind: .idiomatic,
|
||||||
nonTriggeringExamples: [
|
nonTriggeringExamples: [
|
||||||
// Should not trigger unless exactly matches "none"
|
// Should not trigger unless exactly matches "none"
|
||||||
|
@ -206,7 +205,7 @@ private extension DiscouragedNoneNameRule {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
guard let type = type else {
|
guard let type else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,9 +223,9 @@ private extension DiscouragedNoneNameRule {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func reason(type: String) -> String {
|
private func reason(type: String) -> String {
|
||||||
let reason = "Avoid naming \(type) `none` as the compiler can think you mean `Optional<T>.none`."
|
let reason = "Avoid naming \(type) `none` as the compiler can think you mean `Optional<T>.none`"
|
||||||
let recommendation = "Consider using an Optional value instead."
|
let recommendation = "consider using an Optional value instead"
|
||||||
return "\(reason) \(recommendation)"
|
return "\(reason); \(recommendation)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,14 +1,12 @@
|
||||||
import SwiftSyntax
|
import SwiftSyntax
|
||||||
|
|
||||||
struct DiscouragedObjectLiteralRule: SwiftSyntaxRule, OptInRule, ConfigurationProviderRule {
|
struct DiscouragedObjectLiteralRule: SwiftSyntaxRule, OptInRule, ConfigurationProviderRule {
|
||||||
var configuration = ObjectLiteralConfiguration()
|
var configuration = DiscouragedObjectLiteralConfiguration()
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "discouraged_object_literal",
|
identifier: "discouraged_object_literal",
|
||||||
name: "Discouraged Object Literal",
|
name: "Discouraged Object Literal",
|
||||||
description: "Prefer initializers over object literals.",
|
description: "Prefer initializers over object literals",
|
||||||
kind: .idiomatic,
|
kind: .idiomatic,
|
||||||
nonTriggeringExamples: [
|
nonTriggeringExamples: [
|
||||||
Example("let image = UIImage(named: aVariable)"),
|
Example("let image = UIImage(named: aVariable)"),
|
||||||
|
@ -31,9 +29,9 @@ struct DiscouragedObjectLiteralRule: SwiftSyntaxRule, OptInRule, ConfigurationPr
|
||||||
|
|
||||||
private extension DiscouragedObjectLiteralRule {
|
private extension DiscouragedObjectLiteralRule {
|
||||||
final class Visitor: ViolationsSyntaxVisitor {
|
final class Visitor: ViolationsSyntaxVisitor {
|
||||||
private let configuration: ObjectLiteralConfiguration
|
private let configuration: ConfigurationType
|
||||||
|
|
||||||
init(configuration: ObjectLiteralConfiguration) {
|
init(configuration: ConfigurationType) {
|
||||||
self.configuration = configuration
|
self.configuration = configuration
|
||||||
super.init(viewMode: .sourceAccurate)
|
super.init(viewMode: .sourceAccurate)
|
||||||
}
|
}
|
|
@ -1,14 +1,12 @@
|
||||||
import SwiftSyntax
|
import SwiftSyntax
|
||||||
|
|
||||||
struct DiscouragedOptionalBooleanRule: OptInRule, ConfigurationProviderRule, SwiftSyntaxRule {
|
struct DiscouragedOptionalBooleanRule: OptInRule, ConfigurationProviderRule, SwiftSyntaxRule {
|
||||||
var configuration = SeverityConfiguration(.warning)
|
var configuration = SeverityConfiguration<Self>(.warning)
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "discouraged_optional_boolean",
|
identifier: "discouraged_optional_boolean",
|
||||||
name: "Discouraged Optional Boolean",
|
name: "Discouraged Optional Boolean",
|
||||||
description: "Prefer non-optional booleans over optional booleans.",
|
description: "Prefer non-optional booleans over optional booleans",
|
||||||
kind: .idiomatic,
|
kind: .idiomatic,
|
||||||
nonTriggeringExamples: DiscouragedOptionalBooleanRuleExamples.nonTriggeringExamples,
|
nonTriggeringExamples: DiscouragedOptionalBooleanRuleExamples.nonTriggeringExamples,
|
||||||
triggeringExamples: DiscouragedOptionalBooleanRuleExamples.triggeringExamples
|
triggeringExamples: DiscouragedOptionalBooleanRuleExamples.triggeringExamples
|
|
@ -1,14 +1,12 @@
|
||||||
import SourceKittenFramework
|
import SourceKittenFramework
|
||||||
|
|
||||||
struct DiscouragedOptionalCollectionRule: ASTRule, OptInRule, ConfigurationProviderRule {
|
struct DiscouragedOptionalCollectionRule: ASTRule, OptInRule, ConfigurationProviderRule {
|
||||||
var configuration = SeverityConfiguration(.warning)
|
var configuration = SeverityConfiguration<Self>(.warning)
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "discouraged_optional_collection",
|
identifier: "discouraged_optional_collection",
|
||||||
name: "Discouraged Optional Collection",
|
name: "Discouraged Optional Collection",
|
||||||
description: "Prefer empty collection over optional collection.",
|
description: "Prefer empty collection over optional collection",
|
||||||
kind: .idiomatic,
|
kind: .idiomatic,
|
||||||
nonTriggeringExamples: DiscouragedOptionalCollectionExamples.nonTriggeringExamples,
|
nonTriggeringExamples: DiscouragedOptionalCollectionExamples.nonTriggeringExamples,
|
||||||
triggeringExamples: DiscouragedOptionalCollectionExamples.triggeringExamples
|
triggeringExamples: DiscouragedOptionalCollectionExamples.triggeringExamples
|
||||||
|
@ -17,7 +15,7 @@ struct DiscouragedOptionalCollectionRule: ASTRule, OptInRule, ConfigurationProvi
|
||||||
func validate(file: SwiftLintFile,
|
func validate(file: SwiftLintFile,
|
||||||
kind: SwiftDeclarationKind,
|
kind: SwiftDeclarationKind,
|
||||||
dictionary: SourceKittenDictionary) -> [StyleViolation] {
|
dictionary: SourceKittenDictionary) -> [StyleViolation] {
|
||||||
let offsets = variableViolations(file: file, kind: kind, dictionary: dictionary) +
|
let offsets = variableViolations(kind: kind, dictionary: dictionary) +
|
||||||
functionViolations(file: file, kind: kind, dictionary: dictionary)
|
functionViolations(file: file, kind: kind, dictionary: dictionary)
|
||||||
|
|
||||||
return offsets.map {
|
return offsets.map {
|
||||||
|
@ -29,9 +27,7 @@ struct DiscouragedOptionalCollectionRule: ASTRule, OptInRule, ConfigurationProvi
|
||||||
|
|
||||||
// MARK: - Private
|
// MARK: - Private
|
||||||
|
|
||||||
private func variableViolations(file: SwiftLintFile,
|
private func variableViolations(kind: SwiftDeclarationKind, dictionary: SourceKittenDictionary) -> [ByteCount] {
|
||||||
kind: SwiftDeclarationKind,
|
|
||||||
dictionary: SourceKittenDictionary) -> [ByteCount] {
|
|
||||||
guard
|
guard
|
||||||
SwiftDeclarationKind.variableKinds.contains(kind),
|
SwiftDeclarationKind.variableKinds.contains(kind),
|
||||||
let offset = dictionary.offset,
|
let offset = dictionary.offset,
|
|
@ -2,7 +2,7 @@ import Foundation
|
||||||
import SourceKittenFramework
|
import SourceKittenFramework
|
||||||
|
|
||||||
struct DuplicateImportsRule: ConfigurationProviderRule, CorrectableRule {
|
struct DuplicateImportsRule: ConfigurationProviderRule, CorrectableRule {
|
||||||
var configuration = SeverityConfiguration(.warning)
|
var configuration = SeverityConfiguration<Self>(.warning)
|
||||||
|
|
||||||
// List of all possible import kinds
|
// List of all possible import kinds
|
||||||
static let importKinds = [
|
static let importKinds = [
|
||||||
|
@ -11,12 +11,10 @@ struct DuplicateImportsRule: ConfigurationProviderRule, CorrectableRule {
|
||||||
"var", "func"
|
"var", "func"
|
||||||
]
|
]
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "duplicate_imports",
|
identifier: "duplicate_imports",
|
||||||
name: "Duplicate Imports",
|
name: "Duplicate Imports",
|
||||||
description: "Imports should be unique.",
|
description: "Imports should be unique",
|
||||||
kind: .idiomatic,
|
kind: .idiomatic,
|
||||||
nonTriggeringExamples: DuplicateImportsRuleExamples.nonTriggeringExamples,
|
nonTriggeringExamples: DuplicateImportsRuleExamples.nonTriggeringExamples,
|
||||||
triggeringExamples: DuplicateImportsRuleExamples.triggeringExamples,
|
triggeringExamples: DuplicateImportsRuleExamples.triggeringExamples,
|
||||||
|
@ -194,7 +192,7 @@ private extension Line {
|
||||||
|
|
||||||
/// For "import A.B.C" returns slices [["A", "B", "C"], ["A", "B"], ["A"]]
|
/// For "import A.B.C" returns slices [["A", "B", "C"], ["A", "B"], ["A"]]
|
||||||
var importSlices: [ImportSlice] {
|
var importSlices: [ImportSlice] {
|
||||||
guard let importIdentifier = importIdentifier else { return [] }
|
guard let importIdentifier else { return [] }
|
||||||
|
|
||||||
let importedSubpathParts = importIdentifier.split(separator: ".").map { String($0) }
|
let importedSubpathParts = importIdentifier.split(separator: ".").map { String($0) }
|
||||||
guard !importedSubpathParts.isEmpty else { return [] }
|
guard !importedSubpathParts.isEmpty else { return [] }
|
|
@ -4,14 +4,12 @@ import SourceKittenFramework
|
||||||
private typealias SourceKittenElement = SourceKittenDictionary
|
private typealias SourceKittenElement = SourceKittenDictionary
|
||||||
|
|
||||||
struct ExplicitACLRule: OptInRule, ConfigurationProviderRule {
|
struct ExplicitACLRule: OptInRule, ConfigurationProviderRule {
|
||||||
var configuration = SeverityConfiguration(.warning)
|
var configuration = SeverityConfiguration<Self>(.warning)
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "explicit_acl",
|
identifier: "explicit_acl",
|
||||||
name: "Explicit ACL",
|
name: "Explicit ACL",
|
||||||
description: "All declarations should specify Access Control Level keywords explicitly.",
|
description: "All declarations should specify Access Control Level keywords explicitly",
|
||||||
kind: .idiomatic,
|
kind: .idiomatic,
|
||||||
nonTriggeringExamples: [
|
nonTriggeringExamples: [
|
||||||
Example("internal enum A {}\n"),
|
Example("internal enum A {}\n"),
|
||||||
|
@ -188,7 +186,7 @@ private extension SwiftDeclarationKind {
|
||||||
.functionMethodInstance, .functionMethodStatic, .functionOperator, .functionOperatorInfix,
|
.functionMethodInstance, .functionMethodStatic, .functionOperator, .functionOperatorInfix,
|
||||||
.functionOperatorPostfix, .functionOperatorPrefix, .functionSubscript, .protocol, .opaqueType:
|
.functionOperatorPostfix, .functionOperatorPrefix, .functionSubscript, .protocol, .opaqueType:
|
||||||
return true
|
return true
|
||||||
case .class, .enum, .extension, .extensionClass, .extensionEnum,
|
case .actor, .class, .enum, .extension, .extensionClass, .extensionEnum,
|
||||||
.extensionProtocol, .extensionStruct, .struct:
|
.extensionProtocol, .extensionStruct, .struct:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
|
@ -1,14 +1,12 @@
|
||||||
import SwiftSyntax
|
import SwiftSyntax
|
||||||
|
|
||||||
struct ExplicitEnumRawValueRule: SwiftSyntaxRule, OptInRule, ConfigurationProviderRule {
|
struct ExplicitEnumRawValueRule: SwiftSyntaxRule, OptInRule, ConfigurationProviderRule {
|
||||||
var configuration = SeverityConfiguration(.warning)
|
var configuration = SeverityConfiguration<Self>(.warning)
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "explicit_enum_raw_value",
|
identifier: "explicit_enum_raw_value",
|
||||||
name: "Explicit Enum Raw Value",
|
name: "Explicit Enum Raw Value",
|
||||||
description: "Enums should be explicitly assigned their raw values.",
|
description: "Enums should be explicitly assigned their raw values",
|
||||||
kind: .idiomatic,
|
kind: .idiomatic,
|
||||||
nonTriggeringExamples: [
|
nonTriggeringExamples: [
|
||||||
Example("""
|
Example("""
|
||||||
|
@ -89,17 +87,14 @@ private extension ExplicitEnumRawValueRule {
|
||||||
override var skippableDeclarations: [DeclSyntaxProtocol.Type] { [ProtocolDeclSyntax.self] }
|
override var skippableDeclarations: [DeclSyntaxProtocol.Type] { [ProtocolDeclSyntax.self] }
|
||||||
|
|
||||||
override func visitPost(_ node: EnumCaseElementSyntax) {
|
override func visitPost(_ node: EnumCaseElementSyntax) {
|
||||||
if node.rawValue == nil,
|
if node.rawValue == nil, node.enclosingEnum()?.supportsRawValues == true {
|
||||||
let enclosingEnum = Syntax(node).enclosingEnum(),
|
|
||||||
let inheritance = enclosingEnum.inheritanceClause,
|
|
||||||
inheritance.supportsRawValue {
|
|
||||||
violations.append(node.identifier.positionAfterSkippingLeadingTrivia)
|
violations.append(node.identifier.positionAfterSkippingLeadingTrivia)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension Syntax {
|
private extension SyntaxProtocol {
|
||||||
func enclosingEnum() -> EnumDeclSyntax? {
|
func enclosingEnum() -> EnumDeclSyntax? {
|
||||||
if let node = self.as(EnumDeclSyntax.self) {
|
if let node = self.as(EnumDeclSyntax.self) {
|
||||||
return node
|
return node
|
||||||
|
@ -108,23 +103,3 @@ private extension Syntax {
|
||||||
return parent?.enclosingEnum()
|
return parent?.enclosingEnum()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension TypeInheritanceClauseSyntax {
|
|
||||||
var supportsRawValue: Bool {
|
|
||||||
// Check if it's an enum which supports raw values
|
|
||||||
let implicitRawValueSet: Set<String> = [
|
|
||||||
"Int", "Int8", "Int16", "Int32", "Int64",
|
|
||||||
"UInt", "UInt8", "UInt16", "UInt32", "UInt64",
|
|
||||||
"Double", "Float", "Float80", "Decimal", "NSNumber",
|
|
||||||
"NSDecimalNumber", "NSInteger", "String"
|
|
||||||
]
|
|
||||||
|
|
||||||
return inheritedTypeCollection.contains { element in
|
|
||||||
guard let identifier = element.typeName.as(SimpleTypeIdentifierSyntax.self)?.name.text else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return implicitRawValueSet.contains(identifier)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,14 +2,12 @@ import SwiftSyntax
|
||||||
import SwiftSyntaxBuilder
|
import SwiftSyntaxBuilder
|
||||||
|
|
||||||
struct ExplicitInitRule: SwiftSyntaxCorrectableRule, ConfigurationProviderRule, OptInRule {
|
struct ExplicitInitRule: SwiftSyntaxCorrectableRule, ConfigurationProviderRule, OptInRule {
|
||||||
var configuration = SeverityConfiguration(.warning)
|
var configuration = SeverityConfiguration<Self>(.warning)
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "explicit_init",
|
identifier: "explicit_init",
|
||||||
name: "Explicit Init",
|
name: "Explicit Init",
|
||||||
description: "Explicitly calling .init() should be avoided.",
|
description: "Explicitly calling .init() should be avoided",
|
||||||
kind: .idiomatic,
|
kind: .idiomatic,
|
||||||
nonTriggeringExamples: [
|
nonTriggeringExamples: [
|
||||||
Example("""
|
Example("""
|
||||||
|
@ -218,14 +216,15 @@ private extension ExplicitInitRule {
|
||||||
}
|
}
|
||||||
|
|
||||||
correctionPositions.append(violationPosition)
|
correctionPositions.append(violationPosition)
|
||||||
return super.visit(node.withCalledExpression("\(calledBase.withoutTrivia())"))
|
let newNode = node.with(\.calledExpression, calledBase.trimmed)
|
||||||
|
return super.visit(newNode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension MemberAccessExprSyntax {
|
private extension MemberAccessExprSyntax {
|
||||||
var explicitInitPosition: AbsolutePosition? {
|
var explicitInitPosition: AbsolutePosition? {
|
||||||
if let base = base, base.isTypeReferenceLike, name.text == "init" {
|
if let base, base.isTypeReferenceLike, name.text == "init" {
|
||||||
return base.endPositionBeforeTrailingTrivia
|
return base.endPositionBeforeTrailingTrivia
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
|
@ -1,14 +1,12 @@
|
||||||
import SwiftSyntax
|
import SwiftSyntax
|
||||||
|
|
||||||
struct ExplicitTopLevelACLRule: SwiftSyntaxRule, OptInRule, ConfigurationProviderRule {
|
struct ExplicitTopLevelACLRule: SwiftSyntaxRule, OptInRule, ConfigurationProviderRule {
|
||||||
var configuration = SeverityConfiguration(.warning)
|
var configuration = SeverityConfiguration<Self>(.warning)
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "explicit_top_level_acl",
|
identifier: "explicit_top_level_acl",
|
||||||
name: "Explicit Top Level ACL",
|
name: "Explicit Top Level ACL",
|
||||||
description: "Top-level declarations should specify Access Control Level keywords explicitly.",
|
description: "Top-level declarations should specify Access Control Level keywords explicitly",
|
||||||
kind: .idiomatic,
|
kind: .idiomatic,
|
||||||
nonTriggeringExamples: [
|
nonTriggeringExamples: [
|
||||||
Example("internal enum A {}\n"),
|
Example("internal enum A {}\n"),
|
||||||
|
@ -83,7 +81,7 @@ private extension ExplicitTopLevelACLRule {
|
||||||
|
|
||||||
override func visitPost(_ node: VariableDeclSyntax) {
|
override func visitPost(_ node: VariableDeclSyntax) {
|
||||||
if hasViolation(modifiers: node.modifiers) {
|
if hasViolation(modifiers: node.modifiers) {
|
||||||
violations.append(node.letOrVarKeyword.positionAfterSkippingLeadingTrivia)
|
violations.append(node.bindingKeyword.positionAfterSkippingLeadingTrivia)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,7 +94,7 @@ private extension ExplicitTopLevelACLRule {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func hasViolation(modifiers: ModifierListSyntax?) -> Bool {
|
private func hasViolation(modifiers: ModifierListSyntax?) -> Bool {
|
||||||
guard let modifiers = modifiers else {
|
guard let modifiers else {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,11 +106,11 @@ private extension ExplicitTopLevelACLRule {
|
||||||
private extension DeclModifierSyntax {
|
private extension DeclModifierSyntax {
|
||||||
var isACLModifier: Bool {
|
var isACLModifier: Bool {
|
||||||
let aclModifiers: Set<TokenKind> = [
|
let aclModifiers: Set<TokenKind> = [
|
||||||
.privateKeyword,
|
.keyword(.private),
|
||||||
.fileprivateKeyword,
|
.keyword(.fileprivate),
|
||||||
.internalKeyword,
|
.keyword(.internal),
|
||||||
.publicKeyword,
|
.keyword(.public),
|
||||||
.contextualKeyword("open")
|
.keyword(.open)
|
||||||
]
|
]
|
||||||
|
|
||||||
return detail == nil && aclModifiers.contains(name.tokenKind)
|
return detail == nil && aclModifiers.contains(name.tokenKind)
|
|
@ -0,0 +1,142 @@
|
||||||
|
import SwiftSyntax
|
||||||
|
|
||||||
|
struct ExplicitTypeInterfaceRule: OptInRule, ConfigurationProviderRule, SwiftSyntaxRule {
|
||||||
|
var configuration = ExplicitTypeInterfaceConfiguration()
|
||||||
|
|
||||||
|
static let description = RuleDescription(
|
||||||
|
identifier: "explicit_type_interface",
|
||||||
|
name: "Explicit Type Interface",
|
||||||
|
description: "Properties should have a type interface",
|
||||||
|
kind: .idiomatic,
|
||||||
|
nonTriggeringExamples: [
|
||||||
|
Example("""
|
||||||
|
class Foo {
|
||||||
|
var myVar: Int? = 0
|
||||||
|
}
|
||||||
|
"""),
|
||||||
|
Example("""
|
||||||
|
class Foo {
|
||||||
|
let myVar: Int? = 0, s: String = ""
|
||||||
|
}
|
||||||
|
"""),
|
||||||
|
Example("""
|
||||||
|
class Foo {
|
||||||
|
static var myVar: Int? = 0
|
||||||
|
}
|
||||||
|
"""),
|
||||||
|
Example("""
|
||||||
|
class Foo {
|
||||||
|
class var myVar: Int? = 0
|
||||||
|
}
|
||||||
|
"""),
|
||||||
|
Example("""
|
||||||
|
func f() {
|
||||||
|
if case .failure(let error) = errorCompletion {}
|
||||||
|
}
|
||||||
|
""", excludeFromDocumentation: true)
|
||||||
|
],
|
||||||
|
triggeringExamples: [
|
||||||
|
Example("""
|
||||||
|
class Foo {
|
||||||
|
var ↓myVar = 0
|
||||||
|
}
|
||||||
|
"""),
|
||||||
|
Example("""
|
||||||
|
class Foo {
|
||||||
|
let ↓mylet = 0
|
||||||
|
}
|
||||||
|
"""),
|
||||||
|
Example("""
|
||||||
|
class Foo {
|
||||||
|
static var ↓myStaticVar = 0
|
||||||
|
}
|
||||||
|
"""),
|
||||||
|
Example("""
|
||||||
|
class Foo {
|
||||||
|
class var ↓myClassVar = 0
|
||||||
|
}
|
||||||
|
"""),
|
||||||
|
Example("""
|
||||||
|
class Foo {
|
||||||
|
let ↓myVar = Int(0), ↓s = ""
|
||||||
|
}
|
||||||
|
"""),
|
||||||
|
Example("""
|
||||||
|
class Foo {
|
||||||
|
let ↓myVar = Set<Int>(0)
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
func makeVisitor(file: SwiftLintFile) -> ViolationsSyntaxVisitor {
|
||||||
|
Visitor(configuration: configuration)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Visitor: ViolationsSyntaxVisitor {
|
||||||
|
let configuration: ExplicitTypeInterfaceConfiguration
|
||||||
|
|
||||||
|
override var skippableDeclarations: [DeclSyntaxProtocol.Type] { [ProtocolDeclSyntax.self] }
|
||||||
|
|
||||||
|
init(configuration: ExplicitTypeInterfaceConfiguration) {
|
||||||
|
self.configuration = configuration
|
||||||
|
super.init(viewMode: .sourceAccurate)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func visitPost(_ node: VariableDeclSyntax) {
|
||||||
|
if node.modifiers.isClass {
|
||||||
|
if configuration.allowedKinds.contains(.class) {
|
||||||
|
checkViolation(node)
|
||||||
|
}
|
||||||
|
} else if node.modifiers.isStatic {
|
||||||
|
if configuration.allowedKinds.contains(.static) {
|
||||||
|
checkViolation(node)
|
||||||
|
}
|
||||||
|
} else if node.parent?.is(MemberDeclListItemSyntax.self) == true {
|
||||||
|
if configuration.allowedKinds.contains(.instance) {
|
||||||
|
checkViolation(node)
|
||||||
|
}
|
||||||
|
} else if node.parent?.is(CodeBlockItemSyntax.self) == true {
|
||||||
|
if configuration.allowedKinds.contains(.local) {
|
||||||
|
checkViolation(node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func checkViolation(_ node: VariableDeclSyntax) {
|
||||||
|
for binding in node.bindings {
|
||||||
|
if configuration.allowRedundancy, let initializer = binding.initializer,
|
||||||
|
initializer.isTypeConstructor || initializer.isTypeReference {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if binding.typeAnnotation == nil {
|
||||||
|
violations.append(binding.positionAfterSkippingLeadingTrivia)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension InitializerClauseSyntax {
|
||||||
|
var isTypeConstructor: Bool {
|
||||||
|
if value.as(FunctionCallExprSyntax.self)?.callsPotentialType == true {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if let tryExpr = value.as(TryExprSyntax.self),
|
||||||
|
tryExpr.expression.as(FunctionCallExprSyntax.self)?.callsPotentialType == true {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var isTypeReference: Bool {
|
||||||
|
value.as(MemberAccessExprSyntax.self)?.name.tokenKind == .keyword(.self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension FunctionCallExprSyntax {
|
||||||
|
var callsPotentialType: Bool {
|
||||||
|
let name = calledExpression.debugDescription
|
||||||
|
return name.first?.isUppercase == true || (name.first == "[" && name.last == "]")
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,9 +2,7 @@ import Foundation
|
||||||
import SourceKittenFramework
|
import SourceKittenFramework
|
||||||
|
|
||||||
struct ExtensionAccessModifierRule: ASTRule, ConfigurationProviderRule, OptInRule {
|
struct ExtensionAccessModifierRule: ASTRule, ConfigurationProviderRule, OptInRule {
|
||||||
var configuration = SeverityConfiguration(.warning)
|
var configuration = SeverityConfiguration<Self>(.warning)
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "extension_access_modifier",
|
identifier: "extension_access_modifier",
|
||||||
|
@ -157,9 +155,7 @@ struct ExtensionAccessModifierRule: ASTRule, ConfigurationProviderRule, OptInRul
|
||||||
// attributeBuiltin (`final` for example) tokens between them
|
// attributeBuiltin (`final` for example) tokens between them
|
||||||
let length = typeOffset - previousInternalByteRange.location
|
let length = typeOffset - previousInternalByteRange.location
|
||||||
let range = ByteRange(location: previousInternalByteRange.location, length: length)
|
let range = ByteRange(location: previousInternalByteRange.location, length: length)
|
||||||
let internalBelongsToType = Set(file.syntaxMap.kinds(inByteRange: range)) == [.attributeBuiltin]
|
return Set(file.syntaxMap.kinds(inByteRange: range)) == [.attributeBuiltin]
|
||||||
|
|
||||||
return internalBelongsToType
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return violationOffsets.map {
|
return violationOffsets.map {
|
|
@ -1,14 +1,12 @@
|
||||||
import SwiftSyntax
|
import SwiftSyntax
|
||||||
|
|
||||||
struct FallthroughRule: SwiftSyntaxRule, ConfigurationProviderRule, OptInRule {
|
struct FallthroughRule: SwiftSyntaxRule, ConfigurationProviderRule, OptInRule {
|
||||||
var configuration = SeverityConfiguration(.warning)
|
var configuration = SeverityConfiguration<Self>(.warning)
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "fallthrough",
|
identifier: "fallthrough",
|
||||||
name: "Fallthrough",
|
name: "Fallthrough",
|
||||||
description: "Fallthrough should be avoided.",
|
description: "Fallthrough should be avoided",
|
||||||
kind: .idiomatic,
|
kind: .idiomatic,
|
||||||
nonTriggeringExamples: [
|
nonTriggeringExamples: [
|
||||||
Example("""
|
Example("""
|
|
@ -1,14 +1,12 @@
|
||||||
import SwiftSyntax
|
import SwiftSyntax
|
||||||
|
|
||||||
struct FatalErrorMessageRule: SwiftSyntaxRule, ConfigurationProviderRule, OptInRule {
|
struct FatalErrorMessageRule: SwiftSyntaxRule, ConfigurationProviderRule, OptInRule {
|
||||||
var configuration = SeverityConfiguration(.warning)
|
var configuration = SeverityConfiguration<Self>(.warning)
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "fatal_error_message",
|
identifier: "fatal_error_message",
|
||||||
name: "Fatal Error Message",
|
name: "Fatal Error Message",
|
||||||
description: "A fatalError call should have a message.",
|
description: "A fatalError call should have a message",
|
||||||
kind: .idiomatic,
|
kind: .idiomatic,
|
||||||
nonTriggeringExamples: [
|
nonTriggeringExamples: [
|
||||||
Example("""
|
Example("""
|
||||||
|
@ -45,7 +43,7 @@ private extension FatalErrorMessageRule {
|
||||||
final class Visitor: ViolationsSyntaxVisitor {
|
final class Visitor: ViolationsSyntaxVisitor {
|
||||||
override func visitPost(_ node: FunctionCallExprSyntax) {
|
override func visitPost(_ node: FunctionCallExprSyntax) {
|
||||||
guard let expression = node.calledExpression.as(IdentifierExprSyntax.self),
|
guard let expression = node.calledExpression.as(IdentifierExprSyntax.self),
|
||||||
expression.identifier.withoutTrivia().text == "fatalError",
|
expression.identifier.text == "fatalError",
|
||||||
node.argumentList.isEmptyOrEmptyString else {
|
node.argumentList.isEmptyOrEmptyString else {
|
||||||
return
|
return
|
||||||
}
|
}
|
|
@ -2,17 +2,12 @@ import Foundation
|
||||||
import SourceKittenFramework
|
import SourceKittenFramework
|
||||||
|
|
||||||
struct FileNameNoSpaceRule: ConfigurationProviderRule, OptInRule, SourceKitFreeRule {
|
struct FileNameNoSpaceRule: ConfigurationProviderRule, OptInRule, SourceKitFreeRule {
|
||||||
var configuration = FileNameNoSpaceConfiguration(
|
var configuration = FileNameNoSpaceConfiguration()
|
||||||
severity: .warning,
|
|
||||||
excluded: []
|
|
||||||
)
|
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "file_name_no_space",
|
identifier: "file_name_no_space",
|
||||||
name: "File Name No Space",
|
name: "File Name no Space",
|
||||||
description: "File name should not contain any whitespace.",
|
description: "File name should not contain any whitespace",
|
||||||
kind: .idiomatic
|
kind: .idiomatic
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -25,7 +20,7 @@ struct FileNameNoSpaceRule: ConfigurationProviderRule, OptInRule, SourceKitFreeR
|
||||||
}
|
}
|
||||||
|
|
||||||
return [StyleViolation(ruleDescription: Self.description,
|
return [StyleViolation(ruleDescription: Self.description,
|
||||||
severity: configuration.severity.severity,
|
severity: configuration.severity,
|
||||||
location: Location(file: filePath, line: 1))]
|
location: Location(file: filePath, line: 1))]
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,20 +1,12 @@
|
||||||
import SwiftSyntax
|
import SwiftSyntax
|
||||||
|
|
||||||
struct FileNameRule: ConfigurationProviderRule, OptInRule, SourceKitFreeRule {
|
struct FileNameRule: ConfigurationProviderRule, OptInRule, SourceKitFreeRule {
|
||||||
var configuration = FileNameConfiguration(
|
var configuration = FileNameConfiguration()
|
||||||
severity: .warning,
|
|
||||||
excluded: ["main.swift", "LinuxMain.swift"],
|
|
||||||
prefixPattern: "",
|
|
||||||
suffixPattern: "\\+.*",
|
|
||||||
nestedTypeSeparator: "."
|
|
||||||
)
|
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "file_name",
|
identifier: "file_name",
|
||||||
name: "File Name",
|
name: "File Name",
|
||||||
description: "File name should match a type or extension declared in the file (if any).",
|
description: "File name should match a type or extension declared in the file (if any)",
|
||||||
kind: .idiomatic
|
kind: .idiomatic
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -54,7 +46,7 @@ struct FileNameRule: ConfigurationProviderRule, OptInRule, SourceKitFreeRule {
|
||||||
}
|
}
|
||||||
|
|
||||||
return [StyleViolation(ruleDescription: Self.description,
|
return [StyleViolation(ruleDescription: Self.description,
|
||||||
severity: configuration.severity.severity,
|
severity: configuration.severity,
|
||||||
location: Location(file: filePath, line: 1))]
|
location: Location(file: filePath, line: 1))]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,6 +79,6 @@ private class TypeNameCollectingVisitor: SyntaxVisitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
override func visitPost(_ node: ExtensionDeclSyntax) {
|
override func visitPost(_ node: ExtensionDeclSyntax) {
|
||||||
names.insert(node.extendedType.withoutTrivia().description)
|
names.insert(node.extendedType.trimmedDescription)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,14 +1,12 @@
|
||||||
import SwiftSyntax
|
import SwiftSyntax
|
||||||
|
|
||||||
struct ForWhereRule: SwiftSyntaxRule, ConfigurationProviderRule {
|
struct ForWhereRule: SwiftSyntaxRule, ConfigurationProviderRule {
|
||||||
var configuration = ForWhereRuleConfiguration()
|
var configuration = ForWhereConfiguration()
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "for_where",
|
identifier: "for_where",
|
||||||
name: "For Where",
|
name: "Prefer For-Where",
|
||||||
description: "`where` clauses are preferred over a single `if` inside a `for`.",
|
description: "`where` clauses are preferred over a single `if` inside a `for`",
|
||||||
kind: .idiomatic,
|
kind: .idiomatic,
|
||||||
nonTriggeringExamples: [
|
nonTriggeringExamples: [
|
||||||
Example("""
|
Example("""
|
||||||
|
@ -134,26 +132,26 @@ private extension ForWhereRule {
|
||||||
|
|
||||||
override func visitPost(_ node: ForInStmtSyntax) {
|
override func visitPost(_ node: ForInStmtSyntax) {
|
||||||
guard node.whereClause == nil,
|
guard node.whereClause == nil,
|
||||||
case let statements = node.body.statements,
|
let onlyExprStmt = node.body.statements.onlyElement?.item.as(ExpressionStmtSyntax.self),
|
||||||
let ifStatement = statements.onlyElement?.item.as(IfStmtSyntax.self),
|
let ifExpr = onlyExprStmt.expression.as(IfExprSyntax.self),
|
||||||
ifStatement.elseBody == nil,
|
ifExpr.elseBody == nil,
|
||||||
!ifStatement.containsOptionalBinding,
|
!ifExpr.containsOptionalBinding,
|
||||||
!ifStatement.containsPatternCondition,
|
!ifExpr.containsPatternCondition,
|
||||||
let condition = ifStatement.conditions.onlyElement,
|
let condition = ifExpr.conditions.onlyElement,
|
||||||
!condition.containsMultipleConditions else {
|
!condition.containsMultipleConditions else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if allowForAsFilter, ifStatement.containsReturnStatement {
|
if allowForAsFilter, ifExpr.containsReturnStatement {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
violations.append(ifStatement.positionAfterSkippingLeadingTrivia)
|
violations.append(ifExpr.positionAfterSkippingLeadingTrivia)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension IfStmtSyntax {
|
private extension IfExprSyntax {
|
||||||
var containsOptionalBinding: Bool {
|
var containsOptionalBinding: Bool {
|
||||||
conditions.contains { element in
|
conditions.contains { element in
|
||||||
element.condition.is(OptionalBindingConditionSyntax.self)
|
element.condition.is(OptionalBindingConditionSyntax.self)
|
|
@ -1,14 +1,12 @@
|
||||||
import SwiftSyntax
|
import SwiftSyntax
|
||||||
|
|
||||||
struct ForceCastRule: ConfigurationProviderRule, SwiftSyntaxRule {
|
struct ForceCastRule: ConfigurationProviderRule, SwiftSyntaxRule {
|
||||||
var configuration = SeverityConfiguration(.error)
|
var configuration = SeverityConfiguration<Self>(.error)
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "force_cast",
|
identifier: "force_cast",
|
||||||
name: "Force Cast",
|
name: "Force Cast",
|
||||||
description: "Force casts should be avoided.",
|
description: "Force casts should be avoided",
|
||||||
kind: .idiomatic,
|
kind: .idiomatic,
|
||||||
nonTriggeringExamples: [
|
nonTriggeringExamples: [
|
||||||
Example("NSNumber() as? Int\n")
|
Example("NSNumber() as? Int\n")
|
|
@ -1,14 +1,12 @@
|
||||||
import SwiftSyntax
|
import SwiftSyntax
|
||||||
|
|
||||||
struct ForceTryRule: ConfigurationProviderRule, SwiftSyntaxRule {
|
struct ForceTryRule: ConfigurationProviderRule, SwiftSyntaxRule {
|
||||||
var configuration = SeverityConfiguration(.error)
|
var configuration = SeverityConfiguration<Self>(.error)
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "force_try",
|
identifier: "force_try",
|
||||||
name: "Force Try",
|
name: "Force Try",
|
||||||
description: "Force tries should be avoided.",
|
description: "Force tries should be avoided",
|
||||||
kind: .idiomatic,
|
kind: .idiomatic,
|
||||||
nonTriggeringExamples: [
|
nonTriggeringExamples: [
|
||||||
Example("""
|
Example("""
|
|
@ -1,14 +1,12 @@
|
||||||
import SwiftSyntax
|
import SwiftSyntax
|
||||||
|
|
||||||
struct ForceUnwrappingRule: OptInRule, SwiftSyntaxRule, ConfigurationProviderRule {
|
struct ForceUnwrappingRule: OptInRule, SwiftSyntaxRule, ConfigurationProviderRule {
|
||||||
var configuration = SeverityConfiguration(.warning)
|
var configuration = SeverityConfiguration<Self>(.warning)
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "force_unwrapping",
|
identifier: "force_unwrapping",
|
||||||
name: "Force Unwrapping",
|
name: "Force Unwrapping",
|
||||||
description: "Force unwrapping should be avoided.",
|
description: "Force unwrapping should be avoided",
|
||||||
kind: .idiomatic,
|
kind: .idiomatic,
|
||||||
nonTriggeringExamples: [
|
nonTriggeringExamples: [
|
||||||
Example("if let url = NSURL(string: query)"),
|
Example("if let url = NSURL(string: query)"),
|
|
@ -1,14 +1,12 @@
|
||||||
import SwiftSyntax
|
import SwiftSyntax
|
||||||
|
|
||||||
struct FunctionDefaultParameterAtEndRule: SwiftSyntaxRule, ConfigurationProviderRule, OptInRule {
|
struct FunctionDefaultParameterAtEndRule: SwiftSyntaxRule, ConfigurationProviderRule, OptInRule {
|
||||||
var configuration = SeverityConfiguration(.warning)
|
var configuration = SeverityConfiguration<Self>(.warning)
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "function_default_parameter_at_end",
|
identifier: "function_default_parameter_at_end",
|
||||||
name: "Function Default Parameter at End",
|
name: "Function Default Parameter at End",
|
||||||
description: "Prefer to locate parameters with defaults toward the end of the parameter list.",
|
description: "Prefer to locate parameters with defaults toward the end of the parameter list",
|
||||||
kind: .idiomatic,
|
kind: .idiomatic,
|
||||||
nonTriggeringExamples: [
|
nonTriggeringExamples: [
|
||||||
Example("func foo(baz: String, bar: Int = 0) {}"),
|
Example("func foo(baz: String, bar: Int = 0) {}"),
|
||||||
|
@ -104,20 +102,20 @@ private extension FunctionSignatureSyntax {
|
||||||
|
|
||||||
private extension FunctionParameterSyntax {
|
private extension FunctionParameterSyntax {
|
||||||
var isClosure: Bool {
|
var isClosure: Bool {
|
||||||
if isEscaping || type?.as(FunctionTypeSyntax.self) != nil {
|
if isEscaping || type.is(FunctionTypeSyntax.self) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if let optionalType = type?.as(OptionalTypeSyntax.self),
|
if let optionalType = type.as(OptionalTypeSyntax.self),
|
||||||
let tuple = optionalType.wrappedType.as(TupleTypeSyntax.self) {
|
let tuple = optionalType.wrappedType.as(TupleTypeSyntax.self) {
|
||||||
return tuple.elements.onlyElement?.type.as(FunctionTypeSyntax.self) != nil
|
return tuple.elements.onlyElement?.type.as(FunctionTypeSyntax.self) != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if let tuple = type?.as(TupleTypeSyntax.self) {
|
if let tuple = type.as(TupleTypeSyntax.self) {
|
||||||
return tuple.elements.onlyElement?.type.as(FunctionTypeSyntax.self) != nil
|
return tuple.elements.onlyElement?.type.as(FunctionTypeSyntax.self) != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if let attrType = type?.as(AttributedTypeSyntax.self) {
|
if let attrType = type.as(AttributedTypeSyntax.self) {
|
||||||
return attrType.baseType.is(FunctionTypeSyntax.self)
|
return attrType.baseType.is(FunctionTypeSyntax.self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,12 +123,10 @@ private extension FunctionParameterSyntax {
|
||||||
}
|
}
|
||||||
|
|
||||||
var isEscaping: Bool {
|
var isEscaping: Bool {
|
||||||
guard let attrType = type?.as(AttributedTypeSyntax.self) else {
|
guard let attrType = type.as(AttributedTypeSyntax.self) else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return attrType.attributes?.contains { attr in
|
return attrType.attributes.contains(attributeNamed: "escaping")
|
||||||
attr.as(AttributeSyntax.self)?.attributeName.tokenKind == .identifier("escaping")
|
|
||||||
} ?? false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,12 +2,10 @@ import Foundation
|
||||||
import SwiftSyntax
|
import SwiftSyntax
|
||||||
|
|
||||||
struct GenericTypeNameRule: SwiftSyntaxRule, ConfigurationProviderRule {
|
struct GenericTypeNameRule: SwiftSyntaxRule, ConfigurationProviderRule {
|
||||||
var configuration = NameConfiguration(minLengthWarning: 1,
|
var configuration = NameConfiguration<Self>(minLengthWarning: 1,
|
||||||
minLengthError: 0,
|
minLengthError: 0,
|
||||||
maxLengthWarning: 20,
|
maxLengthWarning: 20,
|
||||||
maxLengthError: 1000)
|
maxLengthError: 1000)
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "generic_type_name",
|
identifier: "generic_type_name",
|
||||||
|
@ -57,40 +55,39 @@ struct GenericTypeNameRule: SwiftSyntaxRule, ConfigurationProviderRule {
|
||||||
|
|
||||||
private extension GenericTypeNameRule {
|
private extension GenericTypeNameRule {
|
||||||
final class Visitor: ViolationsSyntaxVisitor {
|
final class Visitor: ViolationsSyntaxVisitor {
|
||||||
private let configuration: NameConfiguration
|
private let configuration: ConfigurationType
|
||||||
|
|
||||||
init(configuration: NameConfiguration) {
|
init(configuration: ConfigurationType) {
|
||||||
self.configuration = configuration
|
self.configuration = configuration
|
||||||
super.init(viewMode: .sourceAccurate)
|
super.init(viewMode: .sourceAccurate)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func visitPost(_ node: GenericParameterSyntax) {
|
override func visitPost(_ node: GenericParameterSyntax) {
|
||||||
let name = node.name.text
|
let name = node.name.text
|
||||||
guard !configuration.excluded.contains(name) else {
|
guard !name.isEmpty, !configuration.shouldExclude(name: name) else { return }
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let allowedSymbols = configuration.allowedSymbols.union(.alphanumerics)
|
if !configuration.allowedSymbolsAndAlphanumerics.isSuperset(of: CharacterSet(charactersIn: name)) {
|
||||||
if !allowedSymbols.isSuperset(of: CharacterSet(charactersIn: name)) {
|
|
||||||
violations.append(
|
violations.append(
|
||||||
ReasonedRuleViolation(
|
ReasonedRuleViolation(
|
||||||
position: node.positionAfterSkippingLeadingTrivia,
|
position: node.positionAfterSkippingLeadingTrivia,
|
||||||
reason: "Generic type name should only contain alphanumeric characters: '\(name)'",
|
reason: """
|
||||||
|
Generic type name '\(name)' should only contain alphanumeric and other allowed characters
|
||||||
|
""",
|
||||||
severity: .error
|
severity: .error
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
} else if configuration.validatesStartWithLowercase &&
|
} else if let caseCheckSeverity = configuration.validatesStartWithLowercase.severity,
|
||||||
!String(name[name.startIndex]).isUppercase() {
|
!String(name[name.startIndex]).isUppercase() {
|
||||||
violations.append(
|
violations.append(
|
||||||
ReasonedRuleViolation(
|
ReasonedRuleViolation(
|
||||||
position: node.positionAfterSkippingLeadingTrivia,
|
position: node.positionAfterSkippingLeadingTrivia,
|
||||||
reason: "Generic type name should start with an uppercase character: '\(name)'",
|
reason: "Generic type name '\(name)' should start with an uppercase character",
|
||||||
severity: .error
|
severity: caseCheckSeverity
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
} else if let severity = configuration.severity(forLength: name.count) {
|
} else if let severity = configuration.severity(forLength: name.count) {
|
||||||
let reason = "Generic type name should be between \(configuration.minLengthThreshold) and " +
|
let reason = "Generic type name '\(name)' should be between \(configuration.minLengthThreshold) and " +
|
||||||
"\(configuration.maxLengthThreshold) characters long: '\(name)'"
|
"\(configuration.maxLengthThreshold) characters long"
|
||||||
violations.append(
|
violations.append(
|
||||||
ReasonedRuleViolation(
|
ReasonedRuleViolation(
|
||||||
position: node.positionAfterSkippingLeadingTrivia,
|
position: node.positionAfterSkippingLeadingTrivia,
|
|
@ -1,17 +1,12 @@
|
||||||
import SwiftSyntax
|
import SwiftSyntax
|
||||||
|
|
||||||
struct ImplicitlyUnwrappedOptionalRule: SwiftSyntaxRule, ConfigurationProviderRule, OptInRule {
|
struct ImplicitlyUnwrappedOptionalRule: SwiftSyntaxRule, ConfigurationProviderRule, OptInRule {
|
||||||
var configuration = ImplicitlyUnwrappedOptionalConfiguration(
|
var configuration = ImplicitlyUnwrappedOptionalConfiguration()
|
||||||
mode: .allExceptIBOutlets,
|
|
||||||
severityConfiguration: SeverityConfiguration(.warning)
|
|
||||||
)
|
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "implicitly_unwrapped_optional",
|
identifier: "implicitly_unwrapped_optional",
|
||||||
name: "Implicitly Unwrapped Optional",
|
name: "Implicitly Unwrapped Optional",
|
||||||
description: "Implicitly unwrapped optionals should be avoided when possible.",
|
description: "Implicitly unwrapped optionals should be avoided when possible",
|
||||||
kind: .idiomatic,
|
kind: .idiomatic,
|
||||||
nonTriggeringExamples: [
|
nonTriggeringExamples: [
|
||||||
Example("@IBOutlet private var label: UILabel!"),
|
Example("@IBOutlet private var label: UILabel!"),
|
||||||
|
@ -19,19 +14,30 @@ struct ImplicitlyUnwrappedOptionalRule: SwiftSyntaxRule, ConfigurationProviderRu
|
||||||
Example("@IBOutlet var label: [UILabel!]"),
|
Example("@IBOutlet var label: [UILabel!]"),
|
||||||
Example("if !boolean {}"),
|
Example("if !boolean {}"),
|
||||||
Example("let int: Int? = 42"),
|
Example("let int: Int? = 42"),
|
||||||
Example("let int: Int? = nil")
|
Example("let int: Int? = nil"),
|
||||||
|
Example("""
|
||||||
|
class MyClass {
|
||||||
|
@IBOutlet
|
||||||
|
weak var bar: SomeObject!
|
||||||
|
}
|
||||||
|
""", configuration: ["mode": "all_except_iboutlets"], excludeFromDocumentation: true)
|
||||||
],
|
],
|
||||||
triggeringExamples: [
|
triggeringExamples: [
|
||||||
Example("let label: UILabel!"),
|
Example("let label: ↓UILabel!"),
|
||||||
Example("let IBOutlet: UILabel!"),
|
Example("let IBOutlet: ↓UILabel!"),
|
||||||
Example("let labels: [UILabel!]"),
|
Example("let labels: [↓UILabel!]"),
|
||||||
Example("var ints: [Int!] = [42, nil, 42]"),
|
Example("var ints: [↓Int!] = [42, nil, 42]"),
|
||||||
Example("let label: IBOutlet!"),
|
Example("let label: ↓IBOutlet!"),
|
||||||
Example("let int: Int! = 42"),
|
Example("let int: ↓Int! = 42"),
|
||||||
Example("let int: Int! = nil"),
|
Example("let int: ↓Int! = nil"),
|
||||||
Example("var int: Int! = 42"),
|
Example("var int: ↓Int! = 42"),
|
||||||
Example("let collection: AnyCollection<Int!>"),
|
Example("let collection: AnyCollection<↓Int!>"),
|
||||||
Example("func foo(int: Int!) {}")
|
Example("func foo(int: ↓Int!) {}"),
|
||||||
|
Example("""
|
||||||
|
class MyClass {
|
||||||
|
weak var bar: ↓SomeObject!
|
||||||
|
}
|
||||||
|
""")
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -42,9 +48,9 @@ struct ImplicitlyUnwrappedOptionalRule: SwiftSyntaxRule, ConfigurationProviderRu
|
||||||
|
|
||||||
private extension ImplicitlyUnwrappedOptionalRule {
|
private extension ImplicitlyUnwrappedOptionalRule {
|
||||||
final class Visitor: ViolationsSyntaxVisitor {
|
final class Visitor: ViolationsSyntaxVisitor {
|
||||||
private let mode: ImplicitlyUnwrappedOptionalModeConfiguration
|
private let mode: ConfigurationType.ImplicitlyUnwrappedOptionalModeConfiguration
|
||||||
|
|
||||||
init(mode: ImplicitlyUnwrappedOptionalModeConfiguration) {
|
init(mode: ConfigurationType.ImplicitlyUnwrappedOptionalModeConfiguration) {
|
||||||
self.mode = mode
|
self.mode = mode
|
||||||
super.init(viewMode: .sourceAccurate)
|
super.init(viewMode: .sourceAccurate)
|
||||||
}
|
}
|
|
@ -1,14 +1,12 @@
|
||||||
import SwiftSyntax
|
import SwiftSyntax
|
||||||
|
|
||||||
struct IsDisjointRule: SwiftSyntaxRule, ConfigurationProviderRule {
|
struct IsDisjointRule: SwiftSyntaxRule, ConfigurationProviderRule {
|
||||||
var configuration = SeverityConfiguration(.warning)
|
var configuration = SeverityConfiguration<Self>(.warning)
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "is_disjoint",
|
identifier: "is_disjoint",
|
||||||
name: "Is Disjoint",
|
name: "Is Disjoint",
|
||||||
description: "Prefer using `Set.isDisjoint(with:)` over `Set.intersection(_:).isEmpty`.",
|
description: "Prefer using `Set.isDisjoint(with:)` over `Set.intersection(_:).isEmpty`",
|
||||||
kind: .idiomatic,
|
kind: .idiomatic,
|
||||||
nonTriggeringExamples: [
|
nonTriggeringExamples: [
|
||||||
Example("_ = Set(syntaxKinds).isDisjoint(with: commentAndStringKindsSet)"),
|
Example("_ = Set(syntaxKinds).isDisjoint(with: commentAndStringKindsSet)"),
|
|
@ -1,14 +1,12 @@
|
||||||
import SwiftSyntax
|
import SwiftSyntax
|
||||||
|
|
||||||
struct JoinedDefaultParameterRule: SwiftSyntaxCorrectableRule, ConfigurationProviderRule, OptInRule {
|
struct JoinedDefaultParameterRule: SwiftSyntaxCorrectableRule, ConfigurationProviderRule, OptInRule {
|
||||||
var configuration = SeverityConfiguration(.warning)
|
var configuration = SeverityConfiguration<Self>(.warning)
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "joined_default_parameter",
|
identifier: "joined_default_parameter",
|
||||||
name: "Joined Default Parameter",
|
name: "Joined Default Parameter",
|
||||||
description: "Discouraged explicit usage of the default separator.",
|
description: "Discouraged explicit usage of the default separator",
|
||||||
kind: .idiomatic,
|
kind: .idiomatic,
|
||||||
nonTriggeringExamples: [
|
nonTriggeringExamples: [
|
||||||
Example("let foo = bar.joined()"),
|
Example("let foo = bar.joined()"),
|
||||||
|
@ -76,7 +74,7 @@ private extension JoinedDefaultParameterRule {
|
||||||
}
|
}
|
||||||
|
|
||||||
correctionPositions.append(violationPosition)
|
correctionPositions.append(violationPosition)
|
||||||
let newNode = node.withArgumentList(nil)
|
let newNode = node.with(\.argumentList, [])
|
||||||
return super.visit(newNode)
|
return super.visit(newNode)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,7 +1,5 @@
|
||||||
struct LegacyCGGeometryFunctionsRule: SwiftSyntaxCorrectableRule, ConfigurationProviderRule {
|
struct LegacyCGGeometryFunctionsRule: SwiftSyntaxCorrectableRule, ConfigurationProviderRule {
|
||||||
var configuration = SeverityConfiguration(.warning)
|
var configuration = SeverityConfiguration<Self>(.warning)
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "legacy_cggeometry_functions",
|
identifier: "legacy_cggeometry_functions",
|
||||||
|
@ -70,7 +68,7 @@ struct LegacyCGGeometryFunctionsRule: SwiftSyntaxCorrectableRule, ConfigurationP
|
||||||
Example("↓CGRectInset(rect, 5.0, -7.0)\n"): Example("rect.insetBy(dx: 5.0, dy: -7.0)\n"),
|
Example("↓CGRectInset(rect, 5.0, -7.0)\n"): Example("rect.insetBy(dx: 5.0, dy: -7.0)\n"),
|
||||||
Example("↓CGRectOffset(rect, -2, 8.3)\n"): Example("rect.offsetBy(dx: -2, dy: 8.3)\n"),
|
Example("↓CGRectOffset(rect, -2, 8.3)\n"): Example("rect.offsetBy(dx: -2, dy: 8.3)\n"),
|
||||||
Example("↓CGRectUnion(rect1, rect2)\n"): Example("rect1.union(rect2)\n"),
|
Example("↓CGRectUnion(rect1, rect2)\n"): Example("rect1.union(rect2)\n"),
|
||||||
Example("↓CGRectIntersection( rect1 ,rect2)\n"): Example("rect1.intersect(rect2)\n"),
|
Example("↓CGRectIntersection( rect1 ,rect2)\n"): Example("rect1.intersection(rect2)\n"),
|
||||||
Example("↓CGRectContainsRect( rect1,rect2 )\n"): Example("rect1.contains(rect2)\n"),
|
Example("↓CGRectContainsRect( rect1,rect2 )\n"): Example("rect1.contains(rect2)\n"),
|
||||||
Example("↓CGRectContainsPoint(rect ,point)\n"): Example("rect.contains(point)\n"),
|
Example("↓CGRectContainsPoint(rect ,point)\n"): Example("rect.contains(point)\n"),
|
||||||
Example("↓CGRectIntersectsRect( rect1,rect2 )\n"): Example("rect1.intersects(rect2)\n"),
|
Example("↓CGRectIntersectsRect( rect1,rect2 )\n"): Example("rect1.intersects(rect2)\n"),
|
||||||
|
@ -99,7 +97,7 @@ struct LegacyCGGeometryFunctionsRule: SwiftSyntaxCorrectableRule, ConfigurationP
|
||||||
"CGRectContainsRect": .function(name: "contains", argumentLabels: [""]),
|
"CGRectContainsRect": .function(name: "contains", argumentLabels: [""]),
|
||||||
"CGRectContainsPoint": .function(name: "contains", argumentLabels: [""]),
|
"CGRectContainsPoint": .function(name: "contains", argumentLabels: [""]),
|
||||||
"CGRectIntersectsRect": .function(name: "intersects", argumentLabels: [""]),
|
"CGRectIntersectsRect": .function(name: "intersects", argumentLabels: [""]),
|
||||||
"CGRectIntersection": .function(name: "intersect", argumentLabels: [""])
|
"CGRectIntersection": .function(name: "intersection", argumentLabels: [""])
|
||||||
]
|
]
|
||||||
|
|
||||||
func makeVisitor(file: SwiftLintFile) -> ViolationsSyntaxVisitor {
|
func makeVisitor(file: SwiftLintFile) -> ViolationsSyntaxVisitor {
|
|
@ -2,14 +2,12 @@ import SwiftSyntax
|
||||||
import SwiftSyntaxBuilder
|
import SwiftSyntaxBuilder
|
||||||
|
|
||||||
struct LegacyConstantRule: SwiftSyntaxCorrectableRule, ConfigurationProviderRule {
|
struct LegacyConstantRule: SwiftSyntaxCorrectableRule, ConfigurationProviderRule {
|
||||||
var configuration = SeverityConfiguration(.warning)
|
var configuration = SeverityConfiguration<Self>(.warning)
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "legacy_constant",
|
identifier: "legacy_constant",
|
||||||
name: "Legacy Constant",
|
name: "Legacy Constant",
|
||||||
description: "Struct-scoped constants are preferred over legacy global constants.",
|
description: "Struct-scoped constants are preferred over legacy global constants",
|
||||||
kind: .idiomatic,
|
kind: .idiomatic,
|
||||||
nonTriggeringExamples: LegacyConstantRuleExamples.nonTriggeringExamples,
|
nonTriggeringExamples: LegacyConstantRuleExamples.nonTriggeringExamples,
|
||||||
triggeringExamples: LegacyConstantRuleExamples.triggeringExamples,
|
triggeringExamples: LegacyConstantRuleExamples.triggeringExamples,
|
||||||
|
@ -62,9 +60,9 @@ private extension LegacyConstantRule {
|
||||||
}
|
}
|
||||||
|
|
||||||
correctionPositions.append(node.positionAfterSkippingLeadingTrivia)
|
correctionPositions.append(node.positionAfterSkippingLeadingTrivia)
|
||||||
return ("\(correction)" as ExprSyntax)
|
return ("\(raw: correction)" as ExprSyntax)
|
||||||
.withLeadingTrivia(node.leadingTrivia ?? .zero)
|
.with(\.leadingTrivia, node.leadingTrivia)
|
||||||
.withTrailingTrivia(node.trailingTrivia ?? .zero)
|
.with(\.trailingTrivia, node.trailingTrivia)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func visit(_ node: FunctionCallExprSyntax) -> ExprSyntax {
|
override func visit(_ node: FunctionCallExprSyntax) -> ExprSyntax {
|
||||||
|
@ -77,9 +75,9 @@ private extension LegacyConstantRule {
|
||||||
}
|
}
|
||||||
|
|
||||||
correctionPositions.append(node.positionAfterSkippingLeadingTrivia)
|
correctionPositions.append(node.positionAfterSkippingLeadingTrivia)
|
||||||
return ("\(calledExpression.identifier.text).pi" as ExprSyntax)
|
return ("\(raw: calledExpression.identifier.text).pi" as ExprSyntax)
|
||||||
.withLeadingTrivia(node.leadingTrivia ?? .zero)
|
.with(\.leadingTrivia, node.leadingTrivia)
|
||||||
.withTrailingTrivia(node.trailingTrivia ?? .zero)
|
.with(\.trailingTrivia, node.trailingTrivia)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,14 +1,12 @@
|
||||||
import SwiftSyntax
|
import SwiftSyntax
|
||||||
|
|
||||||
struct LegacyConstructorRule: SwiftSyntaxCorrectableRule, ConfigurationProviderRule {
|
struct LegacyConstructorRule: SwiftSyntaxCorrectableRule, ConfigurationProviderRule {
|
||||||
var configuration = SeverityConfiguration(.warning)
|
var configuration = SeverityConfiguration<Self>(.warning)
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "legacy_constructor",
|
identifier: "legacy_constructor",
|
||||||
name: "Legacy Constructor",
|
name: "Legacy Constructor",
|
||||||
description: "Swift constructors are preferred over legacy convenience functions.",
|
description: "Swift constructors are preferred over legacy convenience functions",
|
||||||
kind: .idiomatic,
|
kind: .idiomatic,
|
||||||
nonTriggeringExamples: [
|
nonTriggeringExamples: [
|
||||||
Example("CGPoint(x: 10, y: 10)"),
|
Example("CGPoint(x: 10, y: 10)"),
|
||||||
|
@ -140,7 +138,7 @@ private extension LegacyConstructorRule {
|
||||||
final class Visitor: ViolationsSyntaxVisitor {
|
final class Visitor: ViolationsSyntaxVisitor {
|
||||||
override func visitPost(_ node: FunctionCallExprSyntax) {
|
override func visitPost(_ node: FunctionCallExprSyntax) {
|
||||||
if let identifierExpr = node.calledExpression.as(IdentifierExprSyntax.self),
|
if let identifierExpr = node.calledExpression.as(IdentifierExprSyntax.self),
|
||||||
constructorsToCorrectedNames[identifierExpr.identifier.withoutTrivia().text] != nil {
|
constructorsToCorrectedNames[identifierExpr.identifier.text] != nil {
|
||||||
violations.append(node.positionAfterSkippingLeadingTrivia)
|
violations.append(node.positionAfterSkippingLeadingTrivia)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -158,7 +156,7 @@ private extension LegacyConstructorRule {
|
||||||
|
|
||||||
override func visit(_ node: FunctionCallExprSyntax) -> ExprSyntax {
|
override func visit(_ node: FunctionCallExprSyntax) -> ExprSyntax {
|
||||||
guard let identifierExpr = node.calledExpression.as(IdentifierExprSyntax.self),
|
guard let identifierExpr = node.calledExpression.as(IdentifierExprSyntax.self),
|
||||||
case let identifier = identifierExpr.identifier.withoutTrivia().text,
|
case let identifier = identifierExpr.identifier.text,
|
||||||
let correctedName = constructorsToCorrectedNames[identifier],
|
let correctedName = constructorsToCorrectedNames[identifier],
|
||||||
let args = constructorsToArguments[identifier],
|
let args = constructorsToArguments[identifier],
|
||||||
!node.isContainedIn(regions: disabledRegions, locationConverter: locationConverter) else {
|
!node.isContainedIn(regions: disabledRegions, locationConverter: locationConverter) else {
|
||||||
|
@ -167,12 +165,13 @@ private extension LegacyConstructorRule {
|
||||||
|
|
||||||
correctionPositions.append(node.positionAfterSkippingLeadingTrivia)
|
correctionPositions.append(node.positionAfterSkippingLeadingTrivia)
|
||||||
|
|
||||||
let arguments = TupleExprElementListSyntax(node.argumentList.map { elem in
|
let arguments = TupleExprElementListSyntax(node.argumentList.enumerated().map { index, elem in
|
||||||
elem
|
elem
|
||||||
.withLabel(.identifier(args[elem.indexInParent]))
|
.with(\.label, .identifier(args[index]))
|
||||||
.withColon(.colonToken(trailingTrivia: .space))
|
.with(\.colon, .colonToken(trailingTrivia: .space))
|
||||||
})
|
})
|
||||||
let newExpression = identifierExpr.withIdentifier(
|
let newExpression = identifierExpr.with(
|
||||||
|
\.identifier,
|
||||||
.identifier(
|
.identifier(
|
||||||
correctedName,
|
correctedName,
|
||||||
leadingTrivia: identifierExpr.identifier.leadingTrivia,
|
leadingTrivia: identifierExpr.identifier.leadingTrivia,
|
||||||
|
@ -180,8 +179,8 @@ private extension LegacyConstructorRule {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
let newNode = node
|
let newNode = node
|
||||||
.withCalledExpression(ExprSyntax(newExpression))
|
.with(\.calledExpression, ExprSyntax(newExpression))
|
||||||
.withArgumentList(arguments)
|
.with(\.argumentList, arguments)
|
||||||
return super.visit(newNode)
|
return super.visit(newNode)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,9 +1,7 @@
|
||||||
import SwiftSyntax
|
import SwiftSyntax
|
||||||
|
|
||||||
struct LegacyHashingRule: SwiftSyntaxRule, ConfigurationProviderRule {
|
struct LegacyHashingRule: SwiftSyntaxRule, ConfigurationProviderRule {
|
||||||
var configuration = SeverityConfiguration(.warning)
|
var configuration = SeverityConfiguration<Self>(.warning)
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "legacy_hashing",
|
identifier: "legacy_hashing",
|
||||||
|
@ -85,7 +83,7 @@ extension LegacyHashingRule {
|
||||||
override func visitPost(_ node: VariableDeclSyntax) {
|
override func visitPost(_ node: VariableDeclSyntax) {
|
||||||
guard
|
guard
|
||||||
node.parent?.is(MemberDeclListItemSyntax.self) == true,
|
node.parent?.is(MemberDeclListItemSyntax.self) == true,
|
||||||
node.letOrVarKeyword.tokenKind == .varKeyword,
|
node.bindingKeyword.tokenKind == .keyword(.var),
|
||||||
let binding = node.bindings.onlyElement,
|
let binding = node.bindings.onlyElement,
|
||||||
let identifier = binding.pattern.as(IdentifierPatternSyntax.self),
|
let identifier = binding.pattern.as(IdentifierPatternSyntax.self),
|
||||||
identifier.identifier.text == "hashValue",
|
identifier.identifier.text == "hashValue",
|
||||||
|
@ -95,7 +93,7 @@ extension LegacyHashingRule {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
violations.append(node.letOrVarKeyword.positionAfterSkippingLeadingTrivia)
|
violations.append(node.bindingKeyword.positionAfterSkippingLeadingTrivia)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,14 +1,12 @@
|
||||||
import SwiftSyntax
|
import SwiftSyntax
|
||||||
|
|
||||||
struct LegacyMultipleRule: OptInRule, ConfigurationProviderRule, SwiftSyntaxRule {
|
struct LegacyMultipleRule: OptInRule, ConfigurationProviderRule, SwiftSyntaxRule {
|
||||||
var configuration = SeverityConfiguration(.warning)
|
var configuration = SeverityConfiguration<Self>(.warning)
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "legacy_multiple",
|
identifier: "legacy_multiple",
|
||||||
name: "Legacy Multiple",
|
name: "Legacy Multiple",
|
||||||
description: "Prefer using the `isMultiple(of:)` function instead of using the remainder operator (`%`).",
|
description: "Prefer using the `isMultiple(of:)` function instead of using the remainder operator (`%`)",
|
||||||
kind: .idiomatic,
|
kind: .idiomatic,
|
||||||
nonTriggeringExamples: [
|
nonTriggeringExamples: [
|
||||||
Example("cell.contentView.backgroundColor = indexPath.row.isMultiple(of: 2) ? .gray : .white"),
|
Example("cell.contentView.backgroundColor = indexPath.row.isMultiple(of: 2) ? .gray : .white"),
|
||||||
|
@ -39,8 +37,8 @@ struct LegacyMultipleRule: OptInRule, ConfigurationProviderRule, SwiftSyntaxRule
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
func preprocess(syntaxTree: SourceFileSyntax) -> SourceFileSyntax? {
|
func preprocess(file: SwiftLintFile) -> SourceFileSyntax? {
|
||||||
syntaxTree.folded()
|
file.foldedSyntaxTree
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeVisitor(file: SwiftLintFile) -> ViolationsSyntaxVisitor {
|
func makeVisitor(file: SwiftLintFile) -> ViolationsSyntaxVisitor {
|
||||||
|
@ -52,7 +50,7 @@ private extension LegacyMultipleRule {
|
||||||
final class Visitor: ViolationsSyntaxVisitor {
|
final class Visitor: ViolationsSyntaxVisitor {
|
||||||
override func visitPost(_ node: InfixOperatorExprSyntax) {
|
override func visitPost(_ node: InfixOperatorExprSyntax) {
|
||||||
guard let operatorNode = node.operatorOperand.as(BinaryOperatorExprSyntax.self),
|
guard let operatorNode = node.operatorOperand.as(BinaryOperatorExprSyntax.self),
|
||||||
operatorNode.operatorToken.tokenKind == .spacedBinaryOperator("%"),
|
operatorNode.operatorToken.tokenKind == .binaryOperator("%"),
|
||||||
let parent = node.parent?.as(InfixOperatorExprSyntax.self),
|
let parent = node.parent?.as(InfixOperatorExprSyntax.self),
|
||||||
let parentOperatorNode = parent.operatorOperand.as(BinaryOperatorExprSyntax.self),
|
let parentOperatorNode = parent.operatorOperand.as(BinaryOperatorExprSyntax.self),
|
||||||
parentOperatorNode.isEqualityOrInequalityOperator else {
|
parentOperatorNode.isEqualityOrInequalityOperator else {
|
||||||
|
@ -80,8 +78,7 @@ private extension LegacyMultipleRule {
|
||||||
|
|
||||||
private extension BinaryOperatorExprSyntax {
|
private extension BinaryOperatorExprSyntax {
|
||||||
var isEqualityOrInequalityOperator: Bool {
|
var isEqualityOrInequalityOperator: Bool {
|
||||||
operatorToken.tokenKind == .spacedBinaryOperator("==") ||
|
operatorToken.tokenKind == .binaryOperator("==") ||
|
||||||
operatorToken.tokenKind == .unspacedBinaryOperator("==") ||
|
operatorToken.tokenKind == .binaryOperator("!=")
|
||||||
operatorToken.tokenKind == .spacedBinaryOperator("!=")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,7 +1,5 @@
|
||||||
struct LegacyNSGeometryFunctionsRule: SwiftSyntaxCorrectableRule, ConfigurationProviderRule {
|
struct LegacyNSGeometryFunctionsRule: SwiftSyntaxCorrectableRule, ConfigurationProviderRule {
|
||||||
var configuration = SeverityConfiguration(.warning)
|
var configuration = SeverityConfiguration<Self>(.warning)
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "legacy_nsgeometry_functions",
|
identifier: "legacy_nsgeometry_functions",
|
|
@ -29,9 +29,7 @@ private let legacyObjcTypes = [
|
||||||
]
|
]
|
||||||
|
|
||||||
struct LegacyObjcTypeRule: SwiftSyntaxRule, OptInRule, ConfigurationProviderRule {
|
struct LegacyObjcTypeRule: SwiftSyntaxRule, OptInRule, ConfigurationProviderRule {
|
||||||
var configuration = SeverityConfiguration(.warning)
|
var configuration = SeverityConfiguration<Self>(.warning)
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "legacy_objc_type",
|
identifier: "legacy_objc_type",
|
|
@ -1,14 +1,12 @@
|
||||||
import SwiftSyntax
|
import SwiftSyntax
|
||||||
|
|
||||||
struct LegacyRandomRule: SwiftSyntaxRule, ConfigurationProviderRule {
|
struct LegacyRandomRule: SwiftSyntaxRule, ConfigurationProviderRule {
|
||||||
var configuration = SeverityConfiguration(.warning)
|
var configuration = SeverityConfiguration<Self>(.warning)
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static var description = RuleDescription(
|
static var description = RuleDescription(
|
||||||
identifier: "legacy_random",
|
identifier: "legacy_random",
|
||||||
name: "Legacy Random",
|
name: "Legacy Random",
|
||||||
description: "Prefer using `type.random(in:)` over legacy functions.",
|
description: "Prefer using `type.random(in:)` over legacy functions",
|
||||||
kind: .idiomatic,
|
kind: .idiomatic,
|
||||||
nonTriggeringExamples: [
|
nonTriggeringExamples: [
|
||||||
Example("Int.random(in: 0..<10)\n"),
|
Example("Int.random(in: 0..<10)\n"),
|
||||||
|
@ -16,9 +14,9 @@ struct LegacyRandomRule: SwiftSyntaxRule, ConfigurationProviderRule {
|
||||||
Example("Float.random(in: 0 ..< 1)\n")
|
Example("Float.random(in: 0 ..< 1)\n")
|
||||||
],
|
],
|
||||||
triggeringExamples: [
|
triggeringExamples: [
|
||||||
Example("↓arc4random(10)\n"),
|
Example("↓arc4random()\n"),
|
||||||
Example("↓arc4random_uniform(83)\n"),
|
Example("↓arc4random_uniform(83)\n"),
|
||||||
Example("↓drand48(52)\n")
|
Example("↓drand48()\n")
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -36,7 +34,7 @@ private extension LegacyRandomRule {
|
||||||
]
|
]
|
||||||
|
|
||||||
override func visitPost(_ node: FunctionCallExprSyntax) {
|
override func visitPost(_ node: FunctionCallExprSyntax) {
|
||||||
if let function = node.calledExpression.as(IdentifierExprSyntax.self)?.identifier.withoutTrivia().text,
|
if let function = node.calledExpression.as(IdentifierExprSyntax.self)?.identifier.text,
|
||||||
Self.legacyRandomFunctions.contains(function) {
|
Self.legacyRandomFunctions.contains(function) {
|
||||||
violations.append(node.positionAfterSkippingLeadingTrivia)
|
violations.append(node.positionAfterSkippingLeadingTrivia)
|
||||||
}
|
}
|
|
@ -2,14 +2,12 @@ import Foundation
|
||||||
import SourceKittenFramework
|
import SourceKittenFramework
|
||||||
|
|
||||||
struct NimbleOperatorRule: ConfigurationProviderRule, OptInRule, CorrectableRule {
|
struct NimbleOperatorRule: ConfigurationProviderRule, OptInRule, CorrectableRule {
|
||||||
var configuration = SeverityConfiguration(.warning)
|
var configuration = SeverityConfiguration<Self>(.warning)
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "nimble_operator",
|
identifier: "nimble_operator",
|
||||||
name: "Nimble Operator",
|
name: "Nimble Operator",
|
||||||
description: "Prefer Nimble operator overloads over free matcher functions.",
|
description: "Prefer Nimble operator overloads over free matcher functions",
|
||||||
kind: .idiomatic,
|
kind: .idiomatic,
|
||||||
nonTriggeringExamples: [
|
nonTriggeringExamples: [
|
||||||
Example("expect(seagull.squawk) != \"Hi!\"\n"),
|
Example("expect(seagull.squawk) != \"Hi!\"\n"),
|
||||||
|
@ -114,12 +112,10 @@ struct NimbleOperatorRule: ConfigurationProviderRule, OptInRule, CorrectableRule
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
let containsCall = file.structureDictionary.structures(forByteOffset: byteRange.upperBound - 1)
|
return file.structureDictionary.structures(forByteOffset: byteRange.upperBound - 1)
|
||||||
.contains(where: { dict -> Bool in
|
.contains(where: { dict -> Bool in
|
||||||
return dict.expressionKind == .call && (dict.name ?? "").starts(with: "expect")
|
return dict.expressionKind == .call && (dict.name ?? "").starts(with: "expect")
|
||||||
})
|
})
|
||||||
|
|
||||||
return containsCall
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
import SwiftSyntax
|
import SwiftSyntax
|
||||||
|
|
||||||
struct NoExtensionAccessModifierRule: SwiftSyntaxRule, OptInRule, ConfigurationProviderRule {
|
struct NoExtensionAccessModifierRule: SwiftSyntaxRule, OptInRule, ConfigurationProviderRule {
|
||||||
var configuration = SeverityConfiguration(.error)
|
var configuration = SeverityConfiguration<Self>(.error)
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "no_extension_access_modifier",
|
identifier: "no_extension_access_modifier",
|
|
@ -1,14 +1,12 @@
|
||||||
import SwiftSyntax
|
import SwiftSyntax
|
||||||
|
|
||||||
struct NoFallthroughOnlyRule: SwiftSyntaxRule, ConfigurationProviderRule {
|
struct NoFallthroughOnlyRule: SwiftSyntaxRule, ConfigurationProviderRule {
|
||||||
var configuration = SeverityConfiguration(.warning)
|
var configuration = SeverityConfiguration<Self>(.warning)
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "no_fallthrough_only",
|
identifier: "no_fallthrough_only",
|
||||||
name: "No Fallthrough Only",
|
name: "No Fallthrough only",
|
||||||
description: "Fallthroughs can only be used if the `case` contains at least one other statement.",
|
description: "Fallthroughs can only be used if the `case` contains at least one other statement",
|
||||||
kind: .idiomatic,
|
kind: .idiomatic,
|
||||||
nonTriggeringExamples: NoFallthroughOnlyRuleExamples.nonTriggeringExamples,
|
nonTriggeringExamples: NoFallthroughOnlyRuleExamples.nonTriggeringExamples,
|
||||||
triggeringExamples: NoFallthroughOnlyRuleExamples.triggeringExamples
|
triggeringExamples: NoFallthroughOnlyRuleExamples.triggeringExamples
|
|
@ -1,14 +1,12 @@
|
||||||
import SourceKittenFramework
|
import SourceKittenFramework
|
||||||
|
|
||||||
struct NoGroupingExtensionRule: OptInRule, ConfigurationProviderRule {
|
struct NoGroupingExtensionRule: OptInRule, ConfigurationProviderRule {
|
||||||
var configuration = SeverityConfiguration(.warning)
|
var configuration = SeverityConfiguration<Self>(.warning)
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "no_grouping_extension",
|
identifier: "no_grouping_extension",
|
||||||
name: "No Grouping Extension",
|
name: "No Grouping Extension",
|
||||||
description: "Extensions shouldn't be used to group code within the same source file.",
|
description: "Extensions shouldn't be used to group code within the same source file",
|
||||||
kind: .idiomatic,
|
kind: .idiomatic,
|
||||||
nonTriggeringExamples: [
|
nonTriggeringExamples: [
|
||||||
Example("protocol Food {}\nextension Food {}\n"),
|
Example("protocol Food {}\nextension Food {}\n"),
|
|
@ -0,0 +1,129 @@
|
||||||
|
import SwiftSyntax
|
||||||
|
|
||||||
|
struct NoMagicNumbersRule: SwiftSyntaxRule, OptInRule, ConfigurationProviderRule {
|
||||||
|
var configuration = NoMagicNumbersConfiguration()
|
||||||
|
|
||||||
|
static let description = RuleDescription(
|
||||||
|
identifier: "no_magic_numbers",
|
||||||
|
name: "No Magic Numbers",
|
||||||
|
description: "Magic numbers should be replaced by named constants",
|
||||||
|
kind: .idiomatic,
|
||||||
|
nonTriggeringExamples: [
|
||||||
|
Example("var foo = 123"),
|
||||||
|
Example("static let bar: Double = 0.123"),
|
||||||
|
Example("let a = b + 1.0"),
|
||||||
|
Example("array[0] + array[1] "),
|
||||||
|
Example("let foo = 1_000.000_01"),
|
||||||
|
Example("// array[1337]"),
|
||||||
|
Example("baz(\"9999\")"),
|
||||||
|
Example("""
|
||||||
|
func foo() {
|
||||||
|
let x: Int = 2
|
||||||
|
let y = 3
|
||||||
|
let vector = [x, y, -1]
|
||||||
|
}
|
||||||
|
"""),
|
||||||
|
Example("""
|
||||||
|
class A {
|
||||||
|
var foo: Double = 132
|
||||||
|
static let bar: Double = 0.98
|
||||||
|
}
|
||||||
|
"""),
|
||||||
|
Example("""
|
||||||
|
@available(iOS 13, *)
|
||||||
|
func version() {
|
||||||
|
if #available(iOS 13, OSX 10.10, *) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""),
|
||||||
|
Example("""
|
||||||
|
enum Example: Int {
|
||||||
|
case positive = 2
|
||||||
|
case negative = -2
|
||||||
|
}
|
||||||
|
"""),
|
||||||
|
Example("""
|
||||||
|
class FooTests: XCTestCase {
|
||||||
|
let array: [Int] = []
|
||||||
|
let bar = array[42]
|
||||||
|
}
|
||||||
|
"""),
|
||||||
|
Example("""
|
||||||
|
class FooTests: XCTestCase {
|
||||||
|
class Bar {
|
||||||
|
let array: [Int] = []
|
||||||
|
let bar = array[42]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
],
|
||||||
|
triggeringExamples: [
|
||||||
|
Example("foo(↓321)"),
|
||||||
|
Example("bar(↓1_000.005_01)"),
|
||||||
|
Example("array[↓42]"),
|
||||||
|
Example("let box = array[↓12 + ↓14]"),
|
||||||
|
Example("let a = b + ↓2.0"),
|
||||||
|
Example("Color.primary.opacity(isAnimate ? ↓0.1 : ↓1.5)")
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
func makeVisitor(file: SwiftLintFile) -> ViolationsSyntaxVisitor {
|
||||||
|
Visitor(viewMode: .sourceAccurate, testParentClasses: configuration.testParentClasses)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension NoMagicNumbersRule {
|
||||||
|
final class Visitor: ViolationsSyntaxVisitor {
|
||||||
|
private let testParentClasses: Set<String>
|
||||||
|
|
||||||
|
init(viewMode: SyntaxTreeViewMode, testParentClasses: Set<String>) {
|
||||||
|
self.testParentClasses = testParentClasses
|
||||||
|
super.init(viewMode: viewMode)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func visitPost(_ node: FloatLiteralExprSyntax) {
|
||||||
|
if node.isMemberOfATestClass(testParentClasses) == false, node.floatingDigits.isMagicNumber {
|
||||||
|
violations.append(node.floatingDigits.positionAfterSkippingLeadingTrivia)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func visitPost(_ node: IntegerLiteralExprSyntax) {
|
||||||
|
if node.isMemberOfATestClass(testParentClasses) == false, node.digits.isMagicNumber {
|
||||||
|
violations.append(node.digits.positionAfterSkippingLeadingTrivia)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension TokenSyntax {
|
||||||
|
var isMagicNumber: Bool {
|
||||||
|
guard let number = Double(text.replacingOccurrences(of: "_", with: "")) else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if [0, 1].contains(number) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
guard let grandparent = parent?.parent else {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return !grandparent.is(InitializerClauseSyntax.self)
|
||||||
|
&& grandparent.as(PrefixOperatorExprSyntax.self)?.parent?.is(InitializerClauseSyntax.self) != true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension ExprSyntaxProtocol {
|
||||||
|
func isMemberOfATestClass(_ testParentClasses: Set<String>) -> Bool {
|
||||||
|
var parent = parent
|
||||||
|
while parent != nil {
|
||||||
|
if
|
||||||
|
let classDecl = parent?.as(ClassDeclSyntax.self),
|
||||||
|
classDecl.isXCTestCase(testParentClasses)
|
||||||
|
{
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
parent = parent?.parent
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,14 +1,12 @@
|
||||||
import SwiftSyntax
|
import SwiftSyntax
|
||||||
|
|
||||||
struct ObjectLiteralRule: SwiftSyntaxRule, ConfigurationProviderRule, OptInRule {
|
struct ObjectLiteralRule: SwiftSyntaxRule, ConfigurationProviderRule, OptInRule {
|
||||||
var configuration = ObjectLiteralConfiguration()
|
var configuration = ObjectLiteralConfiguration<Self>()
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "object_literal",
|
identifier: "object_literal",
|
||||||
name: "Object Literal",
|
name: "Object Literal",
|
||||||
description: "Prefer object literals over image and color inits.",
|
description: "Prefer object literals over image and color inits",
|
||||||
kind: .idiomatic,
|
kind: .idiomatic,
|
||||||
nonTriggeringExamples: [
|
nonTriggeringExamples: [
|
||||||
Example("let image = #imageLiteral(resourceName: \"image.jpg\")"),
|
Example("let image = #imageLiteral(resourceName: \"image.jpg\")"),
|
||||||
|
@ -54,7 +52,7 @@ private extension ObjectLiteralRule {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let name = node.calledExpression.withoutTrivia().description
|
let name = node.calledExpression.trimmedDescription
|
||||||
if validateImageLiteral, isImageNamedInit(node: node, name: name) {
|
if validateImageLiteral, isImageNamedInit(node: node, name: name) {
|
||||||
violations.append(node.positionAfterSkippingLeadingTrivia)
|
violations.append(node.positionAfterSkippingLeadingTrivia)
|
||||||
} else if validateColorLiteral, isColorInit(node: node, name: name) {
|
} else if validateColorLiteral, isColorInit(node: node, name: name) {
|
|
@ -1,14 +1,12 @@
|
||||||
import SwiftSyntax
|
import SwiftSyntax
|
||||||
|
|
||||||
struct PatternMatchingKeywordsRule: SwiftSyntaxRule, ConfigurationProviderRule, OptInRule {
|
struct PatternMatchingKeywordsRule: SwiftSyntaxRule, ConfigurationProviderRule, OptInRule {
|
||||||
var configuration = SeverityConfiguration(.warning)
|
var configuration = SeverityConfiguration<Self>(.warning)
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "pattern_matching_keywords",
|
identifier: "pattern_matching_keywords",
|
||||||
name: "Pattern Matching Keywords",
|
name: "Pattern Matching Keywords",
|
||||||
description: "Combine multiple pattern matching bindings by moving keywords out of tuples.",
|
description: "Combine multiple pattern matching bindings by moving keywords out of tuples",
|
||||||
kind: .idiomatic,
|
kind: .idiomatic,
|
||||||
nonTriggeringExamples: [
|
nonTriggeringExamples: [
|
||||||
Example("default"),
|
Example("default"),
|
||||||
|
@ -57,12 +55,12 @@ private extension PatternMatchingKeywordsRule {
|
||||||
}
|
}
|
||||||
|
|
||||||
guard list.count > 1,
|
guard list.count > 1,
|
||||||
let firstLetOrVar = list.first?.letOrVarKeyword.tokenKind else {
|
let firstLetOrVar = list.first?.bindingKeyword.tokenKind else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let hasViolation = list.allSatisfy { elem in
|
let hasViolation = list.allSatisfy { elem in
|
||||||
elem.letOrVarKeyword.tokenKind == firstLetOrVar
|
elem.bindingKeyword.tokenKind == firstLetOrVar
|
||||||
}
|
}
|
||||||
|
|
||||||
guard hasViolation else {
|
guard hasViolation else {
|
||||||
|
@ -70,7 +68,7 @@ private extension PatternMatchingKeywordsRule {
|
||||||
}
|
}
|
||||||
|
|
||||||
violations.append(contentsOf: list.compactMap { elem in
|
violations.append(contentsOf: list.compactMap { elem in
|
||||||
return elem.letOrVarKeyword.positionAfterSkippingLeadingTrivia
|
return elem.bindingKeyword.positionAfterSkippingLeadingTrivia
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,14 +1,12 @@
|
||||||
import SwiftSyntax
|
import SwiftSyntax
|
||||||
|
|
||||||
struct PreferNimbleRule: SwiftSyntaxRule, OptInRule, ConfigurationProviderRule {
|
struct PreferNimbleRule: SwiftSyntaxRule, OptInRule, ConfigurationProviderRule {
|
||||||
var configuration = SeverityConfiguration(.warning)
|
var configuration = SeverityConfiguration<Self>(.warning)
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "prefer_nimble",
|
identifier: "prefer_nimble",
|
||||||
name: "Prefer Nimble",
|
name: "Prefer Nimble",
|
||||||
description: "Prefer Nimble matchers over XCTAssert functions.",
|
description: "Prefer Nimble matchers over XCTAssert functions",
|
||||||
kind: .idiomatic,
|
kind: .idiomatic,
|
||||||
nonTriggeringExamples: [
|
nonTriggeringExamples: [
|
||||||
Example("expect(foo) == 1"),
|
Example("expect(foo) == 1"),
|
|
@ -1,7 +1,7 @@
|
||||||
import SwiftSyntax
|
import SwiftSyntax
|
||||||
|
|
||||||
struct PreferZeroOverExplicitInitRule: SwiftSyntaxCorrectableRule, OptInRule, ConfigurationProviderRule {
|
struct PreferZeroOverExplicitInitRule: SwiftSyntaxCorrectableRule, OptInRule, ConfigurationProviderRule {
|
||||||
var configuration = SeverityConfiguration(.warning)
|
var configuration = SeverityConfiguration<Self>(.warning)
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "prefer_zero_over_explicit_init",
|
identifier: "prefer_zero_over_explicit_init",
|
||||||
|
@ -34,8 +34,6 @@ struct PreferZeroOverExplicitInitRule: SwiftSyntaxCorrectableRule, OptInRule, Co
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
func makeVisitor(file: SwiftLintFile) -> ViolationsSyntaxVisitor {
|
func makeVisitor(file: SwiftLintFile) -> ViolationsSyntaxVisitor {
|
||||||
Visitor(viewMode: .sourceAccurate)
|
Visitor(viewMode: .sourceAccurate)
|
||||||
}
|
}
|
||||||
|
@ -76,11 +74,12 @@ private extension PreferZeroOverExplicitInitRule {
|
||||||
|
|
||||||
correctionPositions.append(node.positionAfterSkippingLeadingTrivia)
|
correctionPositions.append(node.positionAfterSkippingLeadingTrivia)
|
||||||
|
|
||||||
let newNode: MemberAccessExprSyntax = "\(name).zero"
|
let newNode = MemberAccessExprSyntax(name: "zero")
|
||||||
|
.with(\.base, "\(raw: name)")
|
||||||
return super.visit(
|
return super.visit(
|
||||||
newNode
|
newNode
|
||||||
.withLeadingTrivia(node.leadingTrivia ?? .zero)
|
.with(\.leadingTrivia, node.leadingTrivia)
|
||||||
.withTrailingTrivia(node.trailingTrivia ?? .zero)
|
.with(\.trailingTrivia, node.trailingTrivia)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,14 +1,12 @@
|
||||||
import SwiftSyntax
|
import SwiftSyntax
|
||||||
|
|
||||||
struct PrivateOverFilePrivateRule: ConfigurationProviderRule, SwiftSyntaxCorrectableRule {
|
struct PrivateOverFilePrivateRule: ConfigurationProviderRule, SwiftSyntaxCorrectableRule {
|
||||||
var configuration = PrivateOverFilePrivateRuleConfiguration()
|
var configuration = PrivateOverFilePrivateConfiguration()
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "private_over_fileprivate",
|
identifier: "private_over_fileprivate",
|
||||||
name: "Private over fileprivate",
|
name: "Private over Fileprivate",
|
||||||
description: "Prefer `private` over `fileprivate` declarations.",
|
description: "Prefer `private` over `fileprivate` declarations",
|
||||||
kind: .idiomatic,
|
kind: .idiomatic,
|
||||||
nonTriggeringExamples: [
|
nonTriggeringExamples: [
|
||||||
Example("extension String {}"),
|
Example("extension String {}"),
|
||||||
|
@ -150,108 +148,123 @@ private extension PrivateOverFilePrivateRule {
|
||||||
// don't call super in any of the `visit` methods to avoid digging into the children
|
// don't call super in any of the `visit` methods to avoid digging into the children
|
||||||
override func visit(_ node: ExtensionDeclSyntax) -> DeclSyntax {
|
override func visit(_ node: ExtensionDeclSyntax) -> DeclSyntax {
|
||||||
guard validateExtensions, let modifier = node.modifiers.fileprivateModifier,
|
guard validateExtensions, let modifier = node.modifiers.fileprivateModifier,
|
||||||
|
let modifierIndex = node.modifiers.fileprivateModifierIndex,
|
||||||
!node.isContainedIn(regions: disabledRegions, locationConverter: locationConverter) else {
|
!node.isContainedIn(regions: disabledRegions, locationConverter: locationConverter) else {
|
||||||
return DeclSyntax(node)
|
return DeclSyntax(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
correctionPositions.append(modifier.positionAfterSkippingLeadingTrivia)
|
correctionPositions.append(modifier.positionAfterSkippingLeadingTrivia)
|
||||||
let newNode = node.withModifiers(node.modifiers?.replacing(fileprivateModifier: modifier))
|
let newNode = node.with(\.modifiers, node.modifiers?.replacing(fileprivateModifierIndex: modifierIndex))
|
||||||
return DeclSyntax(newNode)
|
return DeclSyntax(newNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func visit(_ node: ClassDeclSyntax) -> DeclSyntax {
|
override func visit(_ node: ClassDeclSyntax) -> DeclSyntax {
|
||||||
guard let modifier = node.modifiers.fileprivateModifier,
|
guard let modifier = node.modifiers.fileprivateModifier,
|
||||||
|
let modifierIndex = node.modifiers.fileprivateModifierIndex,
|
||||||
!node.isContainedIn(regions: disabledRegions, locationConverter: locationConverter) else {
|
!node.isContainedIn(regions: disabledRegions, locationConverter: locationConverter) else {
|
||||||
return DeclSyntax(node)
|
return DeclSyntax(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
correctionPositions.append(modifier.positionAfterSkippingLeadingTrivia)
|
correctionPositions.append(modifier.positionAfterSkippingLeadingTrivia)
|
||||||
let newNode = node.withModifiers(node.modifiers?.replacing(fileprivateModifier: modifier))
|
let newNode = node.with(\.modifiers, node.modifiers?.replacing(fileprivateModifierIndex: modifierIndex))
|
||||||
return DeclSyntax(newNode)
|
return DeclSyntax(newNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func visit(_ node: StructDeclSyntax) -> DeclSyntax {
|
override func visit(_ node: StructDeclSyntax) -> DeclSyntax {
|
||||||
guard let modifier = node.modifiers.fileprivateModifier,
|
guard let modifier = node.modifiers.fileprivateModifier,
|
||||||
|
let modifierIndex = node.modifiers.fileprivateModifierIndex,
|
||||||
!node.isContainedIn(regions: disabledRegions, locationConverter: locationConverter) else {
|
!node.isContainedIn(regions: disabledRegions, locationConverter: locationConverter) else {
|
||||||
return DeclSyntax(node)
|
return DeclSyntax(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
correctionPositions.append(modifier.positionAfterSkippingLeadingTrivia)
|
correctionPositions.append(modifier.positionAfterSkippingLeadingTrivia)
|
||||||
let newNode = node.withModifiers(node.modifiers?.replacing(fileprivateModifier: modifier))
|
let newNode = node.with(\.modifiers, node.modifiers?.replacing(fileprivateModifierIndex: modifierIndex))
|
||||||
return DeclSyntax(newNode)
|
return DeclSyntax(newNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func visit(_ node: EnumDeclSyntax) -> DeclSyntax {
|
override func visit(_ node: EnumDeclSyntax) -> DeclSyntax {
|
||||||
guard let modifier = node.modifiers.fileprivateModifier,
|
guard let modifier = node.modifiers.fileprivateModifier,
|
||||||
|
let modifierIndex = node.modifiers.fileprivateModifierIndex,
|
||||||
!node.isContainedIn(regions: disabledRegions, locationConverter: locationConverter) else {
|
!node.isContainedIn(regions: disabledRegions, locationConverter: locationConverter) else {
|
||||||
return DeclSyntax(node)
|
return DeclSyntax(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
correctionPositions.append(modifier.positionAfterSkippingLeadingTrivia)
|
correctionPositions.append(modifier.positionAfterSkippingLeadingTrivia)
|
||||||
let newNode = node.withModifiers(node.modifiers?.replacing(fileprivateModifier: modifier))
|
let newNode = node.with(\.modifiers, node.modifiers?.replacing(fileprivateModifierIndex: modifierIndex))
|
||||||
return DeclSyntax(newNode)
|
return DeclSyntax(newNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func visit(_ node: ProtocolDeclSyntax) -> DeclSyntax {
|
override func visit(_ node: ProtocolDeclSyntax) -> DeclSyntax {
|
||||||
guard let modifier = node.modifiers.fileprivateModifier,
|
guard let modifier = node.modifiers.fileprivateModifier,
|
||||||
|
let modifierIndex = node.modifiers.fileprivateModifierIndex,
|
||||||
!node.isContainedIn(regions: disabledRegions, locationConverter: locationConverter) else {
|
!node.isContainedIn(regions: disabledRegions, locationConverter: locationConverter) else {
|
||||||
return DeclSyntax(node)
|
return DeclSyntax(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
correctionPositions.append(modifier.positionAfterSkippingLeadingTrivia)
|
correctionPositions.append(modifier.positionAfterSkippingLeadingTrivia)
|
||||||
let newNode = node.withModifiers(node.modifiers?.replacing(fileprivateModifier: modifier))
|
let newNode = node.with(\.modifiers, node.modifiers?.replacing(fileprivateModifierIndex: modifierIndex))
|
||||||
return DeclSyntax(newNode)
|
return DeclSyntax(newNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func visit(_ node: FunctionDeclSyntax) -> DeclSyntax {
|
override func visit(_ node: FunctionDeclSyntax) -> DeclSyntax {
|
||||||
guard let modifier = node.modifiers.fileprivateModifier,
|
guard let modifier = node.modifiers.fileprivateModifier,
|
||||||
|
let modifierIndex = node.modifiers.fileprivateModifierIndex,
|
||||||
!node.isContainedIn(regions: disabledRegions, locationConverter: locationConverter) else {
|
!node.isContainedIn(regions: disabledRegions, locationConverter: locationConverter) else {
|
||||||
return DeclSyntax(node)
|
return DeclSyntax(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
correctionPositions.append(modifier.positionAfterSkippingLeadingTrivia)
|
correctionPositions.append(modifier.positionAfterSkippingLeadingTrivia)
|
||||||
let newNode = node.withModifiers(node.modifiers?.replacing(fileprivateModifier: modifier))
|
let newNode = node.with(\.modifiers, node.modifiers?.replacing(fileprivateModifierIndex: modifierIndex))
|
||||||
return DeclSyntax(newNode)
|
return DeclSyntax(newNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func visit(_ node: VariableDeclSyntax) -> DeclSyntax {
|
override func visit(_ node: VariableDeclSyntax) -> DeclSyntax {
|
||||||
guard let modifier = node.modifiers.fileprivateModifier,
|
guard let modifier = node.modifiers.fileprivateModifier,
|
||||||
|
let modifierIndex = node.modifiers.fileprivateModifierIndex,
|
||||||
!node.isContainedIn(regions: disabledRegions, locationConverter: locationConverter) else {
|
!node.isContainedIn(regions: disabledRegions, locationConverter: locationConverter) else {
|
||||||
return DeclSyntax(node)
|
return DeclSyntax(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
correctionPositions.append(modifier.positionAfterSkippingLeadingTrivia)
|
correctionPositions.append(modifier.positionAfterSkippingLeadingTrivia)
|
||||||
let newNode = node.withModifiers(node.modifiers?.replacing(fileprivateModifier: modifier))
|
let newNode = node.with(\.modifiers, node.modifiers?.replacing(fileprivateModifierIndex: modifierIndex))
|
||||||
return DeclSyntax(newNode)
|
return DeclSyntax(newNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func visit(_ node: TypealiasDeclSyntax) -> DeclSyntax {
|
override func visit(_ node: TypealiasDeclSyntax) -> DeclSyntax {
|
||||||
guard let modifier = node.modifiers.fileprivateModifier,
|
guard let modifier = node.modifiers.fileprivateModifier,
|
||||||
|
let modifierIndex = node.modifiers.fileprivateModifierIndex,
|
||||||
!node.isContainedIn(regions: disabledRegions, locationConverter: locationConverter) else {
|
!node.isContainedIn(regions: disabledRegions, locationConverter: locationConverter) else {
|
||||||
return DeclSyntax(node)
|
return DeclSyntax(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
correctionPositions.append(modifier.positionAfterSkippingLeadingTrivia)
|
correctionPositions.append(modifier.positionAfterSkippingLeadingTrivia)
|
||||||
let newNode = node.withModifiers(node.modifiers?.replacing(fileprivateModifier: modifier))
|
let newNode = node.with(\.modifiers, node.modifiers?.replacing(fileprivateModifierIndex: modifierIndex))
|
||||||
return DeclSyntax(newNode)
|
return DeclSyntax(newNode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension ModifierListSyntax? {
|
private extension ModifierListSyntax? {
|
||||||
|
var fileprivateModifierIndex: ModifierListSyntax.Index? {
|
||||||
|
self?.firstIndex(where: { $0.name.tokenKind == .keyword(.fileprivate) })
|
||||||
|
}
|
||||||
|
|
||||||
var fileprivateModifier: DeclModifierSyntax? {
|
var fileprivateModifier: DeclModifierSyntax? {
|
||||||
self?.first { $0.name.tokenKind == .fileprivateKeyword }
|
fileprivateModifierIndex.flatMap { self?[$0] }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension ModifierListSyntax {
|
private extension ModifierListSyntax {
|
||||||
func replacing(fileprivateModifier: DeclModifierSyntax) -> ModifierListSyntax? {
|
func replacing(fileprivateModifierIndex: ModifierListSyntax.Index) -> ModifierListSyntax? {
|
||||||
replacing(
|
let fileprivateModifier = self[fileprivateModifierIndex]
|
||||||
childAt: fileprivateModifier.indexInParent,
|
return replacing(
|
||||||
with: fileprivateModifier.withName(
|
childAt: self.distance(from: self.startIndex, to: fileprivateModifierIndex),
|
||||||
.privateKeyword(
|
with: fileprivateModifier.with(
|
||||||
leadingTrivia: fileprivateModifier.leadingTrivia ?? .zero,
|
\.name,
|
||||||
trailingTrivia: fileprivateModifier.trailingTrivia ?? .zero
|
.keyword(
|
||||||
|
.private,
|
||||||
|
leadingTrivia: fileprivateModifier.leadingTrivia,
|
||||||
|
trailingTrivia: fileprivateModifier.trailingTrivia
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
|
@ -1,9 +1,7 @@
|
||||||
import SwiftSyntax
|
import SwiftSyntax
|
||||||
|
|
||||||
struct RedundantNilCoalescingRule: OptInRule, SwiftSyntaxCorrectableRule, ConfigurationProviderRule {
|
struct RedundantNilCoalescingRule: OptInRule, SwiftSyntaxCorrectableRule, ConfigurationProviderRule {
|
||||||
var configuration = SeverityConfiguration(.warning)
|
var configuration = SeverityConfiguration<Self>(.warning)
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "redundant_nil_coalescing",
|
identifier: "redundant_nil_coalescing",
|
||||||
|
@ -38,7 +36,8 @@ struct RedundantNilCoalescingRule: OptInRule, SwiftSyntaxCorrectableRule, Config
|
||||||
private extension RedundantNilCoalescingRule {
|
private extension RedundantNilCoalescingRule {
|
||||||
final class Visitor: ViolationsSyntaxVisitor {
|
final class Visitor: ViolationsSyntaxVisitor {
|
||||||
override func visitPost(_ node: TokenSyntax) {
|
override func visitPost(_ node: TokenSyntax) {
|
||||||
if node.tokenKind.isNilCoalescingOperator && node.nextToken?.tokenKind == .nilKeyword {
|
if node.tokenKind.isNilCoalescingOperator,
|
||||||
|
node.nextToken(viewMode: .sourceAccurate)?.tokenKind == .keyword(.nil) {
|
||||||
violations.append(node.position)
|
violations.append(node.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,7 +65,7 @@ private extension RedundantNilCoalescingRule {
|
||||||
return super.visit(node)
|
return super.visit(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
let newNode = node.removingLast().removingLast().withoutTrailingTrivia()
|
let newNode = node.removingLast().removingLast().with(\.trailingTrivia, [])
|
||||||
correctionPositions.append(newNode.endPosition)
|
correctionPositions.append(newNode.endPosition)
|
||||||
return super.visit(newNode)
|
return super.visit(newNode)
|
||||||
}
|
}
|
||||||
|
@ -75,6 +74,6 @@ private extension RedundantNilCoalescingRule {
|
||||||
|
|
||||||
private extension TokenKind {
|
private extension TokenKind {
|
||||||
var isNilCoalescingOperator: Bool {
|
var isNilCoalescingOperator: Bool {
|
||||||
self == .spacedBinaryOperator("??") || self == .unspacedBinaryOperator("??")
|
self == .binaryOperator("??")
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,14 +6,12 @@ private let attributeNamesImplyingObjc: Set<String> = [
|
||||||
]
|
]
|
||||||
|
|
||||||
struct RedundantObjcAttributeRule: SwiftSyntaxRule, SubstitutionCorrectableRule, ConfigurationProviderRule {
|
struct RedundantObjcAttributeRule: SwiftSyntaxRule, SubstitutionCorrectableRule, ConfigurationProviderRule {
|
||||||
var configuration = SeverityConfiguration(.warning)
|
var configuration = SeverityConfiguration<Self>(.warning)
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "redundant_objc_attribute",
|
identifier: "redundant_objc_attribute",
|
||||||
name: "Redundant @objc Attribute",
|
name: "Redundant @objc Attribute",
|
||||||
description: "Objective-C attribute (@objc) is redundant in declaration.",
|
description: "Objective-C attribute (@objc) is redundant in declaration",
|
||||||
kind: .idiomatic,
|
kind: .idiomatic,
|
||||||
nonTriggeringExamples: RedundantObjcAttributeRuleExamples.nonTriggeringExamples,
|
nonTriggeringExamples: RedundantObjcAttributeRuleExamples.nonTriggeringExamples,
|
||||||
triggeringExamples: RedundantObjcAttributeRuleExamples.triggeringExamples,
|
triggeringExamples: RedundantObjcAttributeRuleExamples.triggeringExamples,
|
||||||
|
@ -42,22 +40,15 @@ struct RedundantObjcAttributeRule: SwiftSyntaxRule, SubstitutionCorrectableRule,
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension AttributeListSyntax {
|
private extension AttributeListSyntax {
|
||||||
var hasObjCMembers: Bool {
|
|
||||||
contains { $0.as(AttributeSyntax.self)?.attributeName.tokenKind == .identifier("objcMembers") }
|
|
||||||
}
|
|
||||||
|
|
||||||
var objCAttribute: AttributeSyntax? {
|
var objCAttribute: AttributeSyntax? {
|
||||||
lazy
|
lazy
|
||||||
.compactMap { $0.as(AttributeSyntax.self) }
|
.compactMap { $0.as(AttributeSyntax.self) }
|
||||||
.first { attribute in
|
.first { $0.attributeNameText == "objc" && $0.argument == nil }
|
||||||
attribute.attributeName.tokenKind == .contextualKeyword("objc") &&
|
|
||||||
attribute.argument == nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var hasAttributeImplyingObjC: Bool {
|
var hasAttributeImplyingObjC: Bool {
|
||||||
contains { element in
|
contains { element in
|
||||||
guard case let .identifier(attributeName) = element.as(AttributeSyntax.self)?.attributeName.tokenKind else {
|
guard let attributeName = element.as(AttributeSyntax.self)?.attributeNameText else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,6 +68,15 @@ private extension Syntax {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var functionOrVariableModifiers: ModifierListSyntax? {
|
||||||
|
if let functionDecl = self.as(FunctionDeclSyntax.self) {
|
||||||
|
return functionDecl.modifiers
|
||||||
|
} else if let variableDecl = self.as(VariableDeclSyntax.self) {
|
||||||
|
return variableDecl.modifiers
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension AttributeListSyntax {
|
private extension AttributeListSyntax {
|
||||||
|
@ -87,10 +87,12 @@ private extension AttributeListSyntax {
|
||||||
|
|
||||||
if hasAttributeImplyingObjC, parent?.is(ExtensionDeclSyntax.self) != true {
|
if hasAttributeImplyingObjC, parent?.is(ExtensionDeclSyntax.self) != true {
|
||||||
return objcAttribute
|
return objcAttribute
|
||||||
|
} else if parent?.is(EnumDeclSyntax.self) == true {
|
||||||
|
return nil
|
||||||
} else if parent?.isFunctionOrStoredProperty == true,
|
} else if parent?.isFunctionOrStoredProperty == true,
|
||||||
let parentClassDecl = parent?.parent?.parent?.parent?.parent?.as(ClassDeclSyntax.self),
|
let parentClassDecl = parent?.parent?.parent?.parent?.parent?.as(ClassDeclSyntax.self),
|
||||||
parentClassDecl.attributes?.hasObjCMembers == true {
|
parentClassDecl.attributes.contains(attributeNamed: "objcMembers") {
|
||||||
return objcAttribute
|
return parent?.functionOrVariableModifiers.isPrivateOrFileprivate == true ? nil : objcAttribute
|
||||||
} else if let parentExtensionDecl = parent?.parent?.parent?.parent?.parent?.as(ExtensionDeclSyntax.self),
|
} else if let parentExtensionDecl = parent?.parent?.parent?.parent?.parent?.as(ExtensionDeclSyntax.self),
|
||||||
parentExtensionDecl.attributes?.objCAttribute != nil {
|
parentExtensionDecl.attributes?.objCAttribute != nil {
|
||||||
return objcAttribute
|
return objcAttribute
|
|
@ -13,7 +13,7 @@ struct RedundantObjcAttributeRuleExamples {
|
||||||
class Foo {
|
class Foo {
|
||||||
var bar: Any?
|
var bar: Any?
|
||||||
@objc
|
@objc
|
||||||
class Bar {
|
class Bar: NSObject {
|
||||||
@objc
|
@objc
|
||||||
var foo: Any?
|
var foo: Any?
|
||||||
}
|
}
|
||||||
|
@ -59,14 +59,15 @@ struct RedundantObjcAttributeRuleExamples {
|
||||||
Example("""
|
Example("""
|
||||||
@objcMembers
|
@objcMembers
|
||||||
class Foo {
|
class Foo {
|
||||||
|
@objc
|
||||||
class Bar: NSObject {
|
class Bar: NSObject {
|
||||||
@objc var foo: Any
|
@objc var foo: Any?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""),
|
"""),
|
||||||
Example("""
|
Example("""
|
||||||
@objcMembers
|
@objcMembers
|
||||||
class Foo {
|
class Foo: NSObject {
|
||||||
@objc class Bar {}
|
@objc class Bar {}
|
||||||
}
|
}
|
||||||
"""),
|
"""),
|
||||||
|
@ -75,6 +76,59 @@ struct RedundantObjcAttributeRuleExamples {
|
||||||
@objc(addElementsObject:)
|
@objc(addElementsObject:)
|
||||||
@NSManaged public func addToElements(_ value: BlockEditorSettingElement)
|
@NSManaged public func addToElements(_ value: BlockEditorSettingElement)
|
||||||
}
|
}
|
||||||
|
"""),
|
||||||
|
Example("""
|
||||||
|
@objcMembers
|
||||||
|
public class Foo: NSObject {
|
||||||
|
@objc
|
||||||
|
private func handler(_ notification: Notification) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func registerForNotifications() {
|
||||||
|
NotificationCenter.default.addObserver(self, selector: #selector(handler(_:)), name: nil, object: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""),
|
||||||
|
Example("""
|
||||||
|
class Foo: NSObject { }
|
||||||
|
|
||||||
|
@objc extension Foo {
|
||||||
|
@objc enum Bar: Int {
|
||||||
|
case bar
|
||||||
|
}
|
||||||
|
|
||||||
|
var bar: Bar { .bar }
|
||||||
|
}
|
||||||
|
"""),
|
||||||
|
Example("""
|
||||||
|
class Foo: NSObject { }
|
||||||
|
|
||||||
|
@objc extension Foo {
|
||||||
|
@objc private enum Baz: Int {
|
||||||
|
case baz
|
||||||
|
}
|
||||||
|
|
||||||
|
private var baz: Baz { .baz }
|
||||||
|
}
|
||||||
|
"""),
|
||||||
|
Example("""
|
||||||
|
@objcMembers
|
||||||
|
internal class Foo: NSObject {
|
||||||
|
@objc
|
||||||
|
private var baz: Int = 1
|
||||||
|
|
||||||
|
var x: Any? {
|
||||||
|
value(forKey: "baz")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""),
|
||||||
|
Example("""
|
||||||
|
@objcMembers
|
||||||
|
class Foo: NSObject {
|
||||||
|
@objc enum Bar: Int {
|
||||||
|
case bar
|
||||||
|
}
|
||||||
|
}
|
||||||
""")
|
""")
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -90,13 +144,13 @@ struct RedundantObjcAttributeRuleExamples {
|
||||||
Example("↓@objc @IBDesignable class Foo {}"),
|
Example("↓@objc @IBDesignable class Foo {}"),
|
||||||
Example("""
|
Example("""
|
||||||
@objcMembers
|
@objcMembers
|
||||||
class Foo {
|
class Foo: NSObject {
|
||||||
↓@objc var bar: Any?
|
↓@objc var bar: Any?
|
||||||
}
|
}
|
||||||
"""),
|
"""),
|
||||||
Example("""
|
Example("""
|
||||||
@objcMembers
|
@objcMembers
|
||||||
class Foo {
|
class Foo: NSObject {
|
||||||
↓@objc var bar: Any?
|
↓@objc var bar: Any?
|
||||||
↓@objc var foo: Any?
|
↓@objc var foo: Any?
|
||||||
@objc
|
@objc
|
||||||
|
@ -126,7 +180,7 @@ struct RedundantObjcAttributeRuleExamples {
|
||||||
"""),
|
"""),
|
||||||
Example("""
|
Example("""
|
||||||
@objcMembers
|
@objcMembers
|
||||||
class Foo {
|
class Foo: NSObject {
|
||||||
@objcMembers
|
@objcMembers
|
||||||
class Bar: NSObject {
|
class Bar: NSObject {
|
||||||
↓@objc var foo: Any
|
↓@objc var foo: Any
|
||||||
|
@ -160,23 +214,23 @@ struct RedundantObjcAttributeRuleExamples {
|
||||||
Example("↓@objc @IBDesignable class Foo {}"): Example("@IBDesignable class Foo {}"),
|
Example("↓@objc @IBDesignable class Foo {}"): Example("@IBDesignable class Foo {}"),
|
||||||
Example("""
|
Example("""
|
||||||
@objcMembers
|
@objcMembers
|
||||||
class Foo {
|
class Foo: NSObject {
|
||||||
↓@objc var bar: Any?
|
↓@objc var bar: Any?
|
||||||
}
|
}
|
||||||
"""):
|
"""):
|
||||||
Example("""
|
Example("""
|
||||||
@objcMembers
|
@objcMembers
|
||||||
class Foo {
|
class Foo: NSObject {
|
||||||
var bar: Any?
|
var bar: Any?
|
||||||
}
|
}
|
||||||
"""),
|
"""),
|
||||||
Example("""
|
Example("""
|
||||||
@objcMembers
|
@objcMembers
|
||||||
class Foo {
|
class Foo: NSObject {
|
||||||
↓@objc var bar: Any?
|
↓@objc var bar: Any?
|
||||||
↓@objc var foo: Any?
|
↓@objc var foo: Any?
|
||||||
@objc
|
@objc
|
||||||
class Bar {
|
class Bar: NSObject {
|
||||||
@objc
|
@objc
|
||||||
var foo2: Any?
|
var foo2: Any?
|
||||||
}
|
}
|
||||||
|
@ -184,11 +238,11 @@ struct RedundantObjcAttributeRuleExamples {
|
||||||
"""):
|
"""):
|
||||||
Example("""
|
Example("""
|
||||||
@objcMembers
|
@objcMembers
|
||||||
class Foo {
|
class Foo: NSObject {
|
||||||
var bar: Any?
|
var bar: Any?
|
||||||
var foo: Any?
|
var foo: Any?
|
||||||
@objc
|
@objc
|
||||||
class Bar {
|
class Bar: NSObject {
|
||||||
@objc
|
@objc
|
||||||
var foo2: Any?
|
var foo2: Any?
|
||||||
}
|
}
|
||||||
|
@ -230,7 +284,7 @@ struct RedundantObjcAttributeRuleExamples {
|
||||||
"""),
|
"""),
|
||||||
Example("""
|
Example("""
|
||||||
@objcMembers
|
@objcMembers
|
||||||
class Foo {
|
class Foo: NSObject {
|
||||||
@objcMembers
|
@objcMembers
|
||||||
class Bar: NSObject {
|
class Bar: NSObject {
|
||||||
↓@objc var foo: Any
|
↓@objc var foo: Any
|
||||||
|
@ -239,7 +293,7 @@ struct RedundantObjcAttributeRuleExamples {
|
||||||
"""):
|
"""):
|
||||||
Example("""
|
Example("""
|
||||||
@objcMembers
|
@objcMembers
|
||||||
class Foo {
|
class Foo: NSObject {
|
||||||
@objcMembers
|
@objcMembers
|
||||||
class Bar: NSObject {
|
class Bar: NSObject {
|
||||||
var foo: Any
|
var foo: Any
|
|
@ -1,14 +1,12 @@
|
||||||
import SwiftSyntax
|
import SwiftSyntax
|
||||||
|
|
||||||
struct RedundantOptionalInitializationRule: SwiftSyntaxCorrectableRule, ConfigurationProviderRule {
|
struct RedundantOptionalInitializationRule: SwiftSyntaxCorrectableRule, ConfigurationProviderRule {
|
||||||
var configuration = SeverityConfiguration(.warning)
|
var configuration = SeverityConfiguration<Self>(.warning)
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "redundant_optional_initialization",
|
identifier: "redundant_optional_initialization",
|
||||||
name: "Redundant Optional Initialization",
|
name: "Redundant Optional Initialization",
|
||||||
description: "Initializing an optional variable with nil is redundant.",
|
description: "Initializing an optional variable with nil is redundant",
|
||||||
kind: .idiomatic,
|
kind: .idiomatic,
|
||||||
nonTriggeringExamples: [
|
nonTriggeringExamples: [
|
||||||
Example("var myVar: Int?\n"),
|
Example("var myVar: Int?\n"),
|
||||||
|
@ -96,12 +94,12 @@ struct RedundantOptionalInitializationRule: SwiftSyntaxCorrectableRule, Configur
|
||||||
"""),
|
"""),
|
||||||
Example("""
|
Example("""
|
||||||
func foo() {
|
func foo() {
|
||||||
var myVar: String?↓ = nil
|
var myVar: String?↓ = nil, b: Int
|
||||||
}
|
}
|
||||||
"""):
|
"""):
|
||||||
Example("""
|
Example("""
|
||||||
func foo() {
|
func foo() {
|
||||||
var myVar: String?
|
var myVar: String?, b: Int
|
||||||
}
|
}
|
||||||
""")
|
""")
|
||||||
]
|
]
|
||||||
|
@ -121,7 +119,7 @@ struct RedundantOptionalInitializationRule: SwiftSyntaxCorrectableRule, Configur
|
||||||
private extension RedundantOptionalInitializationRule {
|
private extension RedundantOptionalInitializationRule {
|
||||||
final class Visitor: ViolationsSyntaxVisitor {
|
final class Visitor: ViolationsSyntaxVisitor {
|
||||||
override func visitPost(_ node: VariableDeclSyntax) {
|
override func visitPost(_ node: VariableDeclSyntax) {
|
||||||
guard node.letOrVarKeyword.tokenKind == .varKeyword,
|
guard node.bindingKeyword.tokenKind == .keyword(.var),
|
||||||
!node.modifiers.containsLazy else {
|
!node.modifiers.containsLazy else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -141,7 +139,7 @@ private extension RedundantOptionalInitializationRule {
|
||||||
}
|
}
|
||||||
|
|
||||||
override func visit(_ node: VariableDeclSyntax) -> DeclSyntax {
|
override func visit(_ node: VariableDeclSyntax) -> DeclSyntax {
|
||||||
guard node.letOrVarKeyword.tokenKind == .varKeyword,
|
guard node.bindingKeyword.tokenKind == .keyword(.var),
|
||||||
!node.modifiers.containsLazy else {
|
!node.modifiers.containsLazy else {
|
||||||
return super.visit(node)
|
return super.visit(node)
|
||||||
}
|
}
|
||||||
|
@ -165,24 +163,24 @@ private extension RedundantOptionalInitializationRule {
|
||||||
guard violatingBindings.contains(binding) else {
|
guard violatingBindings.contains(binding) else {
|
||||||
return binding
|
return binding
|
||||||
}
|
}
|
||||||
|
let newBinding = binding.with(\.initializer, nil)
|
||||||
let newBinding = binding.withInitializer(nil)
|
if newBinding.accessor != nil {
|
||||||
|
|
||||||
if newBinding.accessor == nil {
|
|
||||||
return newBinding.withTrailingTrivia(binding.initializer?.trailingTrivia ?? .zero)
|
|
||||||
} else {
|
|
||||||
return newBinding
|
return newBinding
|
||||||
}
|
}
|
||||||
|
if binding.trailingComma != nil {
|
||||||
|
return newBinding.with(\.typeAnnotation, binding.typeAnnotation?.with(\.trailingTrivia, Trivia()))
|
||||||
|
}
|
||||||
|
return newBinding.with(\.trailingTrivia, binding.initializer?.trailingTrivia ?? Trivia())
|
||||||
})
|
})
|
||||||
|
|
||||||
return super.visit(node.withBindings(newBindings))
|
return super.visit(node.with(\.bindings, newBindings))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension PatternBindingSyntax {
|
private extension PatternBindingSyntax {
|
||||||
var violationPosition: AbsolutePosition? {
|
var violationPosition: AbsolutePosition? {
|
||||||
guard let initializer = initializer,
|
guard let initializer,
|
||||||
let type = typeAnnotation,
|
let type = typeAnnotation,
|
||||||
initializer.isInitializingToNil,
|
initializer.isInitializingToNil,
|
||||||
type.isOptionalType else {
|
type.isOptionalType else {
|
|
@ -1,15 +1,13 @@
|
||||||
import SwiftSyntax
|
import SwiftSyntax
|
||||||
|
|
||||||
struct RedundantSetAccessControlRule: ConfigurationProviderRule, SwiftSyntaxRule {
|
struct RedundantSetAccessControlRule: ConfigurationProviderRule, SwiftSyntaxRule {
|
||||||
var configuration = SeverityConfiguration(.warning)
|
var configuration = SeverityConfiguration<Self>(.warning)
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "redundant_set_access_control",
|
identifier: "redundant_set_access_control",
|
||||||
name: "Redundant Set Access Control Rule",
|
name: "Redundant Access Control for Setter",
|
||||||
description: "Property setter access level shouldn't be explicit if " +
|
description: "Property setter access level shouldn't be explicit if " +
|
||||||
"it's the same as the variable access level.",
|
"it's the same as the variable access level",
|
||||||
kind: .idiomatic,
|
kind: .idiomatic,
|
||||||
nonTriggeringExamples: [
|
nonTriggeringExamples: [
|
||||||
Example("private(set) public var foo: Int"),
|
Example("private(set) public var foo: Int"),
|
||||||
|
@ -82,11 +80,11 @@ private extension RedundantSetAccessControlRule {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if setAccessor.name.tokenKind == .fileprivateKeyword,
|
if setAccessor.name.tokenKind == .keyword(.fileprivate),
|
||||||
modifiers.getAccessor == nil,
|
modifiers.getAccessor == nil,
|
||||||
let closestDeclModifiers = node.closestDecl()?.modifiers {
|
let closestDeclModifiers = node.closestDecl()?.modifiers {
|
||||||
let closestDeclIsFilePrivate = closestDeclModifiers.contains {
|
let closestDeclIsFilePrivate = closestDeclModifiers.contains {
|
||||||
$0.name.tokenKind == .fileprivateKeyword
|
$0.name.tokenKind == .keyword(.fileprivate)
|
||||||
}
|
}
|
||||||
|
|
||||||
if closestDeclIsFilePrivate {
|
if closestDeclIsFilePrivate {
|
||||||
|
@ -95,12 +93,12 @@ private extension RedundantSetAccessControlRule {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if setAccessor.name.tokenKind == .internalKeyword,
|
if setAccessor.name.tokenKind == .keyword(.internal),
|
||||||
modifiers.getAccessor == nil,
|
modifiers.getAccessor == nil,
|
||||||
let closesDecl = node.closestDecl(),
|
let closesDecl = node.closestDecl(),
|
||||||
let closestDeclModifiers = closesDecl.modifiers {
|
let closestDeclModifiers = closesDecl.modifiers {
|
||||||
let closestDeclIsInternal = closestDeclModifiers.isEmpty || closestDeclModifiers.contains {
|
let closestDeclIsInternal = closestDeclModifiers.isEmpty || closestDeclModifiers.contains {
|
||||||
$0.name.tokenKind == .internalKeyword
|
$0.name.tokenKind == .keyword(.internal)
|
||||||
}
|
}
|
||||||
|
|
||||||
if closestDeclIsInternal {
|
if closestDeclIsInternal {
|
||||||
|
@ -144,7 +142,7 @@ private extension DeclSyntax {
|
||||||
|
|
||||||
private extension ModifierListSyntax {
|
private extension ModifierListSyntax {
|
||||||
var setAccessor: DeclModifierSyntax? {
|
var setAccessor: DeclModifierSyntax? {
|
||||||
first { $0.detail?.detail.tokenKind == .contextualKeyword("set") }
|
first { $0.detail?.detail.tokenKind == .identifier("set") }
|
||||||
}
|
}
|
||||||
|
|
||||||
var getAccessor: DeclModifierSyntax? {
|
var getAccessor: DeclModifierSyntax? {
|
|
@ -1,14 +1,12 @@
|
||||||
import SwiftSyntax
|
import SwiftSyntax
|
||||||
|
|
||||||
struct RedundantStringEnumValueRule: SwiftSyntaxRule, ConfigurationProviderRule {
|
struct RedundantStringEnumValueRule: SwiftSyntaxRule, ConfigurationProviderRule {
|
||||||
var configuration = SeverityConfiguration(.warning)
|
var configuration = SeverityConfiguration<Self>(.warning)
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "redundant_string_enum_value",
|
identifier: "redundant_string_enum_value",
|
||||||
name: "Redundant String Enum Value",
|
name: "Redundant String Enum Value",
|
||||||
description: "String enum values can be omitted when they are equal to the enumcase name.",
|
description: "String enum values can be omitted when they are equal to the enumcase name",
|
||||||
kind: .idiomatic,
|
kind: .idiomatic,
|
||||||
nonTriggeringExamples: [
|
nonTriggeringExamples: [
|
||||||
Example("""
|
Example("""
|
||||||
|
@ -73,7 +71,7 @@ private extension RedundantStringEnumValueRule {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let enumsWithExplicitValues = node.members.members
|
let enumsWithExplicitValues = node.memberBlock.members
|
||||||
.flatMap { member -> EnumCaseElementListSyntax in
|
.flatMap { member -> EnumCaseElementListSyntax in
|
||||||
guard let enumCaseDecl = member.decl.as(EnumCaseDeclSyntax.self) else {
|
guard let enumCaseDecl = member.decl.as(EnumCaseDeclSyntax.self) else {
|
||||||
return EnumCaseElementListSyntax([])
|
return EnumCaseElementListSyntax([])
|
||||||
|
@ -103,7 +101,7 @@ private extension RedundantStringEnumValueRule {
|
||||||
|
|
||||||
private extension EnumDeclSyntax {
|
private extension EnumDeclSyntax {
|
||||||
var isStringEnum: Bool {
|
var isStringEnum: Bool {
|
||||||
guard let inheritanceClause = inheritanceClause else {
|
guard let inheritanceClause else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import Foundation
|
||||||
import SourceKittenFramework
|
import SourceKittenFramework
|
||||||
|
|
||||||
struct RedundantTypeAnnotationRule: OptInRule, SubstitutionCorrectableRule, ConfigurationProviderRule {
|
struct RedundantTypeAnnotationRule: OptInRule, SubstitutionCorrectableRule, ConfigurationProviderRule {
|
||||||
var configuration = SeverityConfiguration(.warning)
|
var configuration = SeverityConfiguration<Self>(.warning)
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "redundant_type_annotation",
|
identifier: "redundant_type_annotation",
|
||||||
|
@ -110,7 +110,7 @@ struct RedundantTypeAnnotationRule: OptInRule, SubstitutionCorrectableRule, Conf
|
||||||
}
|
}
|
||||||
|
|
||||||
func violationRanges(in file: SwiftLintFile) -> [NSRange] {
|
func violationRanges(in file: SwiftLintFile) -> [NSRange] {
|
||||||
let violatingRanges = file
|
return file
|
||||||
.match(pattern: expressionPattern)
|
.match(pattern: expressionPattern)
|
||||||
.filter {
|
.filter {
|
||||||
$0.1 == [.keyword, .identifier, .typeidentifier, .identifier] ||
|
$0.1 == [.keyword, .identifier, .typeidentifier, .identifier] ||
|
||||||
|
@ -122,8 +122,6 @@ struct RedundantTypeAnnotationRule: OptInRule, SubstitutionCorrectableRule, Conf
|
||||||
file.match(pattern: typeAnnotationPattern,
|
file.match(pattern: typeAnnotationPattern,
|
||||||
excludingSyntaxKinds: SyntaxKind.commentAndStringKinds, range: $0.0).first
|
excludingSyntaxKinds: SyntaxKind.commentAndStringKinds, range: $0.0).first
|
||||||
}
|
}
|
||||||
|
|
||||||
return violatingRanges
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func isFalsePositive(file: SwiftLintFile, range: NSRange) -> Bool {
|
private func isFalsePositive(file: SwiftLintFile, range: NSRange) -> Bool {
|
|
@ -2,14 +2,12 @@ import Foundation
|
||||||
import SourceKittenFramework
|
import SourceKittenFramework
|
||||||
|
|
||||||
struct RedundantVoidReturnRule: ConfigurationProviderRule, SubstitutionCorrectableASTRule {
|
struct RedundantVoidReturnRule: ConfigurationProviderRule, SubstitutionCorrectableASTRule {
|
||||||
var configuration = SeverityConfiguration(.warning)
|
var configuration = SeverityConfiguration<Self>(.warning)
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "redundant_void_return",
|
identifier: "redundant_void_return",
|
||||||
name: "Redundant Void Return",
|
name: "Redundant Void Return",
|
||||||
description: "Returning Void in a function declaration is redundant.",
|
description: "Returning Void in a function declaration is redundant",
|
||||||
kind: .idiomatic,
|
kind: .idiomatic,
|
||||||
nonTriggeringExamples: [
|
nonTriggeringExamples: [
|
||||||
Example("func foo() {}\n"),
|
Example("func foo() {}\n"),
|
|
@ -1,14 +1,12 @@
|
||||||
import SwiftSyntax
|
import SwiftSyntax
|
||||||
|
|
||||||
struct ReturnValueFromVoidFunctionRule: ConfigurationProviderRule, OptInRule, SwiftSyntaxRule {
|
struct ReturnValueFromVoidFunctionRule: ConfigurationProviderRule, OptInRule, SwiftSyntaxRule {
|
||||||
var configuration = SeverityConfiguration(.warning)
|
var configuration = SeverityConfiguration<Self>(.warning)
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static let description = RuleDescription(
|
static let description = RuleDescription(
|
||||||
identifier: "return_value_from_void_function",
|
identifier: "return_value_from_void_function",
|
||||||
name: "Return Value from Void Function",
|
name: "Return Value from Void Function",
|
||||||
description: "Returning values from Void functions should be avoided.",
|
description: "Returning values from Void functions should be avoided",
|
||||||
kind: .idiomatic,
|
kind: .idiomatic,
|
||||||
minSwiftVersion: .fiveDotOne,
|
minSwiftVersion: .fiveDotOne,
|
||||||
nonTriggeringExamples: ReturnValueFromVoidFunctionRuleExamples.nonTriggeringExamples,
|
nonTriggeringExamples: ReturnValueFromVoidFunctionRuleExamples.nonTriggeringExamples,
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue