Skip to content

Commit 140b45e

Browse files
author
Paul Dingemans
committed
Imports for the same class should not be removed in case different aliases are used
Closes pinterest#1243
1 parent 3b27bf4 commit 140b45e

File tree

4 files changed

+87
-15
lines changed

4 files changed

+87
-15
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
99

1010
### Fixed
1111
- Fix false positive in rule spacing-between-declarations-with-annotations ([#1281](https://github.com/pinterest/ktlint/issues/1281))
12+
- Do not remove imports for same class when different alias is used ([#1243](https://github.com/pinterest/ktlint/issues/1243))
1213

1314
### Changed
1415
- Update Kotlin version to `1.6.0` release

ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/ImportOrderingRule.kt

+32-12
Original file line numberDiff line numberDiff line change
@@ -169,13 +169,28 @@ public class ImportOrderingRule :
169169
it.elementType == ElementType.IMPORT_DIRECTIVE ||
170170
it.psi is PsiWhiteSpace && it.textLength > 1 // also collect empty lines, that are represented as "\n\n"
171171
}
172+
173+
val autoCorrectDuplicateImports = imports
174+
.filter { it.psi !is PsiWhiteSpace }
175+
.groupingBy { it.text }
176+
.eachCount()
177+
.filterValues { it > 1 }
178+
.keys
179+
.map {
180+
emit(
181+
node.startOffset,
182+
"Duplicate '$it' found",
183+
true
184+
)
185+
}.any()
186+
val deduplicatedImports = imports.distinctBy { it.text } // distinguish by import path including alias
187+
172188
val hasComments = children.find { it.elementType == ElementType.BLOCK_COMMENT || it.elementType == ElementType.EOL_COMMENT } != null
173-
val sortedImports = imports
189+
val sortedImports = deduplicatedImports
174190
.asSequence()
175191
.filter { it.psi !is PsiWhiteSpace } // sorter expects KtImportDirective, whitespaces are inserted afterwards
176192
.map { it.psi as KtImportDirective }
177193
.sortedWith(importSorter)
178-
.distinctBy { if (it.aliasName != null) it.text.substringBeforeLast(it.aliasName!!) else it.text } // distinguish by import path w/o aliases
179194
.map { it.node } // transform back to ASTNode in order to operate over its method (addChild)
180195

181196
// insert blank lines wherever needed
@@ -201,19 +216,24 @@ public class ImportOrderingRule :
201216
return@fold current
202217
}
203218

204-
val canAutoCorrect = !hasComments
205-
if (!importsAreEqual(imports, sortedImportsWithSpaces) || (hasTooMuchWhitespace(children) && !isCustomLayout())) {
206-
val additionalMessage = if (!canAutoCorrect) {
207-
" -- no autocorrection due to comments in the import list"
208-
} else {
209-
""
210-
}
219+
if (hasComments) {
211220
emit(
212221
node.startOffset,
213-
"${errorMessages.getOrDefault(importsLayout, CUSTOM_ERROR_MESSAGE)}$additionalMessage",
214-
canAutoCorrect
222+
errorMessages.getOrDefault(importsLayout, CUSTOM_ERROR_MESSAGE) +
223+
" -- no autocorrection due to comments in the import list",
224+
false
215225
)
216-
if (autoCorrect && canAutoCorrect) {
226+
} else {
227+
val autoCorrectWhitespace = hasTooMuchWhitespace(children) && !isCustomLayout()
228+
val autoCorrectSortOrder = !importsAreEqual(deduplicatedImports, sortedImportsWithSpaces)
229+
if (autoCorrectSortOrder || autoCorrectWhitespace) {
230+
emit(
231+
node.startOffset,
232+
errorMessages.getOrDefault(importsLayout, CUSTOM_ERROR_MESSAGE),
233+
true
234+
)
235+
}
236+
if (autoCorrect && (autoCorrectDuplicateImports || autoCorrectSortOrder || autoCorrectWhitespace)) {
217237
node.removeRange(node.firstChildNode, node.lastChildNode.treeNext)
218238
sortedImportsWithSpaces.reduce { current, next ->
219239
node.addChild(current, null)

ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/NoUnusedImportsRuleTest.kt

+14
Original file line numberDiff line numberDiff line change
@@ -860,4 +860,18 @@ class NoUnusedImportsRuleTest {
860860
)
861861
)
862862
}
863+
864+
@Test
865+
fun `Issue 1243 - Format should not remove import`() {
866+
val code =
867+
"""
868+
import foo.Bar as Bar1
869+
import foo.Bar as Bar2
870+
871+
val bar1 = Bar1()
872+
val bar2 = Bar2()
873+
""".trimIndent()
874+
assertThat(NoUnusedImportsRule().lint(code)).isEmpty()
875+
assertThat(NoUnusedImportsRule().format(code)).isEqualTo(code)
876+
}
863877
}

ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/importordering/ImportOrderingRuleIdeaTest.kt

+40-3
Original file line numberDiff line numberDiff line change
@@ -125,28 +125,33 @@ class ImportOrderingRuleIdeaTest {
125125
}
126126

127127
@Test
128-
fun testFormatDuplicate() {
128+
fun `remove duplicate class imports unless they have distinct aliases`() {
129129
val imports =
130130
"""
131-
import android.view.ViewGroup
132131
import android.view.View
133132
import android.view.ViewGroup
133+
import android.view.View
134134
import android.content.Context as Ctx1
135135
import android.content.Context as Ctx2
136+
import android.content.Context as Ctx1
136137
""".trimIndent()
137138

138139
val formattedImports =
139140
"""
140141
import android.view.View
141142
import android.view.ViewGroup
142143
import android.content.Context as Ctx1
144+
import android.content.Context as Ctx2
143145
""".trimIndent()
144146

145147
val testFile = writeIdeaImportsOrderingConfig()
146148

147149
assertThat(
148150
rule.lint(testFile, imports)
149-
).isEqualTo(expectedErrors)
151+
).containsExactly(
152+
LintError(1, 1, "import-ordering", "Duplicate 'import android.view.View' found"),
153+
LintError(1, 1, "import-ordering", "Duplicate 'import android.content.Context as Ctx1' found")
154+
)
150155
assertThat(
151156
rule.format(testFile, imports)
152157
).isEqualTo(formattedImports)
@@ -350,6 +355,38 @@ class ImportOrderingRuleIdeaTest {
350355
).isEqualTo(formattedImports)
351356
}
352357

358+
@Test
359+
fun `Issue 1243 - Format should not remove imports when having a distinct alias`() {
360+
val imports =
361+
"""
362+
import foo.Bar as Bar1
363+
import foo.Bar as Bar2
364+
import foo.Bar as Bar2
365+
366+
val bar1 = Bar1()
367+
val bar2 = Bar2()
368+
""".trimIndent()
369+
val formattedImports =
370+
"""
371+
import foo.Bar as Bar1
372+
import foo.Bar as Bar2
373+
374+
val bar1 = Bar1()
375+
val bar2 = Bar2()
376+
""".trimIndent()
377+
378+
val testFile = writeIdeaImportsOrderingConfig()
379+
380+
assertThat(
381+
rule.lint(testFile, imports)
382+
).containsExactly(
383+
LintError(1, 1, "import-ordering", "Duplicate 'import foo.Bar as Bar2' found")
384+
)
385+
assertThat(
386+
rule.format(testFile, imports)
387+
).isEqualTo(formattedImports)
388+
}
389+
353390
private fun writeIdeaImportsOrderingConfig() = editorConfigTestRule
354391
.writeToEditorConfig(
355392
mapOf(

0 commit comments

Comments
 (0)