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 DuplicateImportsRule #2004

Merged
merged 2 commits into from
Jan 17, 2019
Merged

Conversation

sammy-SC
Copy link
Contributor

@sammy-SC sammy-SC commented Jan 13, 2018

implements #1881

Any recommendations for improvement are very welcome.

if you are happy with implementation, I will be happy to get started on autocorrection.

@SwiftLintBot
Copy link

SwiftLintBot commented Jan 13, 2018

13 Warnings
⚠️ This PR introduced a violation in Firefox: /Users/vsts/agent/2.144.0/work/1/s/osscheck/Firefox/Sync/EncryptedJSON.swift:9:1: warning: Duplicate Imports Violation: Imports should be unique. (duplicate_imports)
⚠️ This PR introduced a violation in Kickstarter: /Users/vsts/agent/2.144.0/work/1/s/osscheck/Kickstarter/Library/TestHelpers/XCTestCase+AppEnvironment.swift:8:1: warning: Duplicate Imports Violation: Imports should be unique. (duplicate_imports)
⚠️ This PR introduced a violation in Kickstarter: /Users/vsts/agent/2.144.0/work/1/s/osscheck/Kickstarter/Library/ViewModels/MessagesSearchViewModel.swift:4:1: warning: Duplicate Imports Violation: Imports should be unique. (duplicate_imports)
⚠️ This PR introduced a violation in Kickstarter: /Users/vsts/agent/2.144.0/work/1/s/osscheck/Kickstarter/Library/ViewModels/SearchViewModel.swift:4:1: warning: Duplicate Imports Violation: Imports should be unique. (duplicate_imports)
⚠️ This PR introduced a violation in Realm: /Users/vsts/agent/2.144.0/work/1/s/osscheck/Realm/RealmSwift/Migration.swift:21:1: warning: Duplicate Imports Violation: Imports should be unique. (duplicate_imports)
⚠️ This PR introduced a violation in Realm: /Users/vsts/agent/2.144.0/work/1/s/osscheck/Realm/RealmSwift/List.swift:21:1: warning: Duplicate Imports Violation: Imports should be unique. (duplicate_imports)
⚠️ This PR introduced a violation in Realm: /Users/vsts/agent/2.144.0/work/1/s/osscheck/Realm/RealmSwift/Object.swift:21:1: warning: Duplicate Imports Violation: Imports should be unique. (duplicate_imports)
⚠️ This PR introduced a violation in Realm: /Users/vsts/agent/2.144.0/work/1/s/osscheck/Realm/RealmSwift/RealmConfiguration.swift:21:1: warning: Duplicate Imports Violation: Imports should be unique. (duplicate_imports)
⚠️ This PR introduced a violation in Realm: /Users/vsts/agent/2.144.0/work/1/s/osscheck/Realm/RealmSwift/Realm.swift:21:1: warning: Duplicate Imports Violation: Imports should be unique. (duplicate_imports)
⚠️ This PR introduced a violation in Realm: /Users/vsts/agent/2.144.0/work/1/s/osscheck/Realm/RealmSwift/Sync.swift:20:1: warning: Duplicate Imports Violation: Imports should be unique. (duplicate_imports)
⚠️ This PR introduced a violation in Realm: /Users/vsts/agent/2.144.0/work/1/s/osscheck/Realm/RealmSwift/Tests/MigrationTests.swift:22:1: warning: Duplicate Imports Violation: Imports should be unique. (duplicate_imports)
⚠️ This PR introduced a violation in Realm: /Users/vsts/agent/2.144.0/work/1/s/osscheck/Realm/RealmSwift/Tests/TestCase.swift:21:1: warning: Duplicate Imports Violation: Imports should be unique. (duplicate_imports)
⚠️ This PR introduced a violation in WordPress: /Users/vsts/agent/2.144.0/work/1/s/osscheck/WordPress/WordPress/Classes/ViewRelated/Aztec/ViewControllers/AztecPostViewController.swift:13:1: warning: Duplicate Imports Violation: Imports should be unique. (duplicate_imports)
12 Messages
📖 Linting Aerial with this PR took 1.8s vs 1.72s on master (4% slower)
📖 Linting Alamofire with this PR took 3.53s vs 3.46s on master (2% slower)
📖 Linting Firefox with this PR took 12.1s vs 12.04s on master (0% slower)
📖 Linting Kickstarter with this PR took 19.12s vs 19.39s on master (1% faster)
📖 Linting Moya with this PR took 1.88s vs 1.9s on master (1% faster)
📖 Linting Nimble with this PR took 1.88s vs 1.95s on master (3% faster)
📖 Linting Quick with this PR took 0.55s vs 0.58s on master (5% faster)
📖 Linting Realm with this PR took 3.35s vs 3.35s on master (0% slower)
📖 Linting SourceKitten with this PR took 1.14s vs 1.16s on master (1% faster)
📖 Linting Sourcery with this PR took 4.44s vs 4.51s on master (1% faster)
📖 Linting Swift with this PR took 25.68s vs 27.29s on master (5% faster)
📖 Linting WordPress with this PR took 19.42s vs 19.51s on master (0% faster)

