Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability for SwiftLint to lint files with full type-checked AST awareness #2343

Closed
wants to merge 10 commits into from

Conversation

jpsim
Copy link
Collaborator

@jpsim jpsim commented Aug 6, 2018

Note: very experimental and still a work in progress

This gives SwiftLint the ability to lint files with the assistance of the fully type-checked AST. This can enable much more powerful rules, such as linting for:

  • dead code
  • unused/duplicate imports
  • over-exposed declarations (e.g. internal that could be private)
  • superfluous type annotations (e.g. view.tintColor = UIColor.black vs view.tintColor = .black)
  • use of explicit or implicit self.

It requires passing a file with the full output from a clean & successful xcodebuild invocation (no incremental builds):

$ swiftlint [lint|autocorrect] --no-cache --compiler-log-path /path/to/xcodebuild.log

I've written a first rule to get started, which enforces the use of explicit references to self. when accessing instance members.

You can test this out on SwiftLint itself (which doesn't abide by this rule) by checking out this branch and running the following:

$ # Ensure derived data for SwiftLint is clean
$ xcodebuild -workspace SwiftLint.xcworkspace -scheme swiftlint > xcodebuild.log
$ echo "included:
  - Source
whitelist_rules:
  - explicit_self" > .swiftlint.yml
$ swift build -c release
$ .build/release/swiftlint autocorrect --no-cache --compiler-log-path xcodebuild.log

This will add explicit references to self. to all files linted.

The rules are expensive and awkward to run, which is why I've punted on building this for a while, but it's clear to me that it's very useful nonetheless, so hopefully we can improve this on all fronts from this starting point.

I'd like to get some basic tests running for this and clean up a few things before merging, which is why this is marked as WIP.

@jpsim jpsim requested a review from keith August 6, 2018 23:55
@jpsim jpsim requested a review from marcelofabri August 7, 2018 00:08
@SwiftLintBot
Copy link

SwiftLintBot commented Aug 7, 2018

1 Warning
⚠️ Big PR
12 Messages
📖 Linting Aerial with this PR took 0.38s vs 0.37s on master (2% slower)
📖 Linting Alamofire with this PR took 3.05s vs 3.16s on master (3% faster)
📖 Linting Firefox with this PR took 11.87s vs 11.97s on master (0% faster)
📖 Linting Kickstarter with this PR took 17.37s vs 17.25s on master (0% slower)
📖 Linting Moya with this PR took 1.92s vs 1.89s on master (1% slower)
📖 Linting Nimble with this PR took 1.68s vs 1.67s on master (0% slower)
📖 Linting Quick with this PR took 0.51s vs 0.5s on master (2% slower)
📖 Linting Realm with this PR took 3.22s vs 3.19s on master (0% slower)
📖 Linting SourceKitten with this PR took 1.0s vs 0.99s on master (1% slower)
📖 Linting Sourcery with this PR took 4.47s vs 4.39s on master (1% slower)
📖 Linting Swift with this PR took 27.84s vs 28.3s on master (1% faster)
📖 Linting WordPress with this PR took 16.54s vs 16.48s on master (0% slower)

Generated by 🚫 Danger

@codecov-io
Copy link

codecov-io commented Aug 7, 2018

Codecov Report

Merging #2343 into master will decrease coverage by 0.73%.
The diff coverage is 39.48%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #2343      +/-   ##
==========================================
- Coverage   91.89%   91.15%   -0.74%     
==========================================
  Files         304      307       +3     
  Lines       15254    15503     +249     
==========================================
+ Hits        14017    14132     +115     
- Misses       1237     1371     +134
Impacted Files Coverage Δ
.../swiftlint/Extensions/File+CompilerArguments.swift 0% <0%> (ø)
Source/swiftlint/Commands/LintCommand.swift 0% <0%> (ø) ⬆️
...iftlint/Extensions/Configuration+CommandLine.swift 0% <0%> (ø) ⬆️
...ource/swiftlint/Helpers/LintableFilesVisitor.swift 0% <0%> (ø)
Source/swiftlint/Commands/AutoCorrectCommand.swift 0% <0%> (ø) ⬆️
...ce/SwiftLintFramework/Models/RuleDescription.swift 93.75% <100%> (+0.41%) ⬆️
...iftLintFrameworkTests/TrailingCommaRuleTests.swift 97.72% <100%> (ø) ⬆️
...tFrameworkTests/AutomaticRuleTests.generated.swift 100% <100%> (ø) ⬆️
Source/SwiftLintFramework/Protocols/Rule.swift 83.33% <50%> (-16.67%) ⬇️
Source/SwiftLintFramework/Models/Linter.swift 82.26% <90%> (+0.25%) ⬆️
... and 7 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 5c9b6b9...5257b61. Read the comment docs.

@jpsim jpsim changed the title [WIP] Add ability for SwiftLint to lint files with full type-checked AST awareness Add ability for SwiftLint to lint files with full type-checked AST awareness Aug 12, 2018
@jpsim
Copy link
Collaborator Author

jpsim commented Aug 12, 2018

I've added support for testing compiler argument rules and added changelog entries, so this is no longer WIP if anyone's interested in giving this a proper review.

public protocol CompilerArgumentRule: Rule {}

public extension CompilerArgumentRule {
func validate(file: File) -> [StyleViolation] {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These default extensions don't seem like they would ever work for this type of rule? Are they required or you could omit them and require implementors to add them?

}
"""
],
requiresFileOnDisk: true
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This property isn't very clear to me, which files does it require?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requires that Files that are passed in have a valid path on disk, unlike other uses of File, some of which are initialized with File(contents:) and have no path on disk.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah got it. I wonder to avoid the force unwrapping here if it would be worth a different File type. But maybe that would be an unreasonably large change.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely a large change.


private func violationRanges(in file: File, compilerArguments: [String]) -> [NSRange] {
guard !compilerArguments.isEmpty else {
return []
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be an error of some kind instead?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll log an error here.

}

var compileCommand: [String]?
compilerLogs.enumerateLines { line, stop in
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a future optimization, but I wonder if it would be worth creating a dictionary of file path to commands up front that you could more easily access. I have to assume that doing this over and over is expensive with how large these files can be?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will for sure need to be done before adding more compiler argument rules.

@jpsim
Copy link
Collaborator Author

jpsim commented Aug 13, 2018

I spoke with @keith and @soffes about this earlier today and decided to take the following next steps:

  1. Add an analyze --xcode-log-path ... [--autocorrect] command similar to lint and autocorrect
  2. Ignore CompilerArgumentRules when running normal lint or autocorrect
  3. Add a analyze_rules configuration key where CompilerArgumentRules can be opted into on a per-configuration basis

@jpsim
Copy link
Collaborator Author

jpsim commented Sep 1, 2018

Continued in #2379. I opted to open a new PR to keep this one for historical purposes, given I rewrote large parts of it.

@jpsim jpsim closed this Sep 1, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants