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

#2737 - Fix unused_imports false positive when only operators from the module are used #2840

Merged
merged 2 commits into from Aug 23, 2019

Conversation

abdulowork
Copy link
Contributor

As was discussed #2737, I utilized the indexsource request to look up the operators and fetch the cursorInfo per operator. In the implementation I refrained from using usr to look up the operator because SourceKit doesn't support fetching cursorInfo by usr when it comes from C (but it can still be fetched by offset).

I also made an alternative implementation which uses the indexsource request instead of the editor.open. It seems to work with the unit tests but I am not 100% sure it doesn't introduce a regression since there is no oss-check for analyzer rules.

I also updated the example to better reflect the original issue in case you want to test the changes manually

import Foundation
import ObjectiveC
let 👨‍👩‍👧‍👦 = #selector(NSArray.contains(_:))
👨‍👩‍👧‍👦 == 👨‍👩‍👧‍👦
Copy link
Contributor Author

Choose a reason for hiding this comment

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

ObjectiveC module introduces an overload of the == operator:

public static func == (lhs: Selector, rhs: Selector) -> Bool

and even though Selector is introduced by the ObjectiveC module it can be indirectly created with #selector. This operator overload is the only way I found to reproduce the original issue in the unit tests.

@jpsim
Copy link
Collaborator

jpsim commented Aug 19, 2019

Great to see this 😍

Give me some time to take it for a spin and review.

@SwiftLintBot
Copy link

1 Warning
⚠️ This PR may need tests.
12 Messages
📖 Linting Aerial with this PR took 2.91s vs 3.33s on master (12% faster)
📖 Linting Alamofire with this PR took 5.86s vs 7.36s on master (20% faster)
📖 Linting Firefox with this PR took 14.57s vs 14.92s on master (2% faster)
📖 Linting Kickstarter with this PR took 29.58s vs 26.84s on master (10% slower)
📖 Linting Moya with this PR took 3.24s vs 2.53s on master (28% slower)
📖 Linting Nimble with this PR took 2.79s vs 2.25s on master (24% slower)
📖 Linting Quick with this PR took 1.01s vs 0.8s on master (26% slower)
📖 Linting Realm with this PR took 4.44s vs 3.96s on master (12% slower)
📖 Linting SourceKitten with this PR took 1.9s vs 1.58s on master (20% slower)
📖 Linting Sourcery with this PR took 5.83s vs 4.84s on master (20% slower)
📖 Linting Swift with this PR took 39.87s vs 39.32s on master (1% slower)
📖 Linting WordPress with this PR took 32.9s vs 32.28s on master (1% slower)

Generated by 🚫 Danger

@abdulowork
Copy link
Contributor Author

I tested the alternative implementation which uses indexsource exclusively and found some false positives. I guess lets leave it out of the scope of this issue.

}
.sorted(by: { $0.1.location < $1.1.location })
}

private func containsAttributesRequiringFoundation() -> Bool {
// Operators are omitted in the editor.open request and thus have to be looked up by the indexsource request
func operatorImports(arguments: [String], processedTokenOffsets: Set<Int>) -> Set<String> {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you mark these as private where possible?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since these methods are a part of a private extension, the compiler warns that using private is redundant in this case. Do you still want me to mark them private?
I could also make the extension non-private and the methods private for the sake of consistency.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Oh I didn’t realize these were in a private extension. All good!

@jpsim
Copy link
Collaborator

jpsim commented Aug 23, 2019

Code looks good, I'm validating this now on a large closed source project with a number of files where only operator overloads are used.

@jpsim
Copy link
Collaborator

jpsim commented Aug 23, 2019

One thing that running on a large codebase that this identified was that subscript extensions still aren't detected. Not a blocker to merging this but I'm curious to see if you think this approach can be extended to catch those? For example, if this is defined in ModuleA:

extension Collection where Index == Int {
    public subscript(safe index: Int) -> Iterator.Element? {
        return index < self.count && index >= 0 ? self[index] : nil
    }
}

and ModuleB has this:

import ModuleA

let array = ["a", "b", "c"]
_ = array[safe: 0] // equivalent to array.first

Then ModuleA will still be listed as an unused import.

@jpsim
Copy link
Collaborator

jpsim commented Aug 23, 2019

ok, finally validated this. Looks great, thank you so much for owning this @Biboran ! ❤️

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.

None yet

3 participants