Generated by 🚫 Danger

@ornithocoder
Copy link
Contributor

Hi @sammy-SC. I don't know useful my suggestion would be for the implementation. But an easy way to find dups is using a Set. Inserting into a set - using func insert(_:) - returns a tuple with inserted and newMember. So if it fails trying to insert into the set, it's because the value is already there, therefore a dup violation.

@sammy-SC
Copy link
Contributor Author

Hello @ornithocoder
thank you very much for the suggestion.

I was considering that approach however I couldn't find a way to deal with nested modules.
For example you can have following code

import A.B
import A

and this should throw a warning on first line as that module has already been imported.

Hope it makes sense and explains why I chose to go with comparing lines among themselves with two nested for loops.

@codecov-io
Copy link

codecov-io commented Jan 13, 2018

Codecov Report

Merging #2004 into master will increase coverage by 0.04%.
The diff coverage is 97.47%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #2004      +/-   ##
==========================================
+ Coverage   91.92%   91.97%   +0.04%     
==========================================
  Files         307      309       +2     
  Lines       15333    15452     +119     
==========================================
+ Hits        14095    14212     +117     
- Misses       1238     1240       +2
Impacted Files Coverage Δ
Tests/SwiftLintFrameworkTests/RulesTests.swift 100% <ø> (ø) ⬆️
...tFrameworkTests/AutomaticRuleTests.generated.swift 100% <100%> (ø) ⬆️
...Rules/Idiomatic/DuplicateImportsRuleExamples.swift 100% <100%> (ø)
...amework/Rules/Idiomatic/DuplicateImportsRule.swift 96.2% <96.2%> (ø)
...tFramework/Rules/Lint/ValidIBInspectableRule.swift 100% <0%> (+1.36%) ⬆️

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 269dbef...afd2bbb. Read the comment docs.

@sammy-SC
Copy link
Contributor Author

sammy-SC commented Jan 13, 2018

@ornithocoder
currently I am struggling with

#if DEBUG
    @testable import KsApi
#else
    import KsApi
#endif

I don't know how to reliably detect this.

@jpsim @marcelofabri how would you go about detecting #if DEBUG branch?

Also to me it doesn't look like valid use case of #if DEBUG, should we even care about it?

Thank you

