From da0bfcb566a4a38e1b35c6e3dbaacf4177275067 Mon Sep 17 00:00:00 2001 From: Ivan Date: Sun, 3 Mar 2024 12:21:49 +0400 Subject: [PATCH 1/3] Prevent name clashes between an function in class and a function call in current scope. --- .../com/squareup/kotlinpoet/CodeWriter.kt | 5 +- .../com/squareup/kotlinpoet/MemberNameTest.kt | 81 +++++++++++++++++++ 2 files changed, 85 insertions(+), 1 deletion(-) diff --git a/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/CodeWriter.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/CodeWriter.kt index 4921c59a9..4fdd3b625 100644 --- a/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/CodeWriter.kt +++ b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/CodeWriter.kt @@ -464,11 +464,14 @@ internal class CodeWriter constructor( val simpleName = imports[memberName.canonicalName]?.alias ?: memberName.simpleName // Match an imported member. val importedMember = importedMembers[simpleName] - if (importedMember == memberName) { + val found = importedMember == memberName + if (found && !isMethodNameUsedInCurrentContext(simpleName)) { return simpleName } else if (importedMember != null && memberName.enclosingClassName != null) { val enclosingClassName = lookupName(memberName.enclosingClassName) return "$enclosingClassName.$simpleName" + } else if (found) { + return simpleName } // If the member is in the same package, we're done. diff --git a/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/MemberNameTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/MemberNameTest.kt index 83fb870da..b7fa5b873 100644 --- a/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/MemberNameTest.kt +++ b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/MemberNameTest.kt @@ -322,6 +322,87 @@ class MemberNameTest { ) } + @Test fun importedMemberClassFunctionNameDontClashForParameterValue() { + fun createSimpleParameterCodeBlock(): CodeBlock { + val member = ClassName( + packageName = "com.squareup", + simpleNames = listOf("Fridge"), + ).member("meat") + val block = CodeBlock.builder() + .add("%M", member) + .add(" { }") + .build() + return CodeBlock + .builder() + .add("%L", block) + .build() + } + + fun createFuncParameter(name: String) = + CodeBlock + .builder() + .add(name).add(" = ") + .add(createSimpleParameterCodeBlock()) + .build() + + fun createFuncParameters(names: List): List = + names.map { name -> createFuncParameter(name) } + + fun createBuildFunc(params: List): FunSpec { + val tacoClassname = ClassName.bestGuess("com.squareup.tacos.Taco") + val paramsCodeBlock = CodeBlock.builder() + .add("return %T", tacoClassname) + .apply { + add( + params.let(::createFuncParameters) + .map { block -> CodeBlock.of("%L", block) } + .joinToCode(prefix = "(", suffix = ")"), + ) + } + .build() + return FunSpec.builder("build") + .returns(tacoClassname) + .addCode(paramsCodeBlock).build() + } + + val spec = FileSpec.builder("com.squareup.tacos", "Tacos") + .addType( + TypeSpec.classBuilder("DeliciousTaco") + .addFunction(createBuildFunc(listOf("deliciousMeat"))) + .addFunction(FunSpec.builder("deliciousMeat").build()) + .build(), + ) + .addType( + TypeSpec.classBuilder("TastelessTaco") + .addFunction(createBuildFunc(listOf("meat"))) + .addFunction(FunSpec.builder("meat").build()) + .build(), + ) + val source = spec.build() + assertThat(source.toString()).isEqualTo( + """ + |package com.squareup.tacos + | + |import com.squareup.Fridge.meat + | + |public class DeliciousTaco { + | public fun build(): Taco = Taco(deliciousMeat = meat { }) + | + | public fun deliciousMeat() { + | } + |} + | + |public class TastelessTaco { + | public fun build(): Taco = Taco(meat = com.squareup.Fridge.meat { }) + | + | public fun meat() { + | } + |} + | + """.trimMargin(), + ) + } + @Test fun memberNameAliases() { val createSquareTaco = MemberName("com.squareup.tacos", "createTaco") val createTwitterTaco = MemberName("com.twitter.tacos", "createTaco") From 103b00075245836ad7ab9d91cc7b9904bed7252b Mon Sep 17 00:00:00 2001 From: Ivan Date: Sun, 3 Mar 2024 12:30:32 +0400 Subject: [PATCH 2/3] update changelog; --- docs/changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.md b/docs/changelog.md index 785d5fac4..f6917c419 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -3,6 +3,7 @@ Change Log ## Unreleased +* 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). From 4135a77d96409d05c5b96bf804636dfc78960e63 Mon Sep 17 00:00:00 2001 From: Ivan Date: Mon, 4 Mar 2024 17:57:20 +0400 Subject: [PATCH 3/3] Improves after review; --- .../com/squareup/kotlinpoet/MemberNameTest.kt | 45 ++++++------------- 1 file changed, 13 insertions(+), 32 deletions(-) diff --git a/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/MemberNameTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/MemberNameTest.kt index b7fa5b873..4ff7122e5 100644 --- a/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/MemberNameTest.kt +++ b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/MemberNameTest.kt @@ -323,46 +323,27 @@ class MemberNameTest { } @Test fun importedMemberClassFunctionNameDontClashForParameterValue() { - fun createSimpleParameterCodeBlock(): CodeBlock { - val member = ClassName( - packageName = "com.squareup", - simpleNames = listOf("Fridge"), - ).member("meat") - val block = CodeBlock.builder() - .add("%M", member) - .add(" { }") - .build() - return CodeBlock - .builder() - .add("%L", block) - .build() - } - - fun createFuncParameter(name: String) = - CodeBlock - .builder() - .add(name).add(" = ") - .add(createSimpleParameterCodeBlock()) - .build() - - fun createFuncParameters(names: List): List = - names.map { name -> createFuncParameter(name) } - fun createBuildFunc(params: List): FunSpec { val tacoClassname = ClassName.bestGuess("com.squareup.tacos.Taco") - val paramsCodeBlock = CodeBlock.builder() + val bodyCodeBlock = CodeBlock.builder() .add("return %T", tacoClassname) .apply { - add( - params.let(::createFuncParameters) - .map { block -> CodeBlock.of("%L", block) } - .joinToCode(prefix = "(", suffix = ")"), - ) + val paramsBlock = params.map { paramName -> + val paramValue = ClassName( + packageName = "com.squareup", + simpleNames = listOf("Fridge"), + ).member("meat") + CodeBlock.of("$paramName = %L", CodeBlock.of("%M { }", paramValue)) + } + .map { block -> CodeBlock.of("%L", block) } + .joinToCode(prefix = "(", suffix = ")") + add(paramsBlock) } .build() return FunSpec.builder("build") .returns(tacoClassname) - .addCode(paramsCodeBlock).build() + .addCode(bodyCodeBlock) + .build() } val spec = FileSpec.builder("com.squareup.tacos", "Tacos")