Skip to content

Commit cfe427b

Browse files
scosenzaSteve Cosenza
andauthored
Fix missing jdeps by consistently calling collectTypeReferences (#1045)
--------- Co-authored-by: Steve Cosenza <[email protected]>
1 parent 0229db6 commit cfe427b

File tree

3 files changed

+213
-36
lines changed

3 files changed

+213
-36
lines changed

src/main/kotlin/io/bazel/kotlin/plugin/jdeps/JdepsGenExtension.kt

Lines changed: 34 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,9 @@ class JdepsGenExtension(
143143
)?.let { explicitClassesCanonicalPaths.add(it) }
144144
}
145145
is FunctionDescriptor -> {
146-
resultingDescriptor.returnType?.let { addImplicitDep(it) }
146+
resultingDescriptor.returnType?.let {
147+
collectTypeReferences(it, isExplicit = false, collectTypeArguments = false)
148+
}
147149
resultingDescriptor.valueParameters.forEach { valueParameter ->
148150
collectTypeReferences(valueParameter.type, isExplicit = false)
149151
}
@@ -173,7 +175,7 @@ class JdepsGenExtension(
173175
explicitClassesCanonicalPaths.add(virtualFileClass.file.path)
174176
}
175177
}
176-
addImplicitDep(resultingDescriptor.type)
178+
collectTypeReferences(resultingDescriptor.type, isExplicit = false)
177179
}
178180
else -> return
179181
}
@@ -217,14 +219,6 @@ class JdepsGenExtension(
217219
}
218220
}
219221