kind: .style,
nonTriggeringExamples: [
"import A\nimport B\nimport C",
"import A.B\nimport import A.C"
Copy link
Contributor

Choose a reason for hiding this comment

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

There's a double import for A.C.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@ornithocoder thank you, I've fixed that.

@ornithocoder
Copy link
Contributor

Hi @sammy-SC. Regarding the #ifdef/else/endif, it doesn't show on the structure

{
  "key.diagnostic_stage" : "source.diagnostic.stage.swift.parse",
  "key.substructure" : [

  ],
  "key.offset" : 0,
  "key.length" : 67
}

But "shows" on the syntax map.

[
  {
    "type" : "source.lang.swift.syntaxtype.buildconfig.keyword",
    "offset" : 0,
    "length" : 3
  },
  {
    "type" : "source.lang.swift.syntaxtype.buildconfig.id",
    "offset" : 4,
    "length" : 5
  },
  {
    "type" : "source.lang.swift.syntaxtype.attribute.builtin",
    "offset" : 14,
    "length" : 9
  },
  {
    "type" : "source.lang.swift.syntaxtype.keyword",
    "offset" : 24,
    "length" : 6
  },
  {
    "type" : "source.lang.swift.syntaxtype.identifier",
    "offset" : 31,
    "length" : 5
  },
  {
    "type" : "source.lang.swift.syntaxtype.buildconfig.keyword",
    "offset" : 37,
    "length" : 5
  },
  {
    "type" : "source.lang.swift.syntaxtype.keyword",
    "offset" : 47,
    "length" : 6
  },
  {
    "type" : "source.lang.swift.syntaxtype.identifier",
    "offset" : 54,
    "length" : 5
  },
  {
    "type" : "source.lang.swift.syntaxtype.buildconfig.keyword",
    "offset" : 60,
    "length" : 6
  }
]

But honestly, I don't know what to do in this situation. Probably ignore duplicates if they're wrapped by flags? Specially if the #if/endif has elifs.

@sammy-SC
Copy link
Contributor Author

Hello @ornithocoder

I've done what you suggested, ignoring imports in conditional compilation directives.

@marcelofabri
Copy link
Collaborator

@sammy-SC sammy-SC force-pushed the new-rule/duplicate-imports branch 2 times, most recently from b1099b7 to d864afa Compare February 15, 2018 17:24
@sammy-SC
Copy link
Contributor Author

@marcelofabri I'm sorry I must have missed that.
I've fixed the issue and made sure all of the warnings are correct.

@marcelofabri
Copy link
Collaborator

Hmm, apparently this rule made linting way slower on oss-check:

12 Messages
📖 Linting Aerial with this PR took 0.73s vs 0.42s on master (73% slower)
📖 Linting Alamofire with this PR took 5.43s vs 3.49s on master (55% slower)
📖 Linting Firefox with this PR took 22.14s vs 14.81s on master (49% slower)
📖 Linting Kickstarter with this PR took 31.66s vs 24.36s on master (29% slower)
📖 Linting Moya with this PR took 2.82s vs 2.49s on master (13% slower)
📖 Linting Nimble with this PR took 2.98s vs 2.52s on master (18% slower)
📖 Linting Quick with this PR took 0.85s vs 0.79s on master (7% slower)
📖 Linting Realm with this PR took 4.95s vs 4.28s on master (15% slower)
📖 Linting SourceKitten with this PR took 1.52s vs 1.34s on master (13% slower)
📖 Linting Sourcery with this PR took 6.7s vs 6.71s on master (0% faster)
📖 Linting Swift with this PR took 19.88s vs 21.21s on master (6% faster)
📖 Linting WordPress with this PR took 17.32s vs 22.63s on master (23% faster)

I'm re-running it just to be sure it wasn't caused by a Circle CI instability.

@sammy-SC
Copy link
Contributor Author

@marcelofabri are the new results sufficient or should I look into improving performance?

@marcelofabri
Copy link
Collaborator

@sammy-SC I think it's fine now. I'll try to review this in the following days.

@sammy-SC
Copy link
Contributor Author

@marcelofabri could you please check this PR when you have time?

I think it would be a useful addition to SwiftLint.

@marcelofabri
Copy link
Collaborator

@sammy-SC Sorry for the delay.

I think we should make sure we're handling all the import forms, including the less-known import kind (https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/doc/uid/TP40014097-CH34-ID354).

Or we can skip that for now and make the rule opt-in.

import Foundation
import SourceKittenFramework

extension Line {
Copy link
Collaborator

Choose a reason for hiding this comment

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

this extension should be private (and I would move it to the end of this file)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

identifier: "duplicate_imports",
name: "Duplicate Imports",
description: "Imports should be unique.",
kind: .style,
Copy link
Collaborator

Choose a reason for hiding this comment

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

.idiomatic?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed.

guard let kind = SyntaxKind(rawValue: token.type) else {
return false
}
return kind == SyntaxKind.buildconfigKeyword
Copy link
Collaborator

@marcelofabri marcelofabri Mar 25, 2018

Choose a reason for hiding this comment

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

you can use just kind == .buildconfigKeyword

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed

@@ -5,6 +5,7 @@
// Created by Scott Hoyt on 12/28/15.
// Copyright © 2015 Realm. All rights reserved.
//

Copy link
Collaborator

Choose a reason for hiding this comment

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

🤔

Did you manually add this line?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm sorry, I removed the line

@sammy-SC sammy-SC force-pushed the new-rule/duplicate-imports branch 2 times, most recently from c7e1c8c to f96068b Compare June 30, 2018 16:32
@sammy-SC
Copy link
Contributor Author

@marcelofabri
I know it has been a while but I managed to change it so all import forms are accounted for.

Please when you have a time could you take another look at this?

Thank you

@sammy-SC
Copy link
Contributor Author

@marcelofabri could you please rerun the speed check?

@sammy-SC sammy-SC force-pushed the new-rule/duplicate-imports branch 2 times, most recently from afd2bbb to 761f75e Compare August 30, 2018 14:43
@sammy-SC
Copy link
Contributor Author

@marcelofabri I resolved merge conflicts.
This rule seems to slow down SwiftLint, do we want to do anything about it? If so do you happen to have any tips/tricks on how to go about it?

@marcelofabri
Copy link
Collaborator

@sammy-SC I ran oss-check locally and it seems it's not too bad:

Message: Linting Aerial with this PR took 0.32s vs 0.3s on master (6% slower)
Message: Linting Alamofire with this PR took 2.23s vs 2.19s on master (1% slower)
Message: Linting Firefox with this PR took 9.42s vs 9.38s on master (0% slower)
Message: Linting Kickstarter with this PR took 13.03s vs 12.43s on master (4% slower)
Message: Linting Moya with this PR took 1.39s vs 1.38s on master (0% slower)
Message: Linting Nimble with this PR took 1.12s vs 1.14s on master (1% faster)
Message: Linting Quick with this PR took 0.36s vs 0.35s on master (2% slower)
Message: Linting Realm with this PR took 2.13s vs 2.13s on master (0% slower)
Message: Linting SourceKitten with this PR took 0.76s vs 0.79s on master (3% faster)
Message: Linting Sourcery with this PR took 3.5s vs 3.51s on master (0% faster)
Message: Linting Swift with this PR took 22.37s vs 21.82s on master (2% slower)
Message: Linting WordPress with this PR took 12.17s vs 11.49s on master (5% slower)

@sammy-SC
Copy link
Contributor Author

@marcelofabri please let me know if there is anything else you would like me to change. Any tips on improving the code are very welcome, even if it was to improve readability / clarify of the solution.

If approved I would be more than happy to add autocorrection.

@jpsim
Copy link
Collaborator

jpsim commented Nov 28, 2018

OSSCheck highlighted a number of false positives. For example, Realm and Realm.Private are different modules, but this is triggering a violation:

import Realm
import Realm.Private

@sammy-SC
Copy link
Contributor Author

sammy-SC commented Jan 9, 2019

@jpsim that's intentional, is that not correct?

The idea was that if you have imports such as

import Foundation
import Foundation.Date

would get highlighted as duplicate import.

Is that not desired?

If not, I'm happy to change it.

@jpsim
Copy link
Collaborator

jpsim commented Jan 17, 2019

Realm.Private and Realm are different modules, and Realm.Private isn't re-exported by Realm, so both imports are necessary.

However, I think this is fairly rare in practice that the false positive may be worth the ability to find duplicate Apple module imports, which as you point out are usually re-exported by parents.

@jpsim jpsim merged commit 90232d5 into realm:master Jan 17, 2019
@jpsim
Copy link
Collaborator

jpsim commented Jan 17, 2019

Thank you for being so patient on this one @sammy-SC! I finally had some time to update this, refactor it a little and came to a decision on keeping the rule more powerful but with a few rare false positives.

@sammy-SC
Copy link
Contributor Author

@jpsim thanks for the help. I will check out the refactoring.

It took a little over a year so not too bad :)

@sammy-SC sammy-SC deleted the new-rule/duplicate-imports branch January 18, 2019 12:09
sjavora pushed a commit to sjavora/SwiftLint that referenced this pull request Mar 9, 2019
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.

6 participants