diff options
-rw-r--r-- | .github/workflows/mkdocs-requirements.txt | 2 | ||||
-rw-r--r-- | docs/changelog.md | 34 | ||||
-rw-r--r-- | gradle.properties | 2 | ||||
-rw-r--r-- | kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/CodeWriter.kt | 53 | ||||
-rw-r--r-- | kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/MemberNameTest.kt | 25 |
5 files changed, 83 insertions, 33 deletions
diff --git a/.github/workflows/mkdocs-requirements.txt b/.github/workflows/mkdocs-requirements.txt index 924ec277..0a47aae7 100644 --- a/.github/workflows/mkdocs-requirements.txt +++ b/.github/workflows/mkdocs-requirements.txt @@ -6,7 +6,7 @@ lunr==0.7.0.post1 MarkupSafe==2.1.5 mkdocs==1.6.0 mkdocs-macros-plugin==1.0.5 -mkdocs-material==9.5.21 +mkdocs-material==9.5.25 mkdocs-material-extensions==1.3.1 Pygments==2.18.0 pymdown-extensions==10.8.1 diff --git a/docs/changelog.md b/docs/changelog.md index 2ba3014b..668118ad 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -3,23 +3,37 @@ Change Log ## Unreleased +## Version 1.17.0 + +Thanks to [@jisungbin][jisungbin], [@hfhbd][hfhbd], [@evant][evant], [@sgjesse][sgjesse], [@sebek64][sebek64] for +contributing to this release. + +_2024-05-24_ + +* Change: kotlinx-metadata 0.9.0. Note that the `KotlinClassMetadata.read` is deprecated in 0.9.0 and replaced with + `readStrict` (#1830). + * Note: we now also provide `lenient` parameters to map to the underlying `readStrict()` and `readLenient()` calls + (#1766). + * We have also removed various `Class`/`TypeElement`/`Metadata`-to-`KmClass` APIs from the public API, as these are + trivial to write now with kotlinx-metadata's newer APIs and allows us to focus the API surface area of this artifact + better (#1891). * New: Supertype list wraps to one-per-line if the primary constructor spans multiple lines (#1866). -* New: Extract `MemberSpecHolder` interface for constructs that can hold `PropertySpec`s and `FunSpec`s and their builders (#1877). -* New: `joinToCode` variant which operates on any type, but requires a transform lambda to convert each element into a `CodeBlock`. +* New: Extract `MemberSpecHolder` interface for constructs that can hold `PropertySpec`s and `FunSpec`s and their + builders (#1877). +* New: `joinToCode` variant which operates on any type, but requires a transform lambda to convert each element into a + `CodeBlock` (#1874). * New: Support annotation type arguments in `KSAnnotation.toAnnotationSpec()` (#1889). * Fix: Prevent name clashes between a function in class and a function call in current scope (#1850). * Fix: Fix extension function imports (#1814). -* Fix: Omit implicit modifiers on FileSpec.scriptBuilder (#1813). -* Fix: Fix trailing newline in PropertySpec (#1827). -* Change: kotlinx-metadata 0.9.0. Note that the `KotlinClassMetadata .read` is deprecated in 0.9.0 and replaced with `readStrict` (#1830). - * Note: we now also `lenient` parameters to map to the underlying `readStrict()` and `readLenient()` calls (#1766). - * We have also removed various `Class`/`TypeElement`/`Metadata`-to-`KmClass` APIs from the public API, as these are trivial to write now with kotlinx-metadata's newer APIs and allows us to focus the API surface area of this artifact better (#1891). +* Fix: Omit implicit modifiers on `FileSpec.scriptBuilder` (#1813). +* Fix: Fix trailing newline in `PropertySpec` (#1827). * Fix: `KSAnnotation.toAnnotationSpec` writes varargs in place instead of making them an array to work around a Kotlin - issue with `OptIn` annotations (#1831). + issue with `OptIn` annotations (#1833). * Fix: `MemberName`s without a package are now correctly imported (#1841) * Fix: Throw if primary constructor delegates to other constructors (#1859). * Fix: Aliased imports with nested class (#1876). * Fix: Check for error types in `KSType.toClassName()` (#1890). +* Fix: Support generating a single import for overloaded `MemberName`s (#1909). ## Version 1.16.0 @@ -803,3 +817,7 @@ _2017-05-16_ [takahirom]: https://github.com/takahirom [mcarleio]: https://github.com/mcarleio [gabrielittner]: https://github.com/gabrielittner + [jisungbin]: https://github.com/jisungbin + [hfhbd]: https://github.com/hfhbd + [sgjesse]: https://github.com/sgjesse + [sebek64]: https://github.com/sebek64 diff --git a/gradle.properties b/gradle.properties index ecde9401..d3559edb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ org.gradle.jvmargs='-Dfile.encoding=UTF-8' GROUP=com.squareup -VERSION_NAME=1.17.0-SNAPSHOT +VERSION_NAME=1.18.0-SNAPSHOT POM_URL=https://github.com/square/kotlinpoet POM_SCM_URL=https://github.com/square/kotlinpoet diff --git a/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/CodeWriter.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/CodeWriter.kt index 4bdd102c..8f959c70 100644 --- a/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/CodeWriter.kt +++ b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/CodeWriter.kt @@ -59,7 +59,7 @@ internal class CodeWriter( private val indent: String = DEFAULT_INDENT, imports: Map<String, Import> = emptyMap(), private val importedTypes: Map<String, ClassName> = emptyMap(), - private val importedMembers: Map<String, MemberName> = emptyMap(), + private val importedMembers: Map<String, Set<MemberName>> = emptyMap(), columnLimit: Int = 100, ) : Closeable { private var out = LineWrapper(out, indent, columnLimit) @@ -463,11 +463,11 @@ internal class CodeWriter( fun lookupName(memberName: MemberName): String { val simpleName = imports[memberName.canonicalName]?.alias ?: memberName.simpleName // Match an imported member. - val importedMember = importedMembers[simpleName] - val found = importedMember == memberName + val importedMembers = importedMembers[simpleName] ?: emptySet() + val found = memberName in importedMembers if (found && !isMethodNameUsedInCurrentContext(simpleName)) { return simpleName - } else if (importedMember != null && memberName.enclosingClassName != null) { + } else if (importedMembers.isNotEmpty() && memberName.enclosingClassName != null) { val enclosingClassName = lookupName(memberName.enclosingClassName) return "$enclosingClassName.$simpleName" } else if (found) { @@ -717,14 +717,14 @@ internal class CodeWriter( ) emitStep(importsCollector) val generatedImports = mutableMapOf<String, Import>() - val suggestedTypeImports = importsCollector.suggestedTypeImports() + val importedTypes = importsCollector.suggestedTypeImports() .generateImports( generatedImports, computeCanonicalName = ClassName::canonicalName, capitalizeAliases = true, referencedNames = importsCollector.referencedNames, ) - val suggestedMemberImports = importsCollector.suggestedMemberImports() + val importedMembers = importsCollector.suggestedMemberImports() .generateImports( generatedImports, computeCanonicalName = MemberName::canonicalName, @@ -737,8 +737,8 @@ internal class CodeWriter( out = out, indent = indent, imports = memberImports + generatedImports.filterKeys { it !in memberImports }, - importedTypes = suggestedTypeImports, - importedMembers = suggestedMemberImports, + importedTypes = importedTypes.mapValues { it.value.single() }, + importedMembers = importedMembers, ) } @@ -747,31 +747,38 @@ internal class CodeWriter( computeCanonicalName: T.() -> String, capitalizeAliases: Boolean, referencedNames: Set<String>, - ): Map<String, T> { - return flatMap { (simpleName, qualifiedNames) -> - if (qualifiedNames.size == 1 && simpleName !in referencedNames) { - listOf(simpleName to qualifiedNames.first()).also { - val canonicalName = qualifiedNames.first().computeCanonicalName() - generatedImports[canonicalName] = Import(canonicalName) - } + ): Map<String, Set<T>> { + val imported = mutableMapOf<String, Set<T>>() + forEach { (simpleName, qualifiedNames) -> + val canonicalNamesToQualifiedNames = qualifiedNames.associateBy { it.computeCanonicalName() } + if (canonicalNamesToQualifiedNames.size == 1 && simpleName !in referencedNames) { + val canonicalName = canonicalNamesToQualifiedNames.keys.single() + generatedImports[canonicalName] = Import(canonicalName) + + // For types, qualifiedNames should consist of a single name, for which an import will be generated. For + // members, there can be more than one qualified name mapping to a single simple name, e.g. overloaded + // functions declared in the same package. In these cases, a single import will suffice for all of them. + imported[simpleName] = qualifiedNames } else { - generateImportAliases(simpleName, qualifiedNames, computeCanonicalName, capitalizeAliases) + generateImportAliases(simpleName, canonicalNamesToQualifiedNames, capitalizeAliases) .onEach { (alias, qualifiedName) -> val canonicalName = qualifiedName.computeCanonicalName() generatedImports[canonicalName] = Import(canonicalName, alias) + + imported[alias] = setOf(qualifiedName) } } - }.toMap() + } + return imported } private fun <T> generateImportAliases( simpleName: String, - qualifiedNames: Set<T>, - canonicalName: T.() -> String, + canonicalNamesToQualifiedNames: Map<String, T>, capitalizeAliases: Boolean, ): List<Pair<String, T>> { - val canonicalNameSegments = qualifiedNames.associateWith { qualifiedName -> - qualifiedName.canonicalName().split('.') + val canonicalNameSegmentsToQualifiedNames = canonicalNamesToQualifiedNames.mapKeys { (canonicalName, _) -> + canonicalName.split('.') .dropLast(1) // Last segment of the canonical name is the simple name, drop it to avoid repetition. .filter { it != "Companion" } .map { it.replaceFirstChar(Char::uppercaseChar) } @@ -779,10 +786,10 @@ internal class CodeWriter( val aliasNames = mutableMapOf<String, T>() var segmentsToUse = 0 // Iterate until we have unique aliases for all names. - while (aliasNames.size != qualifiedNames.size) { + while (aliasNames.size != canonicalNamesToQualifiedNames.size) { segmentsToUse += 1 aliasNames.clear() - for ((qualifiedName, segments) in canonicalNameSegments) { + for ((segments, qualifiedName) in canonicalNameSegmentsToQualifiedNames) { val aliasPrefix = segments.takeLast(min(segmentsToUse, segments.size)) .joinToString(separator = "") .replaceFirstChar { if (!capitalizeAliases) it.lowercaseChar() else it } diff --git a/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/MemberNameTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/MemberNameTest.kt index b680aaac..04eaa536 100644 --- a/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/MemberNameTest.kt +++ b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/MemberNameTest.kt @@ -657,4 +657,29 @@ class MemberNameTest { """.trimIndent(), ) } + + // https://github.com/square/kotlinpoet/issues/1907 + @Test fun `extension and non-extension MemberName clash`() { + val file = FileSpec.builder("com.squareup.tacos", "Tacos") + .addFunction( + FunSpec.builder("main") + .addStatement("println(%M(Taco()))", MemberName("com.squareup.wrappers", "wrap")) + .addStatement("println(Taco().%M())", MemberName("com.squareup.wrappers", "wrap", isExtension = true)) + .build(), + ) + .build() + assertThat(file.toString()).isEqualTo( + """ + package com.squareup.tacos + + import com.squareup.wrappers.wrap + + public fun main() { + println(wrap(Taco())) + println(Taco().wrap()) + } + + """.trimIndent(), + ) + } } |