220-
private fun addImplicitDep(it: KotlinType) {
221-
getClassCanonicalPath(it.constructor)?.let { implicitClassesCanonicalPaths.add(it) }
222-
}
223-
224-
private fun addExplicitDep(it: KotlinType) {
225-
getClassCanonicalPath(it.constructor)?.let { explicitClassesCanonicalPaths.add(it) }
226-
}
227-
228222
/**
229223
* Records direct and indirect references for a given type. Direct references are explicitly
230224
* used in the code, e.g: a type declaration or a generic type declaration. Indirect references
@@ -234,35 +228,41 @@ class JdepsGenExtension(
234228
private fun collectTypeReferences(
235229
kotlinType: KotlinType,
236230
isExplicit: Boolean = true,
231+
collectTypeArguments: Boolean = true,
232+
visitedKotlinTypes: MutableSet<Pair<KotlinType, Boolean>> = mutableSetOf(),
237233
) {
238-
if (isExplicit) {
239-
addExplicitDep(kotlinType)
240-
} else {
241-
addImplicitDep(kotlinType)
242-
}
243-
244-
kotlinType.supertypes().forEach {
245-
addImplicitDep(it)
246-
}
247-
248-
collectTypeArguments(kotlinType, isExplicit)
249-
}
234+
val kotlintTypeAndIsExplicit = Pair(kotlinType, isExplicit)
235+
if (!visitedKotlinTypes.contains(kotlintTypeAndIsExplicit)) {
236+
visitedKotlinTypes.add(kotlintTypeAndIsExplicit)
250237

251-
private fun collectTypeArguments(
252-
kotlinType: KotlinType,
253-
isExplicit: Boolean,
254-
visitedKotlinTypes: MutableSet<KotlinType> = mutableSetOf(),
255-
) {
256-
visitedKotlinTypes.add(kotlinType)
257-
kotlinType.arguments.map { it.type }.forEach { typeArgument ->
258238
if (isExplicit) {
259-
addExplicitDep(typeArgument)
239+
getClassCanonicalPath(kotlinType.constructor)?.let {
240+
explicitClassesCanonicalPaths.add(it)
241+
}
260242
} else {
261-
addImplicitDep(typeArgument)
243+
getClassCanonicalPath(kotlinType.constructor)?.let {
244+
implicitClassesCanonicalPaths.add(it)
245+
}
246+
}
247+
248+
kotlinType.supertypes().forEach { supertype ->
249+
collectTypeReferences(
250+
supertype,
251+
isExplicit = false,
252+
collectTypeArguments = collectTypeArguments,
253+
visitedKotlinTypes,
254+
)
262255
}
263-
typeArgument.supertypes().forEach { addImplicitDep(it) }
264-
if (!visitedKotlinTypes.contains(typeArgument)) {
265-
collectTypeArguments(typeArgument, isExplicit, visitedKotlinTypes)
256+
257+
if (collectTypeArguments) {
258+
kotlinType.arguments.map { it.type }.forEach { typeArgument ->
259+
collectTypeReferences(
260+
typeArgument,
261+
isExplicit = isExplicit,
262+
collectTypeArguments = true,
263+
visitedKotlinTypes = visitedKotlinTypes,
264+
)
265+
}
266266
}
267267
}
268268
}

src/test/kotlin/io/bazel/kotlin/builder/tasks/jvm/KotlinBuilderJvmJdepsTest.kt

Lines changed: 172 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import com.google.common.truth.Truth.assertThat
2020
import com.google.devtools.build.lib.view.proto.Deps
2121
import io.bazel.kotlin.builder.Deps.*
2222
import io.bazel.kotlin.builder.KotlinJvmTestBuilder
23-
import io.bazel.kotlin.builder.utils.BazelRunFiles
2423
import org.junit.Test
2524
import org.junit.runner.RunWith
2625
import org.junit.runners.Parameterized
@@ -391,7 +390,7 @@ class KotlinBuilderJvmJdepsTest(private val enableK2Compiler: Boolean) {
391390
}
392391

393392
@Test
394-
fun `kotlin indirect property reference on object`() {
393+
fun `kotlin indirect property reference on object calling helloWorld function`() {
395394
val transitivePropertyTarget = runCompileTask { c: KotlinJvmTestBuilder.TaskBuilder ->
396395
c.addSource(
397396
"Bar.kt",
@@ -444,6 +443,62 @@ class KotlinBuilderJvmJdepsTest(private val enableK2Compiler: Boolean) {
444443
assertIncomplete(jdeps).isEmpty()
445444
}
446445

446+
@Test
447+
fun `kotlin indirect property reference on object without calling helloWorld function`() {
448+
val transitivePropertyTarget = runCompileTask { c: KotlinJvmTestBuilder.TaskBuilder ->
449+
c.addSource(
450+
"Bar.kt",
451+
"""
452+
package something
453+
454+
class Bar {
455+
fun helloWorld() {}
456+
}
457+
"""
458+
)
459+
}
460+
461+
val dependentTarget = runCompileTask { c: KotlinJvmTestBuilder.TaskBuilder ->
462+
c.addSource(
463+
"Foo.kt",
464+
"""
465+
package something
466+
467+
class Foo {
468+
val bar = Bar()
469+
}
470+
"""
471+
)
472+
c.addDirectDependencies(transitivePropertyTarget)
473+
}
474+
475+
val dependingTarget = runJdepsCompileTask { c: KotlinJvmTestBuilder.TaskBuilder ->
476+
c.addSource(
477+
"HasPropertyDependency.kt",
478+
"""
479+
package something
480+
481+
class HasPropertyDependency {
482+
fun something(): Any {
483+
val foo = Foo()
484+
return foo.bar
485+
}
486+
}
487+
"""
488+
)
489+
c.addDirectDependencies(dependentTarget)
490+
c.addTransitiveDependencies(transitivePropertyTarget)
491+
}
492+
val jdeps = depsProto(dependingTarget)
493+
494+
assertThat(jdeps.ruleLabel).isEqualTo(dependingTarget.label())
495+
assertThat(jdeps.dependencyCount).isEqualTo(2)
496+
assertExplicit(jdeps).contains(dependentTarget.singleCompileJar())
497+
assertImplicit(jdeps).contains(transitivePropertyTarget.singleCompileJar())
498+
assertUnused(jdeps).isEmpty()
499+
assertIncomplete(jdeps).isEmpty()
500+
}
501+
447502
@Test
448503
fun `kotlin extension property reference`() {
449504
val dependentTarget = runCompileTask { c: KotlinJvmTestBuilder.TaskBuilder ->
@@ -987,6 +1042,120 @@ class KotlinBuilderJvmJdepsTest(private val enableK2Compiler: Boolean) {
9871042
assertIncomplete(jdeps).isEmpty()
9881043
}
9891044

1045+
@Test
1046+
fun `function call on a class should collect the indirect super class of that class as an implicit dependency`() {
1047+
val implicitSuperClassDep = runCompileTask { c: KotlinJvmTestBuilder.TaskBuilder ->
1048+
c.addSource(
1049+
"Base.kt",
1050+
"""
1051+
package something
1052+
1053+
open class Base
1054+
"""
1055+
)
1056+
}
1057+
1058+
val explicitSuperClassDep = runCompileTask { c: KotlinJvmTestBuilder.TaskBuilder ->
1059+
c.addSource(
1060+
"Derived.kt",
1061+
"""
1062+
package something
1063+
1064+
class Derived : Base() {
1065+
fun hi(): String {
1066+
return "Hello"
1067+
}
1068+
}
1069+
"""
1070+
)
1071+
c.addDirectDependencies(implicitSuperClassDep)
1072+
}
1073+
1074+
val dependingTarget = runJdepsCompileTask { c: KotlinJvmTestBuilder.TaskBuilder ->
1075+
c.addSource(
1076+
"ReferencesClassWithSuperClass.kt",
1077+
"""
1078+
package something
1079+
1080+
class ReferencesClassWithSuperClass {
1081+
fun stuff(): String {
1082+
return Derived().hi()
1083+
}
1084+
}
1085+
"""
1086+
)
1087+
c.addDirectDependencies(explicitSuperClassDep)
1088+
c.addTransitiveDependencies(implicitSuperClassDep)
1089+
}
1090+
val jdeps = depsProto(dependingTarget)
1091+
1092+
assertThat(jdeps.ruleLabel).isEqualTo(dependingTarget.label())
1093+
1094+
assertExplicit(jdeps).containsExactly(explicitSuperClassDep.singleCompileJar())
1095+
assertImplicit(jdeps).containsExactly(implicitSuperClassDep.singleCompileJar())
1096+
assertUnused(jdeps).isEmpty()
1097+
assertIncomplete(jdeps).isEmpty()
1098+
}
1099+
1100+
@Test
1101+
fun `creating a kotlin class should collect the indirect java super class, with a kotlin type param class, of that class as an implicit dependency`() {
1102+
val implicitSuperClassGenericTypeParamDep = runCompileTask { c: KotlinJvmTestBuilder.TaskBuilder ->
1103+
c.addSource(
1104+
"BaseGenericType.kt",
1105+
"""
1106+
package something
1107+
1108+
class BaseGenericType
1109+
"""
1110+
)
1111+
}
1112+
1113+
val explicitClassWithTypeParamJavaSuperclassDep = runCompileTask { c: KotlinJvmTestBuilder.TaskBuilder ->
1114+
c.addSource(
1115+
"Derived.kt",
1116+
"""
1117+
package something
1118+
import something.JavaBaseWithTypeParam
1119+
1120+
class Derived : JavaBaseWithTypeParam<BaseGenericType>() {
1121+
fun hi(): String {
1122+
return "Hello"
1123+
}
1124+
}
1125+
"""
1126+
)
1127+
c.addDirectDependencies(TEST_FIXTURES_DEP)
1128+
c.addDirectDependencies(implicitSuperClassGenericTypeParamDep)
1129+
}
1130+
1131+
val dependingTarget = runJdepsCompileTask { c: KotlinJvmTestBuilder.TaskBuilder ->
1132+
c.addSource(
1133+
"ReferencesClassWithSuperClass.kt",
1134+
"""
1135+
package something
1136+
1137+
class ReferencesClassWithSuperClass {
1138+
fun stuff(): String {
1139+
val derived = Derived()
1140+
return derived.toString()
1141+
}
1142+
}
1143+
"""
1144+
)
1145+
c.addDirectDependencies(explicitClassWithTypeParamJavaSuperclassDep)
1146+
c.addTransitiveDependencies(TEST_FIXTURES_DEP)
1147+
c.addTransitiveDependencies(implicitSuperClassGenericTypeParamDep)
1148+
}
1149+
val jdeps = depsProto(dependingTarget)
1150+
1151+
assertThat(jdeps.ruleLabel).isEqualTo(dependingTarget.label())
1152+
assertExplicit(jdeps).containsExactly(explicitClassWithTypeParamJavaSuperclassDep.singleCompileJar())
1153+
assertImplicit(jdeps).contains(TEST_FIXTURES_DEP.singleCompileJar())
1154+
assertImplicit(jdeps).contains(implicitSuperClassGenericTypeParamDep.singleCompileJar())
1155+
assertUnused(jdeps).isEmpty()
1156+
assertIncomplete(jdeps).isEmpty()
1157+
}
1158+
9901159
@Test
9911160
fun `class declaration all super class references should be an implicit dependency`() {
9921161
val implicitSuperClassDep = runCompileTask { c: KotlinJvmTestBuilder.TaskBuilder ->
@@ -1207,6 +1376,7 @@ class KotlinBuilderJvmJdepsTest(private val enableK2Compiler: Boolean) {
12071376
)
12081377
c.addDirectDependencies(depWithFunction)
12091378
c.addTransitiveDependencies(depWithReturnType)
1379+
c.addTransitiveDependencies(depWithReturnTypesSuperType)
12101380
c.setLabel("dependingTarget")
12111381
}
12121382
val jdeps = depsProto(dependingTarget)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package something;
2+
3+
public class JavaBaseWithTypeParam<T> {
4+
public T passthru(T t) {
5+
return t;
6+
}
7+
}

0 commit comments

Comments
 (0)