diff --git a/BUILD.bazel b/BUILD.bazel index 5b907f47..9b7c0582 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -7,10 +7,9 @@ buildifier( ) # gazelle:java_maven_install_file contrib_rules_jvm_deps_install.json +# gazelle:java_maven_repository_name contrib_rules_jvm_deps # gazelle:prefix github.com/bazel-contrib/rules_jvm -# gazelle:repository go_repository name=org_golang_x_tools_go_vcs importpath=golang.org/x/tools/go/vcs - # gazelle:resolve java com.beust.jcommander @contrib_rules_jvm_deps//:com_beust_jcommander # gazelle:resolve java com.beust.jcommander.converters @contrib_rules_jvm_deps//:com_beust_jcommander # gazelle:resolve java com.gazelle.java.javaparser.v0 //java/gazelle/private/javaparser/proto/gazelle/java/javaparser/v0:gazelle_java_build_v0_java_library @@ -26,6 +25,11 @@ buildifier( # gazelle:resolve java org.apache.commons.cli @contrib_rules_jvm_deps//:commons_cli_commons_cli # gazelle:resolve java org.junit.jupiter.api @maven//:org_junit_jupiter_junit_jupiter_api # gazelle:resolve java org.slf4j @contrib_rules_jvm_deps//:org_slf4j_slf4j_api +# gazelle:resolve java org.jetbrains.kotlin.cli.jvm.compiler @contrib_rules_jvm_deps//:org_jetbrains_kotlin_kotlin_compiler +# gazelle:resolve java org.jetbrains.kotlin.config @contrib_rules_jvm_deps//:org_jetbrains_kotlin_kotlin_compiler +# gazelle:resolve java org.jetbrains.kotlin.lexer @contrib_rules_jvm_deps//:org_jetbrains_kotlin_kotlin_compiler +# gazelle:resolve java org.jetbrains.kotlin.name @contrib_rules_jvm_deps//:org_jetbrains_kotlin_kotlin_compiler +# gazelle:resolve java org.jetbrains.kotlin.psi @contrib_rules_jvm_deps//:org_jetbrains_kotlin_kotlin_compiler gazelle( name = "gazelle", diff --git a/MODULE.bazel b/MODULE.bazel index ea5ce9fa..7b784b4f 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -114,6 +114,9 @@ maven.install( # Used by us at runtime "org.slf4j:slf4j-simple:%s" % slf4j_version, + # For Kotlin parsing in the Gazelle plugin + "org.jetbrains.kotlin:kotlin-compiler:2.2.20", + # We explicitly declare a protobuf runtime version # so that it coincides with the one we use to generate the code. "com.google.protobuf:protobuf-java:{}".format(PROTOBUF_JAVA_VERSION), diff --git a/contrib_rules_jvm_deps_install.json b/contrib_rules_jvm_deps_install.json index ed26ee6d..6e026c63 100755 --- a/contrib_rules_jvm_deps_install.json +++ b/contrib_rules_jvm_deps_install.json @@ -1,7 +1,7 @@ { "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL", - "__INPUT_ARTIFACTS_HASH": -1883190450, - "__RESOLVED_ARTIFACTS_HASH": -2099725319, + "__INPUT_ARTIFACTS_HASH": 1990527508, + "__RESOLVED_ARTIFACTS_HASH": -1762371038, "artifacts": { "com.github.nawforce:scala-json-rpc-upickle-json-serializer_2.13": { "shasums": { @@ -849,29 +849,53 @@ }, "version": "3.28.0-GA" }, + "org.jetbrains.kotlin:kotlin-compiler": { + "shasums": { + "jar": "8546feb440ec2d59e00d475936523fcd3f528e21c7e8eb8a95e6de5044a6d496" + }, + "version": "2.2.20" + }, + "org.jetbrains.kotlin:kotlin-reflect": { + "shasums": { + "jar": "3277ac102ae17aad10a55abec75ff5696c8d109790396434b496e75087854203" + }, + "version": "1.6.10" + }, + "org.jetbrains.kotlin:kotlin-script-runtime": { + "shasums": { + "jar": "5c8bd5dd7ae1ea2eeb2778fdbeaa19a562184975f6cab8a0bb6779e4190731ae" + }, + "version": "2.2.20" + }, "org.jetbrains.kotlin:kotlin-stdlib": { "shasums": { - "jar": "858b902696da9cf585ab9d98ffc1c2712269828354dfe9107e3711b084a36468" + "jar": "8836ccffd3585fadda9901244b20d42901d2f3cd581058d8434e2ffabcf3a3e7" }, - "version": "1.9.24" + "version": "2.2.20" }, "org.jetbrains.kotlin:kotlin-stdlib-jdk7": { "shasums": { - "jar": "b6699b850ba0789f2e904279cd8bdc7bea9130ffd157cdba001fc7425d8a47b7" + "jar": "3bd26ecb6d12978c5c4e0b41f76c9ff551fac2a5e6268427a9d2a0cdf8a5ad91" }, - "version": "1.9.24" + "version": "2.2.20" }, "org.jetbrains.kotlin:kotlin-stdlib-jdk8": { "shasums": { - "jar": "5b5bbfb3e1184b5e13317c3d42237fa24add443b2e7781961eea334db136adb1" + "jar": "c314177935d8dc2eda879507117f25d6de56f6c57ede99416b14cd622bb9e09d" + }, + "version": "2.2.20" + }, + "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm": { + "shasums": { + "jar": "9860906a1937490bf5f3b06d2f0e10ef451e65b95b269f22daf68a3d1f5065c5" }, - "version": "1.9.24" + "version": "1.8.0" }, "org.jetbrains:annotations": { "shasums": { - "jar": "ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478" + "jar": "7b0f19724082cbfcbc66e5abea2b9bc92cf08a1ea11e191933ed43801eb3cd05" }, - "version": "13.0" + "version": "23.0.0" }, "org.jline:jline": { "shasums": { @@ -1084,6 +1108,10 @@ "org.checkerframework:checker-qual:3.48.1": "org.checkerframework:checker-qual:3.43.0", "org.checkerframework:checker-qual:3.48.2": "org.checkerframework:checker-qual:3.43.0", "org.codehaus.plexus:plexus-utils:3.1.1": "org.codehaus.plexus:plexus-utils:3.3.0", + "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.24": "org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.2.20", + "org.jetbrains.kotlin:kotlin-stdlib:1.9.21": "org.jetbrains.kotlin:kotlin-stdlib:2.2.20", + "org.jetbrains.kotlin:kotlin-stdlib:1.9.24": "org.jetbrains.kotlin:kotlin-stdlib:2.2.20", + "org.jetbrains:annotations:13.0": "org.jetbrains:annotations:23.0.0", "org.ow2.asm:asm:9.7.1": "org.ow2.asm:asm:9.7", "org.scala-lang.modules:scala-collection-compat_2.13:2.1.4": "org.scala-lang.modules:scala-collection-compat_2.13:2.8.1", "org.scala-lang.modules:scala-collection-compat_2.13:2.11.0": "org.scala-lang.modules:scala-collection-compat_2.13:2.8.1", @@ -1642,6 +1670,12 @@ "com.google.code.gson:gson", "javax.annotation:jsr250-api" ], + "org.jetbrains.kotlin:kotlin-compiler": [ + "org.jetbrains.kotlin:kotlin-reflect", + "org.jetbrains.kotlin:kotlin-script-runtime", + "org.jetbrains.kotlin:kotlin-stdlib-jdk8", + "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm" + ], "org.jetbrains.kotlin:kotlin-stdlib": [ "org.jetbrains:annotations" ], @@ -1652,6 +1686,10 @@ "org.jetbrains.kotlin:kotlin-stdlib", "org.jetbrains.kotlin:kotlin-stdlib-jdk7" ], + "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm": [ + "org.jetbrains.kotlin:kotlin-stdlib", + "org.jetbrains:annotations" + ], "org.junit.jupiter:junit-jupiter-api": [ "org.apiguardian:apiguardian-api", "org.junit.platform:junit-platform-commons", @@ -3294,6 +3332,958 @@ "javassist.util", "javassist.util.proxy" ], + "org.jetbrains.kotlin:kotlin-compiler": [ + "com.fasterxml.aalto", + "com.fasterxml.aalto.impl", + "com.fasterxml.aalto.in", + "com.fasterxml.aalto.stax", + "com.fasterxml.aalto.util", + "com.google.common.base", + "com.google.common.collect", + "com.google.common.graph", + "com.google.common.hash", + "com.google.common.io", + "com.google.common.math", + "com.google.common.primitives", + "com.google.common.util.concurrent", + "com.google.gwt.dev.js", + "com.google.gwt.dev.js.parserExceptions", + "com.google.gwt.dev.js.rhino", + "com.intellij", + "com.intellij.codeInsight", + "com.intellij.codeInsight.completion.scope", + "com.intellij.codeInsight.daemon.impl.analysis", + "com.intellij.codeInsight.folding", + "com.intellij.codeInsight.folding.impl", + "com.intellij.codeInsight.highlighting", + "com.intellij.codeInsight.javadoc", + "com.intellij.codeInsight.runner", + "com.intellij.codeWithMe", + "com.intellij.concurrency", + "com.intellij.core", + "com.intellij.diagnostic", + "com.intellij.extapi.psi", + "com.intellij.formatting", + "com.intellij.ide", + "com.intellij.ide.highlighter", + "com.intellij.ide.plugins", + "com.intellij.ide.plugins.cl", + "com.intellij.ide.util", + "com.intellij.idea", + "com.intellij.injected.editor", + "com.intellij.lang", + "com.intellij.lang.folding", + "com.intellij.lang.impl", + "com.intellij.lang.injection", + "com.intellij.lang.java", + "com.intellij.lang.java.beans", + "com.intellij.lang.java.lexer", + "com.intellij.lang.java.parser", + "com.intellij.lang.jvm", + "com.intellij.lang.jvm.annotation", + "com.intellij.lang.jvm.facade", + "com.intellij.lang.jvm.types", + "com.intellij.lexer", + "com.intellij.mock", + "com.intellij.model", + "com.intellij.model.psi", + "com.intellij.navigation", + "com.intellij.notebook.editor", + "com.intellij.openapi", + "com.intellij.openapi.application", + "com.intellij.openapi.application.ex", + "com.intellij.openapi.application.impl", + "com.intellij.openapi.client", + "com.intellij.openapi.command", + "com.intellij.openapi.command.impl", + "com.intellij.openapi.command.undo", + "com.intellij.openapi.components", + "com.intellij.openapi.diagnostic", + "com.intellij.openapi.editor", + "com.intellij.openapi.editor.actionSystem", + "com.intellij.openapi.editor.colors", + "com.intellij.openapi.editor.event", + "com.intellij.openapi.editor.ex", + "com.intellij.openapi.editor.impl", + "com.intellij.openapi.editor.impl.event", + "com.intellij.openapi.editor.markup", + "com.intellij.openapi.extensions", + "com.intellij.openapi.extensions.impl", + "com.intellij.openapi.fileEditor", + "com.intellij.openapi.fileEditor.impl", + "com.intellij.openapi.fileTypes", + "com.intellij.openapi.module", + "com.intellij.openapi.progress", + "com.intellij.openapi.progress.impl", + "com.intellij.openapi.progress.util", + "com.intellij.openapi.project", + "com.intellij.openapi.projectRoots", + "com.intellij.openapi.roots", + "com.intellij.openapi.roots.impl", + "com.intellij.openapi.ui", + "com.intellij.openapi.util", + "com.intellij.openapi.util.io", + "com.intellij.openapi.util.objectTree", + "com.intellij.openapi.util.registry", + "com.intellij.openapi.util.text", + "com.intellij.openapi.vfs", + "com.intellij.openapi.vfs.encoding", + "com.intellij.openapi.vfs.impl", + "com.intellij.openapi.vfs.impl.jar", + "com.intellij.openapi.vfs.local", + "com.intellij.openapi.vfs.newvfs", + "com.intellij.openapi.vfs.newvfs.events", + "com.intellij.openapi.vfs.pointers", + "com.intellij.openapi.wm.ex", + "com.intellij.patterns", + "com.intellij.patterns.compiler", + "com.intellij.platform.backend.navigation", + "com.intellij.platform.diagnostic.telemetry", + "com.intellij.platform.diagnostic.telemetry.helpers", + "com.intellij.pom", + "com.intellij.pom.core.impl", + "com.intellij.pom.event", + "com.intellij.pom.impl", + "com.intellij.pom.java", + "com.intellij.pom.tree", + "com.intellij.pom.tree.events", + "com.intellij.pom.tree.events.impl", + "com.intellij.pom.wrappers", + "com.intellij.psi", + "com.intellij.psi.augment", + "com.intellij.psi.codeStyle", + "com.intellij.psi.compiled", + "com.intellij.psi.controlFlow", + "com.intellij.psi.css", + "com.intellij.psi.filters", + "com.intellij.psi.filters.classes", + "com.intellij.psi.filters.element", + "com.intellij.psi.filters.position", + "com.intellij.psi.impl", + "com.intellij.psi.impl.cache", + "com.intellij.psi.impl.compiled", + "com.intellij.psi.impl.file", + "com.intellij.psi.impl.file.impl", + "com.intellij.psi.impl.java.stubs", + "com.intellij.psi.impl.java.stubs.impl", + "com.intellij.psi.impl.java.stubs.index", + "com.intellij.psi.impl.light", + "com.intellij.psi.impl.meta", + "com.intellij.psi.impl.search", + "com.intellij.psi.impl.smartPointers", + "com.intellij.psi.impl.source", + "com.intellij.psi.impl.source.codeStyle", + "com.intellij.psi.impl.source.javadoc", + "com.intellij.psi.impl.source.resolve", + "com.intellij.psi.impl.source.resolve.graphInference", + "com.intellij.psi.impl.source.resolve.graphInference.constraints", + "com.intellij.psi.impl.source.resolve.reference", + "com.intellij.psi.impl.source.resolve.reference.impl", + "com.intellij.psi.impl.source.resolve.reference.impl.manipulators", + "com.intellij.psi.impl.source.resolve.reference.impl.providers", + "com.intellij.psi.impl.source.tree", + "com.intellij.psi.impl.source.tree.injected", + "com.intellij.psi.impl.source.tree.java", + "com.intellij.psi.infos", + "com.intellij.psi.javadoc", + "com.intellij.psi.meta", + "com.intellij.psi.presentation.java", + "com.intellij.psi.scope", + "com.intellij.psi.scope.conflictResolvers", + "com.intellij.psi.scope.processor", + "com.intellij.psi.scope.util", + "com.intellij.psi.search", + "com.intellij.psi.search.impl", + "com.intellij.psi.search.searches", + "com.intellij.psi.stub", + "com.intellij.psi.stubs", + "com.intellij.psi.targets", + "com.intellij.psi.templateLanguages", + "com.intellij.psi.text", + "com.intellij.psi.tree", + "com.intellij.psi.tree.java", + "com.intellij.psi.util", + "com.intellij.reference", + "com.intellij.serialization", + "com.intellij.serviceContainer", + "com.intellij.testFramework", + "com.intellij.ui", + "com.intellij.ui.icons", + "com.intellij.util", + "com.intellij.util.cls", + "com.intellij.util.codeInsight", + "com.intellij.util.concurrency", + "com.intellij.util.containers", + "com.intellij.util.containers.hash", + "com.intellij.util.diff", + "com.intellij.util.execution", + "com.intellij.util.graph", + "com.intellij.util.graph.impl", + "com.intellij.util.indexing", + "com.intellij.util.indexing.impl", + "com.intellij.util.io", + "com.intellij.util.io.keyStorage", + "com.intellij.util.io.pagecache", + "com.intellij.util.io.pagecache.impl", + "com.intellij.util.io.stats", + "com.intellij.util.io.storage", + "com.intellij.util.keyFMap", + "com.intellij.util.lang", + "com.intellij.util.messages", + "com.intellij.util.messages.impl", + "com.intellij.util.pico", + "com.intellij.util.ref", + "com.intellij.util.system", + "com.intellij.util.text", + "com.intellij.util.ui", + "com.intellij.util.xml.dom", + "com.intellij.util.xmlb", + "com.intellij.util.xmlb.annotations", + "com.sun.jna", + "com.sun.jna.internal", + "com.sun.jna.platform", + "com.sun.jna.platform.dnd", + "com.sun.jna.platform.linux", + "com.sun.jna.platform.mac", + "com.sun.jna.platform.unix", + "com.sun.jna.platform.unix.aix", + "com.sun.jna.platform.unix.solaris", + "com.sun.jna.platform.win32", + "com.sun.jna.platform.win32.COM", + "com.sun.jna.platform.win32.COM.tlb", + "com.sun.jna.platform.win32.COM.tlb.imp", + "com.sun.jna.platform.win32.COM.util", + "com.sun.jna.platform.win32.COM.util.annotation", + "com.sun.jna.platform.wince", + "com.sun.jna.ptr", + "com.sun.jna.win32", + "io.opentelemetry.api", + "io.opentelemetry.api.common", + "io.opentelemetry.api.internal", + "io.opentelemetry.api.logs", + "io.opentelemetry.api.metrics", + "io.opentelemetry.api.trace", + "io.vavr", + "io.vavr.collection", + "io.vavr.concurrent", + "io.vavr.control", + "it.unimi.dsi.fastutil", + "it.unimi.dsi.fastutil.booleans", + "it.unimi.dsi.fastutil.bytes", + "it.unimi.dsi.fastutil.chars", + "it.unimi.dsi.fastutil.doubles", + "it.unimi.dsi.fastutil.floats", + "it.unimi.dsi.fastutil.ints", + "it.unimi.dsi.fastutil.longs", + "it.unimi.dsi.fastutil.objects", + "it.unimi.dsi.fastutil.shorts", + "javax.inject", + "kotlinx.collections.immutable", + "kotlinx.collections.immutable.implementations.immutableList", + "kotlinx.collections.immutable.implementations.immutableMap", + "kotlinx.collections.immutable.implementations.immutableSet", + "kotlinx.collections.immutable.implementations.persistentOrderedMap", + "kotlinx.collections.immutable.implementations.persistentOrderedSet", + "kotlinx.collections.immutable.internal", + "net.jpountz.lz4", + "net.jpountz.util", + "net.jpountz.xxhash", + "one.util.streamex", + "org.apache.log4j", + "org.codehaus.stax2", + "org.codehaus.stax2.ri", + "org.codehaus.stax2.ri.typed", + "org.codehaus.stax2.typed", + "org.fusesource.jansi", + "org.fusesource.jansi.internal", + "org.fusesource.jansi.io", + "org.jdom", + "org.jdom.filter2", + "org.jdom.output", + "org.jetbrains.annotations", + "org.jetbrains.concurrency", + "org.jetbrains.kotlin", + "org.jetbrains.kotlin.analysis.decompiled.light.classes", + "org.jetbrains.kotlin.analysis.decompiled.light.classes.origin", + "org.jetbrains.kotlin.analysis.decompiler.js", + "org.jetbrains.kotlin.analysis.decompiler.konan", + "org.jetbrains.kotlin.analysis.decompiler.psi", + "org.jetbrains.kotlin.analysis.decompiler.psi.file", + "org.jetbrains.kotlin.analysis.decompiler.psi.text", + "org.jetbrains.kotlin.analysis.decompiler.stub", + "org.jetbrains.kotlin.analysis.decompiler.stub.file", + "org.jetbrains.kotlin.analysis.decompiler.stub.flags", + "org.jetbrains.kotlin.analyzer", + "org.jetbrains.kotlin.analyzer.common", + "org.jetbrains.kotlin.asJava", + "org.jetbrains.kotlin.asJava.builder", + "org.jetbrains.kotlin.asJava.classes", + "org.jetbrains.kotlin.asJava.elements", + "org.jetbrains.kotlin.asJava.finder", + "org.jetbrains.kotlin.backend.common", + "org.jetbrains.kotlin.backend.common.actualizer", + "org.jetbrains.kotlin.backend.common.actualizer.checker", + "org.jetbrains.kotlin.backend.common.checkers", + "org.jetbrains.kotlin.backend.common.checkers.context", + "org.jetbrains.kotlin.backend.common.checkers.declaration", + "org.jetbrains.kotlin.backend.common.checkers.expression", + "org.jetbrains.kotlin.backend.common.checkers.symbol", + "org.jetbrains.kotlin.backend.common.checkers.type", + "org.jetbrains.kotlin.backend.common.descriptors", + "org.jetbrains.kotlin.backend.common.diagnostics", + "org.jetbrains.kotlin.backend.common.extensions", + "org.jetbrains.kotlin.backend.common.ir", + "org.jetbrains.kotlin.backend.common.linkage", + "org.jetbrains.kotlin.backend.common.linkage.issues", + "org.jetbrains.kotlin.backend.common.linkage.partial", + "org.jetbrains.kotlin.backend.common.lower", + "org.jetbrains.kotlin.backend.common.lower.coroutines", + "org.jetbrains.kotlin.backend.common.lower.inline", + "org.jetbrains.kotlin.backend.common.lower.loops", + "org.jetbrains.kotlin.backend.common.lower.loops.handlers", + "org.jetbrains.kotlin.backend.common.lower.optimizations", + "org.jetbrains.kotlin.backend.common.output", + "org.jetbrains.kotlin.backend.common.overrides", + "org.jetbrains.kotlin.backend.common.phaser", + "org.jetbrains.kotlin.backend.common.serialization", + "org.jetbrains.kotlin.backend.common.serialization.encodings", + "org.jetbrains.kotlin.backend.common.serialization.mangle", + "org.jetbrains.kotlin.backend.common.serialization.mangle.descriptor", + "org.jetbrains.kotlin.backend.common.serialization.mangle.ir", + "org.jetbrains.kotlin.backend.common.serialization.metadata", + "org.jetbrains.kotlin.backend.common.serialization.proto", + "org.jetbrains.kotlin.backend.common.serialization.signature", + "org.jetbrains.kotlin.backend.js", + "org.jetbrains.kotlin.backend.jvm", + "org.jetbrains.kotlin.backend.jvm.caches", + "org.jetbrains.kotlin.backend.jvm.codegen", + "org.jetbrains.kotlin.backend.jvm.extensions", + "org.jetbrains.kotlin.backend.jvm.intrinsics", + "org.jetbrains.kotlin.backend.jvm.ir", + "org.jetbrains.kotlin.backend.jvm.lower", + "org.jetbrains.kotlin.backend.jvm.lower.indy", + "org.jetbrains.kotlin.backend.jvm.mapping", + "org.jetbrains.kotlin.backend.jvm.metadata", + "org.jetbrains.kotlin.backend.jvm.overrides", + "org.jetbrains.kotlin.backend.jvm.serialization", + "org.jetbrains.kotlin.backend.jvm.serialization.proto", + "org.jetbrains.kotlin.backend.konan", + "org.jetbrains.kotlin.backend.konan.descriptors", + "org.jetbrains.kotlin.backend.konan.ir", + "org.jetbrains.kotlin.backend.konan.lower", + "org.jetbrains.kotlin.backend.konan.serialization", + "org.jetbrains.kotlin.backend.wasm", + "org.jetbrains.kotlin.backend.wasm.dce", + "org.jetbrains.kotlin.backend.wasm.dwarf", + "org.jetbrains.kotlin.backend.wasm.dwarf.entries", + "org.jetbrains.kotlin.backend.wasm.dwarf.lines", + "org.jetbrains.kotlin.backend.wasm.dwarf.utils", + "org.jetbrains.kotlin.backend.wasm.export", + "org.jetbrains.kotlin.backend.wasm.ic", + "org.jetbrains.kotlin.backend.wasm.ir2wasm", + "org.jetbrains.kotlin.backend.wasm.lower", + "org.jetbrains.kotlin.backend.wasm.serialization", + "org.jetbrains.kotlin.backend.wasm.utils", + "org.jetbrains.kotlin.build", + "org.jetbrains.kotlin.build.report", + "org.jetbrains.kotlin.build.report.metrics", + "org.jetbrains.kotlin.build.report.statistics", + "org.jetbrains.kotlin.build.report.statistics.file", + "org.jetbrains.kotlin.buildtools.api", + "org.jetbrains.kotlin.buildtools.api.internal", + "org.jetbrains.kotlin.buildtools.api.internal.wrappers", + "org.jetbrains.kotlin.buildtools.api.jvm", + "org.jetbrains.kotlin.buildtools.internal", + "org.jetbrains.kotlin.builtins", + "org.jetbrains.kotlin.builtins.functions", + "org.jetbrains.kotlin.builtins.jvm", + "org.jetbrains.kotlin.builtins.konan", + "org.jetbrains.kotlin.cfg", + "org.jetbrains.kotlin.cfg.pseudocode", + "org.jetbrains.kotlin.cfg.pseudocode.instructions", + "org.jetbrains.kotlin.cfg.pseudocode.instructions.eval", + "org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps", + "org.jetbrains.kotlin.cfg.pseudocode.instructions.special", + "org.jetbrains.kotlin.cfg.pseudocodeTraverser", + "org.jetbrains.kotlin.cfg.variable", + "org.jetbrains.kotlin.checkers", + "org.jetbrains.kotlin.checkers.diagnostics", + "org.jetbrains.kotlin.checkers.diagnostics.factories", + "org.jetbrains.kotlin.checkers.utils", + "org.jetbrains.kotlin.cli.common", + "org.jetbrains.kotlin.cli.common.arguments", + "org.jetbrains.kotlin.cli.common.config", + "org.jetbrains.kotlin.cli.common.environment", + "org.jetbrains.kotlin.cli.common.extensions", + "org.jetbrains.kotlin.cli.common.fir", + "org.jetbrains.kotlin.cli.common.localfs", + "org.jetbrains.kotlin.cli.common.messages", + "org.jetbrains.kotlin.cli.common.modules", + "org.jetbrains.kotlin.cli.common.output", + "org.jetbrains.kotlin.cli.common.profiling", + "org.jetbrains.kotlin.cli.common.repl", + "org.jetbrains.kotlin.cli.js", + "org.jetbrains.kotlin.cli.js.klib", + "org.jetbrains.kotlin.cli.jvm", + "org.jetbrains.kotlin.cli.jvm.compiler", + "org.jetbrains.kotlin.cli.jvm.compiler.jarfs", + "org.jetbrains.kotlin.cli.jvm.compiler.legacy.pipeline", + "org.jetbrains.kotlin.cli.jvm.config", + "org.jetbrains.kotlin.cli.jvm.index", + "org.jetbrains.kotlin.cli.jvm.javac", + "org.jetbrains.kotlin.cli.jvm.modules", + "org.jetbrains.kotlin.cli.jvm.plugins", + "org.jetbrains.kotlin.cli.metadata", + "org.jetbrains.kotlin.cli.pipeline", + "org.jetbrains.kotlin.cli.pipeline.jvm", + "org.jetbrains.kotlin.cli.pipeline.metadata", + "org.jetbrains.kotlin.cli.pipeline.web", + "org.jetbrains.kotlin.cli.pipeline.web.js", + "org.jetbrains.kotlin.cli.pipeline.web.wasm", + "org.jetbrains.kotlin.cli.plugins", + "org.jetbrains.kotlin.codegen", + "org.jetbrains.kotlin.codegen.coroutines", + "org.jetbrains.kotlin.codegen.extensions", + "org.jetbrains.kotlin.codegen.inline", + "org.jetbrains.kotlin.codegen.inline.coroutines", + "org.jetbrains.kotlin.codegen.intrinsics", + "org.jetbrains.kotlin.codegen.optimization", + "org.jetbrains.kotlin.codegen.optimization.boxing", + "org.jetbrains.kotlin.codegen.optimization.common", + "org.jetbrains.kotlin.codegen.optimization.fixStack", + "org.jetbrains.kotlin.codegen.optimization.nullCheck", + "org.jetbrains.kotlin.codegen.optimization.temporaryVals", + "org.jetbrains.kotlin.codegen.optimization.transformer", + "org.jetbrains.kotlin.codegen.pseudoInsns", + "org.jetbrains.kotlin.codegen.serialization", + "org.jetbrains.kotlin.codegen.signature", + "org.jetbrains.kotlin.codegen.state", + "org.jetbrains.kotlin.codegen.when", + "org.jetbrains.kotlin.compiler.plugin", + "org.jetbrains.kotlin.compilerRunner", + "org.jetbrains.kotlin.config", + "org.jetbrains.kotlin.config.phaser", + "org.jetbrains.kotlin.constant", + "org.jetbrains.kotlin.container", + "org.jetbrains.kotlin.context", + "org.jetbrains.kotlin.contracts", + "org.jetbrains.kotlin.contracts.description", + "org.jetbrains.kotlin.contracts.description.expressions", + "org.jetbrains.kotlin.contracts.interpretation", + "org.jetbrains.kotlin.contracts.model", + "org.jetbrains.kotlin.contracts.model.functors", + "org.jetbrains.kotlin.contracts.model.structure", + "org.jetbrains.kotlin.contracts.model.visitors", + "org.jetbrains.kotlin.contracts.parsing", + "org.jetbrains.kotlin.contracts.parsing.effects", + "org.jetbrains.kotlin.coroutines", + "org.jetbrains.kotlin.descriptors", + "org.jetbrains.kotlin.descriptors.annotations", + "org.jetbrains.kotlin.descriptors.deserialization", + "org.jetbrains.kotlin.descriptors.impl", + "org.jetbrains.kotlin.descriptors.java", + "org.jetbrains.kotlin.descriptors.konan", + "org.jetbrains.kotlin.descriptors.runtime.components", + "org.jetbrains.kotlin.descriptors.runtime.structure", + "org.jetbrains.kotlin.descriptors.synthetic", + "org.jetbrains.kotlin.diagnostics", + "org.jetbrains.kotlin.diagnostics.impl", + "org.jetbrains.kotlin.diagnostics.rendering", + "org.jetbrains.kotlin.extensions", + "org.jetbrains.kotlin.extensions.internal", + "org.jetbrains.kotlin.fileClasses", + "org.jetbrains.kotlin.fir", + "org.jetbrains.kotlin.fir.analysis", + "org.jetbrains.kotlin.fir.analysis.cfa", + "org.jetbrains.kotlin.fir.analysis.cfa.util", + "org.jetbrains.kotlin.fir.analysis.checkers", + "org.jetbrains.kotlin.fir.analysis.checkers.cfa", + "org.jetbrains.kotlin.fir.analysis.checkers.config", + "org.jetbrains.kotlin.fir.analysis.checkers.context", + "org.jetbrains.kotlin.fir.analysis.checkers.declaration", + "org.jetbrains.kotlin.fir.analysis.checkers.experimental", + "org.jetbrains.kotlin.fir.analysis.checkers.expression", + "org.jetbrains.kotlin.fir.analysis.checkers.extra", + "org.jetbrains.kotlin.fir.analysis.checkers.syntax", + "org.jetbrains.kotlin.fir.analysis.checkers.type", + "org.jetbrains.kotlin.fir.analysis.collectors", + "org.jetbrains.kotlin.fir.analysis.collectors.components", + "org.jetbrains.kotlin.fir.analysis.diagnostics", + "org.jetbrains.kotlin.fir.analysis.diagnostics.js", + "org.jetbrains.kotlin.fir.analysis.diagnostics.jvm", + "org.jetbrains.kotlin.fir.analysis.diagnostics.native", + "org.jetbrains.kotlin.fir.analysis.diagnostics.wasm", + "org.jetbrains.kotlin.fir.analysis.diagnostics.web.common", + "org.jetbrains.kotlin.fir.analysis.extensions", + "org.jetbrains.kotlin.fir.analysis.js.checkers", + "org.jetbrains.kotlin.fir.analysis.js.checkers.declaration", + "org.jetbrains.kotlin.fir.analysis.js.checkers.expression", + "org.jetbrains.kotlin.fir.analysis.jvm", + "org.jetbrains.kotlin.fir.analysis.jvm.checkers", + "org.jetbrains.kotlin.fir.analysis.jvm.checkers.declaration", + "org.jetbrains.kotlin.fir.analysis.jvm.checkers.expression", + "org.jetbrains.kotlin.fir.analysis.jvm.checkers.type", + "org.jetbrains.kotlin.fir.analysis.native.checkers", + "org.jetbrains.kotlin.fir.analysis.wasm.checkers", + "org.jetbrains.kotlin.fir.analysis.wasm.checkers.declaration", + "org.jetbrains.kotlin.fir.analysis.wasm.checkers.expression", + "org.jetbrains.kotlin.fir.analysis.web.common.checkers", + "org.jetbrains.kotlin.fir.analysis.web.common.checkers.declaration", + "org.jetbrains.kotlin.fir.analysis.web.common.checkers.expression", + "org.jetbrains.kotlin.fir.backend", + "org.jetbrains.kotlin.fir.backend.generators", + "org.jetbrains.kotlin.fir.backend.jvm", + "org.jetbrains.kotlin.fir.backend.native", + "org.jetbrains.kotlin.fir.backend.native.interop", + "org.jetbrains.kotlin.fir.backend.utils", + "org.jetbrains.kotlin.fir.builder", + "org.jetbrains.kotlin.fir.caches", + "org.jetbrains.kotlin.fir.checkers", + "org.jetbrains.kotlin.fir.contracts", + "org.jetbrains.kotlin.fir.contracts.builder", + "org.jetbrains.kotlin.fir.contracts.description", + "org.jetbrains.kotlin.fir.contracts.impl", + "org.jetbrains.kotlin.fir.declarations", + "org.jetbrains.kotlin.fir.declarations.builder", + "org.jetbrains.kotlin.fir.declarations.comparators", + "org.jetbrains.kotlin.fir.declarations.impl", + "org.jetbrains.kotlin.fir.declarations.synthetic", + "org.jetbrains.kotlin.fir.declarations.utils", + "org.jetbrains.kotlin.fir.descriptors", + "org.jetbrains.kotlin.fir.deserialization", + "org.jetbrains.kotlin.fir.diagnostics", + "org.jetbrains.kotlin.fir.expressions", + "org.jetbrains.kotlin.fir.expressions.builder", + "org.jetbrains.kotlin.fir.expressions.impl", + "org.jetbrains.kotlin.fir.extensions", + "org.jetbrains.kotlin.fir.extensions.predicate", + "org.jetbrains.kotlin.fir.extensions.utils", + "org.jetbrains.kotlin.fir.impl", + "org.jetbrains.kotlin.fir.java", + "org.jetbrains.kotlin.fir.java.declarations", + "org.jetbrains.kotlin.fir.java.deserialization", + "org.jetbrains.kotlin.fir.java.enhancement", + "org.jetbrains.kotlin.fir.java.scopes", + "org.jetbrains.kotlin.fir.java.symbols", + "org.jetbrains.kotlin.fir.lazy", + "org.jetbrains.kotlin.fir.lightTree", + "org.jetbrains.kotlin.fir.lightTree.converter", + "org.jetbrains.kotlin.fir.lightTree.fir", + "org.jetbrains.kotlin.fir.lightTree.fir.modifier", + "org.jetbrains.kotlin.fir.modules", + "org.jetbrains.kotlin.fir.pipeline", + "org.jetbrains.kotlin.fir.plugin", + "org.jetbrains.kotlin.fir.references", + "org.jetbrains.kotlin.fir.references.builder", + "org.jetbrains.kotlin.fir.references.impl", + "org.jetbrains.kotlin.fir.renderer", + "org.jetbrains.kotlin.fir.resolve", + "org.jetbrains.kotlin.fir.resolve.calls", + "org.jetbrains.kotlin.fir.resolve.calls.candidate", + "org.jetbrains.kotlin.fir.resolve.calls.js", + "org.jetbrains.kotlin.fir.resolve.calls.jvm", + "org.jetbrains.kotlin.fir.resolve.calls.overloads", + "org.jetbrains.kotlin.fir.resolve.calls.stages", + "org.jetbrains.kotlin.fir.resolve.calls.tower", + "org.jetbrains.kotlin.fir.resolve.dfa", + "org.jetbrains.kotlin.fir.resolve.dfa.cfg", + "org.jetbrains.kotlin.fir.resolve.diagnostics", + "org.jetbrains.kotlin.fir.resolve.inference", + "org.jetbrains.kotlin.fir.resolve.inference.model", + "org.jetbrains.kotlin.fir.resolve.providers", + "org.jetbrains.kotlin.fir.resolve.providers.impl", + "org.jetbrains.kotlin.fir.resolve.scopes", + "org.jetbrains.kotlin.fir.resolve.substitution", + "org.jetbrains.kotlin.fir.resolve.transformers", + "org.jetbrains.kotlin.fir.resolve.transformers.body.resolve", + "org.jetbrains.kotlin.fir.resolve.transformers.contracts", + "org.jetbrains.kotlin.fir.resolve.transformers.mpp", + "org.jetbrains.kotlin.fir.resolve.transformers.plugin", + "org.jetbrains.kotlin.fir.scopes", + "org.jetbrains.kotlin.fir.scopes.impl", + "org.jetbrains.kotlin.fir.scopes.jvm", + "org.jetbrains.kotlin.fir.serialization", + "org.jetbrains.kotlin.fir.serialization.constant", + "org.jetbrains.kotlin.fir.session", + "org.jetbrains.kotlin.fir.session.environment", + "org.jetbrains.kotlin.fir.symbols", + "org.jetbrains.kotlin.fir.symbols.impl", + "org.jetbrains.kotlin.fir.types", + "org.jetbrains.kotlin.fir.types.builder", + "org.jetbrains.kotlin.fir.types.impl", + "org.jetbrains.kotlin.fir.types.jvm", + "org.jetbrains.kotlin.fir.util", + "org.jetbrains.kotlin.fir.utils.exceptions", + "org.jetbrains.kotlin.fir.visitors", + "org.jetbrains.kotlin.frontend.di", + "org.jetbrains.kotlin.frontend.java.di", + "org.jetbrains.kotlin.frontend.js.di", + "org.jetbrains.kotlin.idea", + "org.jetbrains.kotlin.idea.references", + "org.jetbrains.kotlin.incremental", + "org.jetbrains.kotlin.incremental.classpathDiff", + "org.jetbrains.kotlin.incremental.classpathDiff.impl", + "org.jetbrains.kotlin.incremental.components", + "org.jetbrains.kotlin.incremental.dirtyFiles", + "org.jetbrains.kotlin.incremental.impl", + "org.jetbrains.kotlin.incremental.javaInterop", + "org.jetbrains.kotlin.incremental.js", + "org.jetbrains.kotlin.incremental.multiproject", + "org.jetbrains.kotlin.incremental.parsing", + "org.jetbrains.kotlin.incremental.snapshots", + "org.jetbrains.kotlin.incremental.storage", + "org.jetbrains.kotlin.incremental.util", + "org.jetbrains.kotlin.inline", + "org.jetbrains.kotlin.ir", + "org.jetbrains.kotlin.ir.backend.js", + "org.jetbrains.kotlin.ir.backend.js.checkers", + "org.jetbrains.kotlin.ir.backend.js.checkers.declarations", + "org.jetbrains.kotlin.ir.backend.js.checkers.expressions", + "org.jetbrains.kotlin.ir.backend.js.dce", + "org.jetbrains.kotlin.ir.backend.js.export", + "org.jetbrains.kotlin.ir.backend.js.ic", + "org.jetbrains.kotlin.ir.backend.js.ir", + "org.jetbrains.kotlin.ir.backend.js.lower", + "org.jetbrains.kotlin.ir.backend.js.lower.calls", + "org.jetbrains.kotlin.ir.backend.js.lower.cleanup", + "org.jetbrains.kotlin.ir.backend.js.lower.coroutines", + "org.jetbrains.kotlin.ir.backend.js.lower.inline", + "org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir", + "org.jetbrains.kotlin.ir.backend.js.transformers.irToJs", + "org.jetbrains.kotlin.ir.backend.js.utils", + "org.jetbrains.kotlin.ir.backend.js.utils.serialization", + "org.jetbrains.kotlin.ir.backend.jvm", + "org.jetbrains.kotlin.ir.backend.jvm.serialization", + "org.jetbrains.kotlin.ir.builders", + "org.jetbrains.kotlin.ir.builders.declarations", + "org.jetbrains.kotlin.ir.declarations", + "org.jetbrains.kotlin.ir.declarations.impl", + "org.jetbrains.kotlin.ir.declarations.lazy", + "org.jetbrains.kotlin.ir.descriptors", + "org.jetbrains.kotlin.ir.expressions", + "org.jetbrains.kotlin.ir.expressions.impl", + "org.jetbrains.kotlin.ir.inline", + "org.jetbrains.kotlin.ir.inline.konan", + "org.jetbrains.kotlin.ir.interpreter", + "org.jetbrains.kotlin.ir.interpreter.builtins", + "org.jetbrains.kotlin.ir.interpreter.checker", + "org.jetbrains.kotlin.ir.interpreter.exceptions", + "org.jetbrains.kotlin.ir.interpreter.intrinsics", + "org.jetbrains.kotlin.ir.interpreter.preprocessor", + "org.jetbrains.kotlin.ir.interpreter.proxy", + "org.jetbrains.kotlin.ir.interpreter.proxy.reflection", + "org.jetbrains.kotlin.ir.interpreter.stack", + "org.jetbrains.kotlin.ir.interpreter.state", + "org.jetbrains.kotlin.ir.interpreter.state.reflection", + "org.jetbrains.kotlin.ir.interpreter.transformer", + "org.jetbrains.kotlin.ir.objcinterop", + "org.jetbrains.kotlin.ir.overrides", + "org.jetbrains.kotlin.ir.symbols", + "org.jetbrains.kotlin.ir.symbols.impl", + "org.jetbrains.kotlin.ir.types", + "org.jetbrains.kotlin.ir.types.impl", + "org.jetbrains.kotlin.ir.util", + "org.jetbrains.kotlin.ir.visitors", + "org.jetbrains.kotlin.javac", + "org.jetbrains.kotlin.javac.components", + "org.jetbrains.kotlin.javac.resolve", + "org.jetbrains.kotlin.javac.wrappers.symbols", + "org.jetbrains.kotlin.javac.wrappers.trees", + "org.jetbrains.kotlin.js", + "org.jetbrains.kotlin.js.analyze", + "org.jetbrains.kotlin.js.analyzer", + "org.jetbrains.kotlin.js.backend", + "org.jetbrains.kotlin.js.backend.ast", + "org.jetbrains.kotlin.js.backend.ast.metadata", + "org.jetbrains.kotlin.js.common", + "org.jetbrains.kotlin.js.config", + "org.jetbrains.kotlin.js.descriptorUtils", + "org.jetbrains.kotlin.js.inline.clean", + "org.jetbrains.kotlin.js.inline.util", + "org.jetbrains.kotlin.js.inline.util.collectors", + "org.jetbrains.kotlin.js.naming", + "org.jetbrains.kotlin.js.parser", + "org.jetbrains.kotlin.js.parser.sourcemaps", + "org.jetbrains.kotlin.js.patterns", + "org.jetbrains.kotlin.js.resolve", + "org.jetbrains.kotlin.js.resolve.diagnostics", + "org.jetbrains.kotlin.js.sourceMap", + "org.jetbrains.kotlin.js.translate.context", + "org.jetbrains.kotlin.js.translate.utils", + "org.jetbrains.kotlin.js.util", + "org.jetbrains.kotlin.kapt3.diagnostic", + "org.jetbrains.kotlin.kdoc.lexer", + "org.jetbrains.kotlin.kdoc.parser", + "org.jetbrains.kotlin.kdoc.psi.api", + "org.jetbrains.kotlin.kdoc.psi.impl", + "org.jetbrains.kotlin.konan", + "org.jetbrains.kotlin.konan.exec", + "org.jetbrains.kotlin.konan.file", + "org.jetbrains.kotlin.konan.library", + "org.jetbrains.kotlin.konan.library.impl", + "org.jetbrains.kotlin.konan.properties", + "org.jetbrains.kotlin.konan.target", + "org.jetbrains.kotlin.konan.util", + "org.jetbrains.kotlin.lang", + "org.jetbrains.kotlin.lexer", + "org.jetbrains.kotlin.library", + "org.jetbrains.kotlin.library.abi", + "org.jetbrains.kotlin.library.abi.impl", + "org.jetbrains.kotlin.library.encodings", + "org.jetbrains.kotlin.library.impl", + "org.jetbrains.kotlin.library.loader", + "org.jetbrains.kotlin.library.metadata", + "org.jetbrains.kotlin.library.metadata.impl", + "org.jetbrains.kotlin.library.metadata.resolver", + "org.jetbrains.kotlin.library.metadata.resolver.impl", + "org.jetbrains.kotlin.load.java", + "org.jetbrains.kotlin.load.java.components", + "org.jetbrains.kotlin.load.java.descriptors", + "org.jetbrains.kotlin.load.java.lazy", + "org.jetbrains.kotlin.load.java.lazy.descriptors", + "org.jetbrains.kotlin.load.java.lazy.types", + "org.jetbrains.kotlin.load.java.sam", + "org.jetbrains.kotlin.load.java.sources", + "org.jetbrains.kotlin.load.java.structure", + "org.jetbrains.kotlin.load.java.structure.impl", + "org.jetbrains.kotlin.load.java.structure.impl.classFiles", + "org.jetbrains.kotlin.load.java.structure.impl.source", + "org.jetbrains.kotlin.load.java.typeEnhancement", + "org.jetbrains.kotlin.load.kotlin", + "org.jetbrains.kotlin.load.kotlin.header", + "org.jetbrains.kotlin.load.kotlin.incremental", + "org.jetbrains.kotlin.load.kotlin.incremental.components", + "org.jetbrains.kotlin.metadata", + "org.jetbrains.kotlin.metadata.builtins", + "org.jetbrains.kotlin.metadata.deserialization", + "org.jetbrains.kotlin.metadata.java", + "org.jetbrains.kotlin.metadata.js", + "org.jetbrains.kotlin.metadata.jvm", + "org.jetbrains.kotlin.metadata.jvm.deserialization", + "org.jetbrains.kotlin.metadata.jvm.serialization", + "org.jetbrains.kotlin.metadata.serialization", + "org.jetbrains.kotlin.modules", + "org.jetbrains.kotlin.mpp", + "org.jetbrains.kotlin.name", + "org.jetbrains.kotlin.native.interop", + "org.jetbrains.kotlin.parsing", + "org.jetbrains.kotlin.platform", + "org.jetbrains.kotlin.platform.js", + "org.jetbrains.kotlin.platform.jvm", + "org.jetbrains.kotlin.platform.konan", + "org.jetbrains.kotlin.platform.wasm", + "org.jetbrains.kotlin.plugin.references", + "org.jetbrains.kotlin.progress", + "org.jetbrains.kotlin.protobuf", + "org.jetbrains.kotlin.psi", + "org.jetbrains.kotlin.psi.addRemoveModifier", + "org.jetbrains.kotlin.psi.codeFragmentUtil", + "org.jetbrains.kotlin.psi.debugText", + "org.jetbrains.kotlin.psi.findDocComment", + "org.jetbrains.kotlin.psi.impl", + "org.jetbrains.kotlin.psi.psiUtil", + "org.jetbrains.kotlin.psi.stubs", + "org.jetbrains.kotlin.psi.stubs.elements", + "org.jetbrains.kotlin.psi.stubs.impl", + "org.jetbrains.kotlin.psi.synthetics", + "org.jetbrains.kotlin.psi.typeRefHelpers", + "org.jetbrains.kotlin.psi.utils", + "org.jetbrains.kotlin.psi2ir", + "org.jetbrains.kotlin.psi2ir.descriptors", + "org.jetbrains.kotlin.psi2ir.generators", + "org.jetbrains.kotlin.psi2ir.generators.fragments", + "org.jetbrains.kotlin.psi2ir.intermediate", + "org.jetbrains.kotlin.psi2ir.lazy", + "org.jetbrains.kotlin.psi2ir.preprocessing", + "org.jetbrains.kotlin.psi2ir.transformations", + "org.jetbrains.kotlin.renderer", + "org.jetbrains.kotlin.resolve", + "org.jetbrains.kotlin.resolve.annotations", + "org.jetbrains.kotlin.resolve.bindingContextUtil", + "org.jetbrains.kotlin.resolve.calls", + "org.jetbrains.kotlin.resolve.calls.callUtil", + "org.jetbrains.kotlin.resolve.calls.checkers", + "org.jetbrains.kotlin.resolve.calls.components", + "org.jetbrains.kotlin.resolve.calls.components.candidate", + "org.jetbrains.kotlin.resolve.calls.context", + "org.jetbrains.kotlin.resolve.calls.inference", + "org.jetbrains.kotlin.resolve.calls.inference.components", + "org.jetbrains.kotlin.resolve.calls.inference.constraintPosition", + "org.jetbrains.kotlin.resolve.calls.inference.model", + "org.jetbrains.kotlin.resolve.calls.model", + "org.jetbrains.kotlin.resolve.calls.mpp", + "org.jetbrains.kotlin.resolve.calls.results", + "org.jetbrains.kotlin.resolve.calls.smartcasts", + "org.jetbrains.kotlin.resolve.calls.tasks", + "org.jetbrains.kotlin.resolve.calls.tower", + "org.jetbrains.kotlin.resolve.calls.util", + "org.jetbrains.kotlin.resolve.checkers", + "org.jetbrains.kotlin.resolve.constants", + "org.jetbrains.kotlin.resolve.constants.evaluate", + "org.jetbrains.kotlin.resolve.deprecation", + "org.jetbrains.kotlin.resolve.descriptorUtil", + "org.jetbrains.kotlin.resolve.diagnostics", + "org.jetbrains.kotlin.resolve.extensions", + "org.jetbrains.kotlin.resolve.inline", + "org.jetbrains.kotlin.resolve.jvm", + "org.jetbrains.kotlin.resolve.jvm.annotations", + "org.jetbrains.kotlin.resolve.jvm.checkers", + "org.jetbrains.kotlin.resolve.jvm.diagnostics", + "org.jetbrains.kotlin.resolve.jvm.extensions", + "org.jetbrains.kotlin.resolve.jvm.jvmSignature", + "org.jetbrains.kotlin.resolve.jvm.kotlinSignature", + "org.jetbrains.kotlin.resolve.jvm.modules", + "org.jetbrains.kotlin.resolve.jvm.multiplatform", + "org.jetbrains.kotlin.resolve.jvm.platform", + "org.jetbrains.kotlin.resolve.konan.diagnostics", + "org.jetbrains.kotlin.resolve.konan.platform", + "org.jetbrains.kotlin.resolve.lazy", + "org.jetbrains.kotlin.resolve.lazy.data", + "org.jetbrains.kotlin.resolve.lazy.declarations", + "org.jetbrains.kotlin.resolve.lazy.descriptors", + "org.jetbrains.kotlin.resolve.multiplatform", + "org.jetbrains.kotlin.resolve.references", + "org.jetbrains.kotlin.resolve.repl", + "org.jetbrains.kotlin.resolve.sam", + "org.jetbrains.kotlin.resolve.scopes", + "org.jetbrains.kotlin.resolve.scopes.optimization", + "org.jetbrains.kotlin.resolve.scopes.receivers", + "org.jetbrains.kotlin.resolve.scopes.synthetic", + "org.jetbrains.kotlin.resolve.scopes.utils", + "org.jetbrains.kotlin.resolve.source", + "org.jetbrains.kotlin.resolve.typeBinding", + "org.jetbrains.kotlin.serialization", + "org.jetbrains.kotlin.serialization.deserialization", + "org.jetbrains.kotlin.serialization.deserialization.builtins", + "org.jetbrains.kotlin.serialization.deserialization.descriptors", + "org.jetbrains.kotlin.serialization.js", + "org.jetbrains.kotlin.serialization.js.ast", + "org.jetbrains.kotlin.stats", + "org.jetbrains.kotlin.storage", + "org.jetbrains.kotlin.synthetic", + "org.jetbrains.kotlin.type", + "org.jetbrains.kotlin.types", + "org.jetbrains.kotlin.types.checker", + "org.jetbrains.kotlin.types.error", + "org.jetbrains.kotlin.types.expressions", + "org.jetbrains.kotlin.types.expressions.typeInfoFactory", + "org.jetbrains.kotlin.types.expressions.unqualifiedSuper", + "org.jetbrains.kotlin.types.extensions", + "org.jetbrains.kotlin.types.model", + "org.jetbrains.kotlin.types.typeUtil", + "org.jetbrains.kotlin.types.typesApproximation", + "org.jetbrains.kotlin.util", + "org.jetbrains.kotlin.util.capitalizeDecapitalize", + "org.jetbrains.kotlin.util.collectionUtils", + "org.jetbrains.kotlin.util.slicedMap", + "org.jetbrains.kotlin.util.vavr", + "org.jetbrains.kotlin.utils", + "org.jetbrains.kotlin.utils.addToStdlib", + "org.jetbrains.kotlin.utils.concurrent.block", + "org.jetbrains.kotlin.utils.exceptions", + "org.jetbrains.kotlin.utils.fileUtils", + "org.jetbrains.kotlin.utils.kapt", + "org.jetbrains.kotlin.utils.repl", + "org.jetbrains.kotlin.utils.strings", + "org.jetbrains.kotlin.wasm.analyze", + "org.jetbrains.kotlin.wasm.config", + "org.jetbrains.kotlin.wasm.ir", + "org.jetbrains.kotlin.wasm.ir.convertors", + "org.jetbrains.kotlin.wasm.ir.debug", + "org.jetbrains.kotlin.wasm.ir.source.location", + "org.jetbrains.kotlin.wasm.resolve", + "org.jetbrains.kotlin.wasm.resolve.diagnostics", + "org.jetbrains.kotlin.wasm.util", + "org.jetbrains.org.objectweb.asm", + "org.jetbrains.org.objectweb.asm.commons", + "org.jetbrains.org.objectweb.asm.signature", + "org.jetbrains.org.objectweb.asm.tree", + "org.jetbrains.org.objectweb.asm.tree.analysis", + "org.jetbrains.org.objectweb.asm.util", + "org.jline.builtins", + "org.jline.console", + "org.jline.keymap", + "org.jline.reader", + "org.jline.reader.impl", + "org.jline.reader.impl.history", + "org.jline.terminal", + "org.jline.terminal.impl", + "org.jline.terminal.spi", + "org.jline.utils", + "org.picocontainer" + ], + "org.jetbrains.kotlin:kotlin-reflect": [ + "kotlin.reflect.full", + "kotlin.reflect.jvm", + "kotlin.reflect.jvm.internal", + "kotlin.reflect.jvm.internal.calls", + "kotlin.reflect.jvm.internal.impl", + "kotlin.reflect.jvm.internal.impl.builtins", + "kotlin.reflect.jvm.internal.impl.builtins.functions", + "kotlin.reflect.jvm.internal.impl.builtins.jvm", + "kotlin.reflect.jvm.internal.impl.descriptors", + "kotlin.reflect.jvm.internal.impl.descriptors.annotations", + "kotlin.reflect.jvm.internal.impl.descriptors.deserialization", + "kotlin.reflect.jvm.internal.impl.descriptors.impl", + "kotlin.reflect.jvm.internal.impl.descriptors.java", + "kotlin.reflect.jvm.internal.impl.descriptors.runtime.components", + "kotlin.reflect.jvm.internal.impl.descriptors.runtime.structure", + "kotlin.reflect.jvm.internal.impl.incremental", + "kotlin.reflect.jvm.internal.impl.incremental.components", + "kotlin.reflect.jvm.internal.impl.load.java", + "kotlin.reflect.jvm.internal.impl.load.java.components", + "kotlin.reflect.jvm.internal.impl.load.java.descriptors", + "kotlin.reflect.jvm.internal.impl.load.java.lazy", + "kotlin.reflect.jvm.internal.impl.load.java.lazy.descriptors", + "kotlin.reflect.jvm.internal.impl.load.java.lazy.types", + "kotlin.reflect.jvm.internal.impl.load.java.sources", + "kotlin.reflect.jvm.internal.impl.load.java.structure", + "kotlin.reflect.jvm.internal.impl.load.java.typeEnhancement", + "kotlin.reflect.jvm.internal.impl.load.kotlin", + "kotlin.reflect.jvm.internal.impl.load.kotlin.header", + "kotlin.reflect.jvm.internal.impl.metadata", + "kotlin.reflect.jvm.internal.impl.metadata.builtins", + "kotlin.reflect.jvm.internal.impl.metadata.deserialization", + "kotlin.reflect.jvm.internal.impl.metadata.jvm", + "kotlin.reflect.jvm.internal.impl.metadata.jvm.deserialization", + "kotlin.reflect.jvm.internal.impl.name", + "kotlin.reflect.jvm.internal.impl.platform", + "kotlin.reflect.jvm.internal.impl.protobuf", + "kotlin.reflect.jvm.internal.impl.renderer", + "kotlin.reflect.jvm.internal.impl.resolve", + "kotlin.reflect.jvm.internal.impl.resolve.calls.inference", + "kotlin.reflect.jvm.internal.impl.resolve.constants", + "kotlin.reflect.jvm.internal.impl.resolve.deprecation", + "kotlin.reflect.jvm.internal.impl.resolve.descriptorUtil", + "kotlin.reflect.jvm.internal.impl.resolve.jvm", + "kotlin.reflect.jvm.internal.impl.resolve.sam", + "kotlin.reflect.jvm.internal.impl.resolve.scopes", + "kotlin.reflect.jvm.internal.impl.resolve.scopes.receivers", + "kotlin.reflect.jvm.internal.impl.serialization", + "kotlin.reflect.jvm.internal.impl.serialization.deserialization", + "kotlin.reflect.jvm.internal.impl.serialization.deserialization.builtins", + "kotlin.reflect.jvm.internal.impl.serialization.deserialization.descriptors", + "kotlin.reflect.jvm.internal.impl.storage", + "kotlin.reflect.jvm.internal.impl.types", + "kotlin.reflect.jvm.internal.impl.types.checker", + "kotlin.reflect.jvm.internal.impl.types.error", + "kotlin.reflect.jvm.internal.impl.types.model", + "kotlin.reflect.jvm.internal.impl.types.typeUtil", + "kotlin.reflect.jvm.internal.impl.types.typesApproximation", + "kotlin.reflect.jvm.internal.impl.util", + "kotlin.reflect.jvm.internal.impl.util.capitalizeDecapitalize", + "kotlin.reflect.jvm.internal.impl.util.collectionUtils", + "kotlin.reflect.jvm.internal.impl.utils", + "kotlin.reflect.jvm.internal.pcollections" + ], + "org.jetbrains.kotlin:kotlin-script-runtime": [ + "kotlin.script.dependencies", + "kotlin.script.experimental.dependencies", + "kotlin.script.experimental.location", + "kotlin.script.extensions", + "kotlin.script.templates", + "kotlin.script.templates.standard" + ], "org.jetbrains.kotlin:kotlin-stdlib": [ "kotlin", "kotlin.annotation", @@ -3303,6 +4293,8 @@ "kotlin.collections.unsigned", "kotlin.comparisons", "kotlin.concurrent", + "kotlin.concurrent.atomics", + "kotlin.concurrent.internal", "kotlin.contracts", "kotlin.coroutines", "kotlin.coroutines.cancellation", @@ -3337,7 +4329,25 @@ "kotlin.text", "kotlin.text.jdk8", "kotlin.time", - "kotlin.time.jdk8" + "kotlin.time.jdk8", + "kotlin.uuid" + ], + "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm": [ + "_COROUTINE", + "kotlinx.coroutines", + "kotlinx.coroutines.channels", + "kotlinx.coroutines.debug", + "kotlinx.coroutines.debug.internal", + "kotlinx.coroutines.flow", + "kotlinx.coroutines.flow.internal", + "kotlinx.coroutines.future", + "kotlinx.coroutines.internal", + "kotlinx.coroutines.intrinsics", + "kotlinx.coroutines.scheduling", + "kotlinx.coroutines.selects", + "kotlinx.coroutines.stream", + "kotlinx.coroutines.sync", + "kotlinx.coroutines.time" ], "org.jetbrains:annotations": [ "org.intellij.lang.annotations", @@ -3797,9 +4807,13 @@ "org.dom4j:dom4j", "org.hamcrest:hamcrest-core", "org.javassist:javassist", + "org.jetbrains.kotlin:kotlin-compiler", + "org.jetbrains.kotlin:kotlin-reflect", + "org.jetbrains.kotlin:kotlin-script-runtime", "org.jetbrains.kotlin:kotlin-stdlib", "org.jetbrains.kotlin:kotlin-stdlib-jdk7", "org.jetbrains.kotlin:kotlin-stdlib-jdk8", + "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm", "org.jetbrains:annotations", "org.jline:jline", "org.jsoup:jsoup", @@ -4079,6 +5093,48 @@ "org.apache.logging.log4j.core.impl.Log4jProvider" ] }, + "org.jetbrains.kotlin:kotlin-compiler": { + "org.jetbrains.kotlin.builtins.BuiltInsLoader": [ + "org.jetbrains.kotlin.serialization.deserialization.builtins.BuiltInsLoaderImpl" + ], + "org.jetbrains.kotlin.resolve.ExternalOverridabilityCondition": [ + "org.jetbrains.kotlin.load.java.ErasedOverridabilityCondition", + "org.jetbrains.kotlin.load.java.FieldOverridabilityCondition", + "org.jetbrains.kotlin.load.java.JavaIncompatibilityRulesOverridabilityCondition" + ], + "org.jetbrains.kotlin.resolve.jvm.KotlinToJvmSignatureMapper": [ + "org.jetbrains.kotlin.codegen.signature.KotlinToJvmSignatureMapperImpl" + ], + "org.jetbrains.kotlin.util.ModuleVisibilityHelper": [ + "org.jetbrains.kotlin.cli.common.ModuleVisibilityHelperImpl" + ], + "org/": [], + "org/jline/": [], + "org/jline/terminal/": [], + "org/jline/terminal/provider/": [], + "org/jline/terminal/provider/exec": [ + "class\u003dorg.jline.terminal.impl.exec.ExecTerminalProvider" + ], + "org/jline/terminal/provider/jansi": [ + "class\u003dorg.jline.terminal.impl.jansi.JansiTerminalProvider" + ], + "org/jline/terminal/provider/jna": [ + "class\u003dorg.jline.terminal.impl.jna.JnaTerminalProvider" + ], + "org/jline/terminal/provider/jni": [ + "class\u003dorg.jline.terminal.impl.jni.JniTerminalProvider" + ] + }, + "org.jetbrains.kotlin:kotlin-reflect": { + "kotlin.reflect.jvm.internal.impl.builtins.BuiltInsLoader": [ + "kotlin.reflect.jvm.internal.impl.serialization.deserialization.builtins.BuiltInsLoaderImpl" + ], + "kotlin.reflect.jvm.internal.impl.resolve.ExternalOverridabilityCondition": [ + "kotlin.reflect.jvm.internal.impl.load.java.ErasedOverridabilityCondition", + "kotlin.reflect.jvm.internal.impl.load.java.FieldOverridabilityCondition", + "kotlin.reflect.jvm.internal.impl.load.java.JavaIncompatibilityRulesOverridabilityCondition" + ] + }, "org.jline:jline": { "org.jline.terminal.spi.JansiSupport": [ "org.jline.terminal.impl.jansi.JansiSupportImpl" diff --git a/java/gazelle/BUILD.bazel b/java/gazelle/BUILD.bazel index 2590d94e..440d6b2a 100644 --- a/java/gazelle/BUILD.bazel +++ b/java/gazelle/BUILD.bazel @@ -23,6 +23,7 @@ go_library( "//java/gazelle/private/java", "//java/gazelle/private/java_export_index", "//java/gazelle/private/javaparser", + "//java/gazelle/private/kotlin", "//java/gazelle/private/logconfig", "//java/gazelle/private/maven", "//java/gazelle/private/sorted_multiset", diff --git a/java/gazelle/configure.go b/java/gazelle/configure.go index a1c862a0..86ee699d 100644 --- a/java/gazelle/configure.go +++ b/java/gazelle/configure.go @@ -71,6 +71,7 @@ func (jc *Configurer) KnownDirectives() []string { javaconfig.JavaResolveToJavaExports, javaconfig.JavaSourcesetRoot, javaconfig.JavaStripResourcesPrefix, + javaconfig.JvmKotlinEnabled, } } @@ -223,8 +224,18 @@ func (jc *Configurer) Configure(c *config.Config, rel string, f *rule.File) { case javaconfig.JavaStripResourcesPrefix: cfg.SetStripResourcesPrefix(d.Value) - } + case javaconfig.JvmKotlinEnabled: + switch d.Value { + case "true": + cfg.SetKotlinEnabled(true) + case "false": + cfg.SetKotlinEnabled(false) + default: + jc.lang.logger.Fatal().Msgf("invalid value for directive %q: %s: possible values are true/false", + javaconfig.JvmKotlinEnabled, d.Value) + } + } } } diff --git a/java/gazelle/generate.go b/java/gazelle/generate.go index 876adef9..06e92962 100644 --- a/java/gazelle/generate.go +++ b/java/gazelle/generate.go @@ -56,20 +56,33 @@ func (l javaLang) GenerateRules(args language.GenerateArgs) language.GenerateRes return res } - isModule := cfg.ModuleGranularity() == "module" - if cfg.GenerateProto() { generateProtoLibraries(args, log, &res) } - javaFilenamesRelativeToPackage := filterStrSlice(args.RegularFiles, func(f string) bool { return filepath.Ext(f) == ".java" }) + var srcFilenamesRelativeToPackage []string + hasKotlinFiles := false + if cfg.KotlinEnabled() { + srcFilenamesRelativeToPackage = filterStrSlice(args.RegularFiles, func(f string) bool { + ext := filepath.Ext(f) + if ext == ".kt" { + hasKotlinFiles = true + return true + } else { + return ext == ".java" + } + }) + } else { + srcFilenamesRelativeToPackage = filterStrSlice(args.RegularFiles, func(f string) bool { return filepath.Ext(f) == ".java" }) + } isResourcesRoot := strings.HasSuffix(args.Rel, "/resources") isResourcesSubdir := strings.Contains(args.Rel, "/resources/") && !isResourcesRoot + isModule := cfg.ModuleGranularity() == "module" var javaPkg *java.Package - if len(javaFilenamesRelativeToPackage) == 0 { + if len(srcFilenamesRelativeToPackage) == 0 { if isResourcesSubdir { // Skip subdirectories of resources roots - they shouldn't generate BUILD files return res @@ -82,18 +95,18 @@ func (l javaLang) GenerateRules(args language.GenerateArgs) language.GenerateRes // For resources root directories, continue processing even without Java files } - if len(javaFilenamesRelativeToPackage) == 0 && isResourcesRoot { + if len(srcFilenamesRelativeToPackage) == 0 && isResourcesRoot { // Skip Java parsing for resources-only directories javaPkg = &java.Package{ Name: types.NewPackageName(""), } } else { - sort.Strings(javaFilenamesRelativeToPackage) + sort.Strings(srcFilenamesRelativeToPackage) var err error javaPkg, err = l.parser.ParsePackage(context.Background(), &javaparser.ParsePackageRequest{ Rel: args.Rel, - Files: javaFilenamesRelativeToPackage, + Files: srcFilenamesRelativeToPackage, }) if err != nil { log.Fatal().Err(err).Str("package", args.Rel).Msg("Failed to parse package") @@ -106,13 +119,20 @@ func (l javaLang) GenerateRules(args language.GenerateArgs) language.GenerateRes // 2. "What input files did you have" isn't a great heuristic for "What classes are generated" // (e.g. inner classes, annotation processor generated classes, etc). // But it will do for now. - javaClassNamesFromFileNames := sorted_set.NewSortedSet([]string{}) - for _, filename := range javaFilenamesRelativeToPackage { - javaClassNamesFromFileNames.Add(strings.TrimSuffix(filename, ".java")) + likelyLocalClassNames := sorted_set.NewSortedSet([]string{}) + for _, filename := range srcFilenamesRelativeToPackage { + if strings.HasSuffix(filename, ".kt") { + fileWithoutExtension := strings.TrimSuffix(filename, ".kt") + likelyLocalClassNames.Add(fileWithoutExtension) + // Top level values and functions in Kotlin are accessible from Java under the Kt class. + likelyLocalClassNames.Add(fileWithoutExtension + "Kt") + } else { + likelyLocalClassNames.Add(strings.TrimSuffix(filename, ".java")) + } } if isModule { - if len(javaFilenamesRelativeToPackage) > 0 { + if len(srcFilenamesRelativeToPackage) > 0 { l.javaPackageCache[args.Rel] = javaPkg } @@ -158,14 +178,14 @@ func (l javaLang) GenerateRules(args language.GenerateArgs) language.GenerateRes allPackageNames.Add(mJavaPkg.Name) if !mJavaPkg.TestPackage { - addNonLocalImportsAndExports(productionJavaImports, nonLocalJavaExports, mJavaPkg.ImportedClasses, mJavaPkg.ImportedPackagesWithoutSpecificClasses, mJavaPkg.ExportedClasses, mJavaPkg.Name, javaClassNamesFromFileNames) + addNonLocalImportsAndExports(productionJavaImports, nonLocalJavaExports, mJavaPkg.ImportedClasses, mJavaPkg.ImportedPackagesWithoutSpecificClasses, mJavaPkg.ExportedClasses, mJavaPkg.Name, likelyLocalClassNames) for _, f := range mJavaPkg.Files.SortedSlice() { productionJavaFiles.Add(filepath.Join(mRel, f)) } allMains.AddAll(mJavaPkg.Mains) } else { // Tests don't get to export things, as things shouldn't depend on them. - addNonLocalImportsAndExports(testJavaImports, nil, mJavaPkg.ImportedClasses, mJavaPkg.ImportedPackagesWithoutSpecificClasses, mJavaPkg.ExportedClasses, mJavaPkg.Name, javaClassNamesFromFileNames) + addNonLocalImportsAndExports(testJavaImports, nil, mJavaPkg.ImportedClasses, mJavaPkg.ImportedPackagesWithoutSpecificClasses, mJavaPkg.ExportedClasses, mJavaPkg.Name, likelyLocalClassNames) for _, f := range mJavaPkg.Files.SortedSlice() { path := filepath.Join(mRel, f) file := javaFile{ @@ -183,12 +203,12 @@ func (l javaLang) GenerateRules(args language.GenerateArgs) language.GenerateRes allPackageNames.Add(javaPkg.Name) if javaPkg.TestPackage { // Tests don't get to export things, as things shouldn't depend on them. - addNonLocalImportsAndExports(testJavaImports, nil, javaPkg.ImportedClasses, javaPkg.ImportedPackagesWithoutSpecificClasses, javaPkg.ExportedClasses, javaPkg.Name, javaClassNamesFromFileNames) + addNonLocalImportsAndExports(testJavaImports, nil, javaPkg.ImportedClasses, javaPkg.ImportedPackagesWithoutSpecificClasses, javaPkg.ExportedClasses, javaPkg.Name, likelyLocalClassNames) } else { - addNonLocalImportsAndExports(productionJavaImports, nonLocalJavaExports, javaPkg.ImportedClasses, javaPkg.ImportedPackagesWithoutSpecificClasses, javaPkg.ExportedClasses, javaPkg.Name, javaClassNamesFromFileNames) + addNonLocalImportsAndExports(productionJavaImports, nonLocalJavaExports, javaPkg.ImportedClasses, javaPkg.ImportedPackagesWithoutSpecificClasses, javaPkg.ExportedClasses, javaPkg.Name, likelyLocalClassNames) } allMains.AddAll(javaPkg.Mains) - for _, f := range javaFilenamesRelativeToPackage { + for _, f := range srcFilenamesRelativeToPackage { path := filepath.Join(args.Rel, f) if javaPkg.TestPackage { file := javaFile{ @@ -216,12 +236,15 @@ func (l javaLang) GenerateRules(args language.GenerateArgs) language.GenerateRes }) javaLibraryKind := "java_library" - if kindMap, ok := args.Config.KindMap["java_library"]; ok { + if hasKotlinFiles { + javaLibraryKind = "kt_jvm_library" + } + if kindMap, ok := args.Config.KindMap[javaLibraryKind]; ok { javaLibraryKind = kindMap.KindName } // Check if this is a resources root directory and generate a pkg_files target - if isResourcesRoot && len(javaFilenamesRelativeToPackage) == 0 { + if isResourcesRoot && len(srcFilenamesRelativeToPackage) == 0 { // Collect resource files recursively from this directory and all subdirectories var allResourceFiles []string diff --git a/java/gazelle/javaconfig/config.go b/java/gazelle/javaconfig/config.go index 80eda03d..32f288b0 100644 --- a/java/gazelle/javaconfig/config.go +++ b/java/gazelle/javaconfig/config.go @@ -70,6 +70,11 @@ const ( // This is a direct way to specify the resource_strip_prefix for all resources in a directory. // Example: # gazelle:java_strip_resources_prefix my/data/config JavaStripResourcesPrefix = "java_strip_resources_prefix" + + // JvmKotlinEnabled tells the code generator whether to support `kt_jvm_library` rules for Kotlin sources. + // Can be either "true" or "false". Defaults to "true". + // This requires importing the `@rules_kotlin` repository into your workspace if there are any Kotlin sources in the repo. + JvmKotlinEnabled = "jvm_kotlin_enabled" ) // Configs is an extension of map[string]*Config. It provides finding methods @@ -93,6 +98,7 @@ func (c *Config) NewChild() *Config { isModuleRoot: false, generateProto: true, resolveToJavaExports: c.resolveToJavaExports, + kotlinEnabled: c.kotlinEnabled, mavenInstallFile: c.mavenInstallFile, moduleGranularity: c.moduleGranularity, repoRoot: c.repoRoot, @@ -124,6 +130,7 @@ type Config struct { isModuleRoot bool generateProto bool resolveToJavaExports *types.LateInit[bool] + kotlinEnabled bool mavenInstallFile string moduleGranularity string repoRoot string @@ -150,6 +157,7 @@ func New(repoRoot string) *Config { isModuleRoot: false, generateProto: true, resolveToJavaExports: types.NewLateInit[bool](true), + kotlinEnabled: true, mavenInstallFile: "maven_install.json", moduleGranularity: "package", repoRoot: repoRoot, @@ -187,6 +195,14 @@ func (c *Config) SetGenerateProto(generate bool) { c.generateProto = generate } +func (c *Config) KotlinEnabled() bool { + return c.kotlinEnabled +} + +func (c *Config) SetKotlinEnabled(enabled bool) { + c.kotlinEnabled = enabled +} + func (c *Config) MavenRepositoryName() string { return c.mavenRepositoryName } diff --git a/java/gazelle/lang.go b/java/gazelle/lang.go index 9c023651..5567b6fe 100644 --- a/java/gazelle/lang.go +++ b/java/gazelle/lang.go @@ -134,6 +134,20 @@ var javaExportKind = rule.KindInfo{ }, } +var kotlinLibraryKind = rule.KindInfo{ + NonEmptyAttrs: map[string]bool{ + "deps": true, + "exports": true, + "srcs": true, + }, + MergeableAttrs: map[string]bool{"srcs": true}, + ResolveAttrs: map[string]bool{ + "deps": true, + "exports": true, + "runtime_deps": true, + }, +} + func (l javaLang) Kinds() map[string]rule.KindInfo { kinds := map[string]rule.KindInfo{ "java_binary": kindWithRuntimeDeps, @@ -144,6 +158,7 @@ func (l javaLang) Kinds() map[string]rule.KindInfo { "java_test_suite": kindWithRuntimeDeps, "java_proto_library": kindWithoutRuntimeDeps, "java_grpc_library": kindWithoutRuntimeDeps, + "kt_jvm_library": kotlinLibraryKind, } c := l.Configurer.(*Configurer) @@ -184,6 +199,12 @@ var baseJavaLoads = []rule.LoadInfo{ "pkg_files", }, }, + { + Name: "@rules_kotlin//kotlin:jvm.bzl", + Symbols: []string{ + "kt_jvm_library", + }, + }, } func (l javaLang) Loads() []rule.LoadInfo { diff --git a/java/gazelle/private/javaparser/BUILD.bazel b/java/gazelle/private/javaparser/BUILD.bazel index 780b1466..4ca15f2d 100644 --- a/java/gazelle/private/javaparser/BUILD.bazel +++ b/java/gazelle/private/javaparser/BUILD.bazel @@ -1,4 +1,4 @@ -load("@rules_go//go:def.bzl", "go_library") +load("@rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "javaparser", @@ -16,3 +16,13 @@ go_library( "@org_golang_google_grpc//status", ], ) + +go_test( + name = "javaparser_test", + srcs = ["javaparser_test.go"], + embed = [":javaparser"], + deps = [ + "//java/gazelle/private/javaparser/proto/gazelle/java/javaparser/v0:gazelle_java_build_v0_go_proto", + "//java/gazelle/private/types", + ], +) diff --git a/java/gazelle/private/javaparser/javaparser_test.go b/java/gazelle/private/javaparser/javaparser_test.go new file mode 100644 index 00000000..a2496a13 --- /dev/null +++ b/java/gazelle/private/javaparser/javaparser_test.go @@ -0,0 +1,132 @@ +package javaparser + +import ( + "testing" + + pb "github.com/bazel-contrib/rules_jvm/java/gazelle/private/javaparser/proto/gazelle/java/javaparser/v0" + "github.com/bazel-contrib/rules_jvm/java/gazelle/private/types" +) + +func TestParseExportedClassesFromKotlinFeatures(t *testing.T) { + // Create a mock response with classes that should be exported due to Kotlin features + // (inline functions, extension functions, property delegates, etc.) + resp := &pb.Package{ + Name: "com.example.test", + ExportedClasses: []string{ + "com.google.gson.Gson", + "com.google.common.base.Strings", + }, + } + + // Parse the exported classes + exportedClasses := make([]types.ClassName, 0) + for _, exportedClass := range resp.GetExportedClasses() { + className, err := types.ParseClassName(exportedClass) + if err != nil { + t.Fatalf("Failed to parse exported class %q: %v", exportedClass, err) + } + exportedClasses = append(exportedClasses, *className) + } + + // Verify the results + if len(exportedClasses) != 2 { + t.Fatalf("Expected 2 exported classes, got %d", len(exportedClasses)) + } + + // Check Gson dependency + gson := exportedClasses[0] + if gson.PackageName().Name != "com.google.gson" { + t.Errorf("Expected Gson package 'com.google.gson', got '%s'", gson.PackageName().Name) + } + if gson.BareOuterClassName() != "Gson" { + t.Errorf("Expected Gson class 'Gson', got '%s'", gson.BareOuterClassName()) + } + if gson.FullyQualifiedClassName() != "com.google.gson.Gson" { + t.Errorf("Expected Gson FQN 'com.google.gson.Gson', got '%s'", gson.FullyQualifiedClassName()) + } + + // Check Strings dependency + strings := exportedClasses[1] + if strings.PackageName().Name != "com.google.common.base" { + t.Errorf("Expected Strings package 'com.google.common.base', got '%s'", strings.PackageName().Name) + } + if strings.BareOuterClassName() != "Strings" { + t.Errorf("Expected Strings class 'Strings', got '%s'", strings.BareOuterClassName()) + } + if strings.FullyQualifiedClassName() != "com.google.common.base.Strings" { + t.Errorf("Expected Strings FQN 'com.google.common.base.Strings', got '%s'", strings.FullyQualifiedClassName()) + } + + t.Logf("Successfully parsed exported classes from Kotlin features:") + for i, dep := range exportedClasses { + t.Logf(" [%d] %s -> package: %s, class: %s", i, dep.FullyQualifiedClassName(), dep.PackageName().Name, dep.BareOuterClassName()) + } +} + +func TestParsePackageWithKotlinFeatureExports(t *testing.T) { + // Create a mock response similar to what the Java parser would send + // when parsing Kotlin code with inline functions, extension functions, etc. + resp := &pb.Package{ + Name: "com.example.provider", + ImportedClasses: []string{ + "com.google.gson.Gson", + "com.google.common.base.Strings", + }, + // These classes are both imported AND exported because they're used in + // Kotlin language features (inline functions, extension functions, etc.) + // that leak into the public API + ExportedClasses: []string{ + "com.google.gson.Gson", + "com.google.common.base.Strings", + }, + ImportedPackagesWithoutSpecificClasses: []string{}, + Mains: []string{}, + PerClassMetadata: map[string]*pb.PerClassMetadata{}, + } + + // Simulate what ParsePackage does - just parse the exported classes + packageName := types.NewPackageName(resp.GetName()) + + exportedClasses := make([]types.ClassName, 0) + for _, exportedClass := range resp.GetExportedClasses() { + className, err := types.ParseClassName(exportedClass) + if err != nil { + t.Fatalf("Failed to parse exported class %q: %v", exportedClass, err) + } + exportedClasses = append(exportedClasses, *className) + } + + // Verify the results + if len(exportedClasses) != 2 { + t.Fatalf("Expected 2 exported classes from Kotlin features, got %d", len(exportedClasses)) + } + + // Verify package name + if packageName.Name != "com.example.provider" { + t.Errorf("Expected package name 'com.example.provider', got '%s'", packageName.Name) + } + + // Verify the exported classes include dependencies from Kotlin features + foundGson := false + foundStrings := false + for _, className := range exportedClasses { + if className.FullyQualifiedClassName() == "com.google.gson.Gson" { + foundGson = true + } + if className.FullyQualifiedClassName() == "com.google.common.base.Strings" { + foundStrings = true + } + } + + if !foundGson { + t.Errorf("Expected to find Gson in exported classes from Kotlin features") + } + if !foundStrings { + t.Errorf("Expected to find Strings in exported classes from Kotlin features") + } + + t.Logf("Successfully verified package with Kotlin feature exports:") + for i, className := range exportedClasses { + t.Logf(" [%d] %s", i, className.FullyQualifiedClassName()) + } +} diff --git a/java/gazelle/private/javaparser/proto/gazelle/java/javaparser/v0/javaparser.proto b/java/gazelle/private/javaparser/proto/gazelle/java/javaparser/v0/javaparser.proto index 876683af..040f4b14 100644 --- a/java/gazelle/private/javaparser/proto/gazelle/java/javaparser/v0/javaparser.proto +++ b/java/gazelle/private/javaparser/proto/gazelle/java/javaparser/v0/javaparser.proto @@ -7,9 +7,9 @@ option java_multiple_files = true; option java_package = "com.gazelle.java.javaparser.v0"; // JavaParser service is the RPCs between the gazelle java extension and the -// java parser. +// java and Kotlin parsers. service JavaParser { - // Parse a java package. + // Parse a Java or Kotlin package. rpc ParsePackage(ParsePackageRequest) returns (Package) {} } @@ -18,7 +18,7 @@ message ParsePackageRequest { // Workspace relative directory to consider. string rel = 2; - // List of Java files in the relative directory. + // List of Java or Kotlin files in the relative directory. repeated string files = 3; } @@ -66,6 +66,7 @@ message PerFieldMetadata { repeated string annotation_class_names = 1; } + service Lifecycle { rpc Shutdown(ShutdownRequest) returns (ShutdownResponse) {} } diff --git a/java/gazelle/private/kotlin/BUILD.bazel b/java/gazelle/private/kotlin/BUILD.bazel new file mode 100644 index 00000000..5a065277 --- /dev/null +++ b/java/gazelle/private/kotlin/BUILD.bazel @@ -0,0 +1,20 @@ +load("@rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "kotlin", + srcs = ["kotlin.go"], + importpath = "github.com/bazel-contrib/rules_jvm/java/gazelle/private/kotlin", + # Allow visibility for plugins that don't live in this repo + visibility = ["//visibility:public"], + deps = [ + "//java/gazelle/private/types", + ], +) + +go_test( + name = "kotlin_test", + size = "small", + srcs = ["kotlin_test.go"], + embed = [":kotlin"], + deps = ["//java/gazelle/private/types"], +) diff --git a/java/gazelle/private/kotlin/kotlin.go b/java/gazelle/private/kotlin/kotlin.go new file mode 100644 index 00000000..8b25631c --- /dev/null +++ b/java/gazelle/private/kotlin/kotlin.go @@ -0,0 +1,11 @@ +package kotlin + +import ( + "github.com/bazel-contrib/rules_jvm/java/gazelle/private/types" +) + +var kotlinStdLibPrefix = types.NewPackageName("kotlin") + +func IsStdlib(imp types.PackageName) bool { + return types.PackageNamesHasPrefix(imp, kotlinStdLibPrefix) +} diff --git a/java/gazelle/private/kotlin/kotlin_test.go b/java/gazelle/private/kotlin/kotlin_test.go new file mode 100644 index 00000000..eb9b37d9 --- /dev/null +++ b/java/gazelle/private/kotlin/kotlin_test.go @@ -0,0 +1,26 @@ +package kotlin + +import ( + "testing" + + "github.com/bazel-contrib/rules_jvm/java/gazelle/private/types" +) + +func TestIsStdLib(t *testing.T) { + tests := map[string]bool{ + "": false, + "kotlin": true, + "kotlin.math": true, + "kotlin.collections": true, + "java.lang": false, + "com.example": false, + } + + for pkg, want := range tests { + t.Run(pkg, func(t *testing.T) { + if got := IsStdlib(types.NewPackageName(pkg)); got != want { + t.Errorf("IsStdLib() = %v, want %v", got, want) + } + }) + } +} diff --git a/java/gazelle/private/types/types_test.go b/java/gazelle/private/types/types_test.go index 653c55de..60b04bea 100644 --- a/java/gazelle/private/types/types_test.go +++ b/java/gazelle/private/types/types_test.go @@ -49,6 +49,20 @@ func TestParseClassName(t *testing.T) { innerClassNames: []string{""}, }, }, + "gson class": { + from: "com.google.gson.Gson", + want: &ClassName{ + packageName: NewPackageName("com.google.gson"), + bareOuterClassName: "Gson", + }, + }, + "guava strings class": { + from: "com.google.common.base.Strings", + want: &ClassName{ + packageName: NewPackageName("com.google.common.base"), + bareOuterClassName: "Strings", + }, + }, } { t.Run(name, func(t *testing.T) { got, err := ParseClassName(tc.from) diff --git a/java/gazelle/resolve.go b/java/gazelle/resolve.go index f97d4ec0..49109832 100644 --- a/java/gazelle/resolve.go +++ b/java/gazelle/resolve.go @@ -8,6 +8,7 @@ import ( "github.com/bazel-contrib/rules_jvm/java/gazelle/javaconfig" "github.com/bazel-contrib/rules_jvm/java/gazelle/private/java" + "github.com/bazel-contrib/rules_jvm/java/gazelle/private/kotlin" "github.com/bazel-contrib/rules_jvm/java/gazelle/private/maven" "github.com/bazel-contrib/rules_jvm/java/gazelle/private/sorted_set" "github.com/bazel-contrib/rules_jvm/java/gazelle/private/types" @@ -50,7 +51,7 @@ func (*Resolver) Name() string { func (jr *Resolver) Imports(c *config.Config, r *rule.Rule, f *rule.File) []resolve.ImportSpec { log := jr.lang.logger.With().Str("step", "Imports").Str("rel", f.Pkg).Str("rule", r.Name()).Logger() - if !isJavaLibrary(r.Kind()) && r.Kind() != "java_test_suite" && r.Kind() != "java_export" { + if !isJvmLibrary(r.Kind()) && r.Kind() != "java_test_suite" && r.Kind() != "java_export" { return nil } @@ -258,6 +259,9 @@ func (jr *Resolver) resolveSinglePackage(c *config.Config, pc *javaconfig.Config if java.IsStdlib(imp) { return label.NoLabel } + if kotlin.IsStdlib(imp) { + return label.NoLabel + } // As per https://github.com/bazelbuild/bazel/blob/347407a88fd480fc5e0fbd42cc8196e4356a690b/tools/java/runfiles/Runfiles.java#L41 if imp.Name == "com.google.devtools.build.runfiles" { @@ -383,10 +387,18 @@ func (jr *Resolver) tryResolvingToJavaExport(results []resolve.FindResult, from return nonJavaExportResults } +func isJvmLibrary(kind string) bool { + return isJavaLibrary(kind) || isKotlinLibrary(kind) +} + func isJavaLibrary(kind string) bool { return kind == "java_library" || isJavaProtoLibrary(kind) } +func isKotlinLibrary(kind string) bool { + return kind == "kt_jvm_library" +} + func isJavaProtoLibrary(kind string) bool { return kind == "java_proto_library" || kind == "java_grpc_library" } diff --git a/java/gazelle/resolve_test.go b/java/gazelle/resolve_test.go index 505ac4c3..a8506491 100644 --- a/java/gazelle/resolve_test.go +++ b/java/gazelle/resolve_test.go @@ -24,6 +24,108 @@ import ( "golang.org/x/tools/go/vcs" ) +func TestImports(t *testing.T) { + type buildFile struct { + rel, content string + } + + type wantImport struct { + importSpec resolve.ImportSpec + labelName string + } + + type testCase struct { + old buildFile + want []wantImport + } + + for name, tc := range map[string]testCase{ + "java": { + old: buildFile{ + rel: "", + content: `load("@rules_java//java:defs.bzl", "java_library") + +java_library( + name = "hello", + srcs = ["Hello.java"], + _packages = ["com.example"], + visibility = ["//:__subpackages__"], +)`, + }, + want: []wantImport{ + wantImport{ + importSpec: resolve.ImportSpec{ + Lang: "java", + Imp: "com.example", + }, + labelName: "hello", + }, + }, + }, + "kotlin": { + old: buildFile{ + rel: "", + content: `load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") + +# gazelle:jvm_kotlin_enabled true + +kt_jvm_library( + name = "hello", + srcs = ["Hello.kt"], + _packages = ["com.example"], + visibility = ["//:__subpackages__"], +)`, + }, + want: []wantImport{ + wantImport{ + importSpec: resolve.ImportSpec{ + Lang: "java", + Imp: "com.example", + }, + labelName: "hello", + }, + }, + }, + } { + t.Run(name, func(t *testing.T) { + c, langs, confs := testConfig(t) + + mrslv, exts := InitTestResolversAndExtensions(langs) + ix := resolve.NewRuleIndex(mrslv.Resolver, exts...) + + buildPath := filepath.Join(filepath.FromSlash(tc.old.rel), "BUILD.bazel") + f, err := rule.LoadData(buildPath, tc.old.rel, []byte(tc.old.content)) + if err != nil { + t.Fatal(err) + } + for _, configurer := range confs { + // Update the config to handle gazelle directives in the BUILD file. + configurer.Configure(c, tc.old.rel, f) + } + for _, r := range f.Rules { + // Explicitly set the private `_java_packages` attribute for import resolution, + // This must be done manually as all the attributes stated in the BUILD file are + // considered public. + setPackagesPrivateAttr(r) + ix.AddRule(c, r, f) + t.Logf("added rule %s", r.Name()) + } + ix.Finish() + + for _, want := range tc.want { + results := ix.FindRulesByImportWithConfig(c, want.importSpec, "java") + if len(results) != 1 { + t.Errorf("expected 1 result, got %d for import %v", len(results), want.importSpec.Imp) + } else { + if results[0].Label.Name != want.labelName { + t.Errorf("expected label %s, got %s", want.labelName, results[0].Label) + } + } + } + }) + } +} + func TestResolve(t *testing.T) { type buildFile struct { rel, content string @@ -46,8 +148,7 @@ java_library( _imported_packages = ["java.lang"], _packages = ["com.example"], visibility = ["//:__subpackages__"], -) -`, +)`, }, want: `load("@rules_java//java:defs.bzl", "java_library") @@ -55,8 +156,7 @@ java_library( name = "hello", srcs = ["Hello.java"], visibility = ["//:__subpackages__"], -) -`, +)`, }, "external": { old: buildFile{ @@ -72,8 +172,7 @@ java_library( ], _packages = ["com.example"], visibility = ["//:__subpackages__"], -) -`, +)`, }, want: `load("@rules_java//java:defs.bzl", "java_library") @@ -82,8 +181,36 @@ java_library( srcs = ["App.java"], visibility = ["//:__subpackages__"], deps = ["@maven//:com_google_guava_guava"], -) -`, +)`, + }, + "kotlin": { + old: buildFile{ + rel: "", + content: `load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") + +# gazelle:jvm_kotlin_enabled true + +kt_jvm_library( + name = "myproject", + srcs = ["App.kt"], + _imported_packages = [ + "com.google.common.primitives", + "kotlin.collections", + ], + _packages = ["com.example"], + visibility = ["//:__subpackages__"], +)`, + }, + want: `load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") + +# gazelle:jvm_kotlin_enabled true + +kt_jvm_library( + name = "myproject", + srcs = ["App.kt"], + visibility = ["//:__subpackages__"], + deps = ["@maven//:com_google_guava_guava"], +)`, }, } { t.Run(name, func(t *testing.T) { @@ -177,6 +304,16 @@ func stubModInfo(importPath string) (string, error) { return "", fmt.Errorf("could not find module for import path: %q", importPath) } +func setPackagesPrivateAttr(r *rule.Rule) { + packages := r.AttrStrings("_packages") + resolvablePackages := make([]types.ResolvableJavaPackage, 0, len(packages)) + for _, pkg := range packages { + pkgName := types.NewPackageName(pkg) + resolvablePackages = append(resolvablePackages, *types.NewResolvableJavaPackage(pkgName, false, false)) + } + r.SetPrivateAttr(packagesKey, resolvablePackages) +} + func convertImportsAttr(r *rule.Rule) types.ResolveInput { return types.ResolveInput{ PackageNames: packageAttrToSortedSet(r, "_packages"), diff --git a/java/gazelle/testdata/java_lib_with_kotlin_dep/WORKSPACE b/java/gazelle/testdata/java_lib_with_kotlin_dep/WORKSPACE new file mode 100644 index 00000000..b16824a2 --- /dev/null +++ b/java/gazelle/testdata/java_lib_with_kotlin_dep/WORKSPACE @@ -0,0 +1,19 @@ +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +rules_kotlin_version = "1.9.6" + +rules_kotlin_sha = "3b772976fec7bdcda1d84b9d39b176589424c047eb2175bed09aac630e50af43" + +http_archive( + name = "rules_kotlin", + sha256 = rules_kotlin_sha, + urls = ["https://github.com/bazelbuild/rules_kotlin/releases/download/v%s/rules_kotlin-v%s.tar.gz" % (rules_kotlin_version, rules_kotlin_version)], +) + +load("@rules_kotlin//kotlin:repositories.bzl", "kotlin_repositories") + +kotlin_repositories() + +load("@rules_kotlin//kotlin:core.bzl", "kt_register_toolchains") + +kt_register_toolchains() diff --git a/java/gazelle/testdata/java_lib_with_kotlin_dep/maven_install.json b/java/gazelle/testdata/java_lib_with_kotlin_dep/maven_install.json new file mode 100644 index 00000000..6c01c298 --- /dev/null +++ b/java/gazelle/testdata/java_lib_with_kotlin_dep/maven_install.json @@ -0,0 +1 @@ +{"version": "2"} diff --git a/java/gazelle/testdata/java_lib_with_kotlin_dep/src/main/java/com/example/hello/BUILD.in b/java/gazelle/testdata/java_lib_with_kotlin_dep/src/main/java/com/example/hello/BUILD.in new file mode 100644 index 00000000..c9ac7dee --- /dev/null +++ b/java/gazelle/testdata/java_lib_with_kotlin_dep/src/main/java/com/example/hello/BUILD.in @@ -0,0 +1 @@ +# gazelle:jvm_kotlin_enabled true \ No newline at end of file diff --git a/java/gazelle/testdata/java_lib_with_kotlin_dep/src/main/java/com/example/hello/BUILD.out b/java/gazelle/testdata/java_lib_with_kotlin_dep/src/main/java/com/example/hello/BUILD.out new file mode 100644 index 00000000..2628d057 --- /dev/null +++ b/java/gazelle/testdata/java_lib_with_kotlin_dep/src/main/java/com/example/hello/BUILD.out @@ -0,0 +1,10 @@ +load("@rules_java//java:defs.bzl", "java_library") + +# gazelle:jvm_kotlin_enabled true + +java_library( + name = "hello", + srcs = ["Hello.java"], + visibility = ["//:__subpackages__"], + deps = ["//src/main/java/com/example/hello/greeter"], +) diff --git a/java/gazelle/testdata/java_lib_with_kotlin_dep/src/main/java/com/example/hello/Hello.java b/java/gazelle/testdata/java_lib_with_kotlin_dep/src/main/java/com/example/hello/Hello.java new file mode 100644 index 00000000..cf36ac67 --- /dev/null +++ b/java/gazelle/testdata/java_lib_with_kotlin_dep/src/main/java/com/example/hello/Hello.java @@ -0,0 +1,10 @@ +package com.example.hello; + +import com.example.hello.greeter.Greeter; + +public class Hello { + public static void sayHi() { + Greeter greeter = new Greeter(); + System.out.println(greeter.greet()); + } +} diff --git a/java/gazelle/testdata/java_lib_with_kotlin_dep/src/main/java/com/example/hello/greeter/BUILD.in b/java/gazelle/testdata/java_lib_with_kotlin_dep/src/main/java/com/example/hello/greeter/BUILD.in new file mode 100644 index 00000000..c9ac7dee --- /dev/null +++ b/java/gazelle/testdata/java_lib_with_kotlin_dep/src/main/java/com/example/hello/greeter/BUILD.in @@ -0,0 +1 @@ +# gazelle:jvm_kotlin_enabled true \ No newline at end of file diff --git a/java/gazelle/testdata/java_lib_with_kotlin_dep/src/main/java/com/example/hello/greeter/BUILD.out b/java/gazelle/testdata/java_lib_with_kotlin_dep/src/main/java/com/example/hello/greeter/BUILD.out new file mode 100644 index 00000000..8e5c5c60 --- /dev/null +++ b/java/gazelle/testdata/java_lib_with_kotlin_dep/src/main/java/com/example/hello/greeter/BUILD.out @@ -0,0 +1,10 @@ +load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") + +# gazelle:jvm_kotlin_enabled true + +kt_jvm_library( + name = "greeter", + srcs = ["Greeter.kt"], + visibility = ["//:__subpackages__"], +) + diff --git a/java/gazelle/testdata/java_lib_with_kotlin_dep/src/main/java/com/example/hello/greeter/Greeter.kt b/java/gazelle/testdata/java_lib_with_kotlin_dep/src/main/java/com/example/hello/greeter/Greeter.kt new file mode 100644 index 00000000..819702ec --- /dev/null +++ b/java/gazelle/testdata/java_lib_with_kotlin_dep/src/main/java/com/example/hello/greeter/Greeter.kt @@ -0,0 +1,7 @@ +package com.example.hello.greeter + +class Greeter { + fun greet(): String { + return "Hello, World!" + } +} diff --git a/java/gazelle/testdata/kt_bin/BUILD.in b/java/gazelle/testdata/kt_bin/BUILD.in new file mode 100644 index 00000000..feccf582 --- /dev/null +++ b/java/gazelle/testdata/kt_bin/BUILD.in @@ -0,0 +1 @@ +# gazelle:jvm_kotlin_enabled true diff --git a/java/gazelle/testdata/kt_bin/BUILD.out b/java/gazelle/testdata/kt_bin/BUILD.out new file mode 100644 index 00000000..feccf582 --- /dev/null +++ b/java/gazelle/testdata/kt_bin/BUILD.out @@ -0,0 +1 @@ +# gazelle:jvm_kotlin_enabled true diff --git a/java/gazelle/testdata/kt_bin/WORKSPACE b/java/gazelle/testdata/kt_bin/WORKSPACE new file mode 100644 index 00000000..b16824a2 --- /dev/null +++ b/java/gazelle/testdata/kt_bin/WORKSPACE @@ -0,0 +1,19 @@ +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +rules_kotlin_version = "1.9.6" + +rules_kotlin_sha = "3b772976fec7bdcda1d84b9d39b176589424c047eb2175bed09aac630e50af43" + +http_archive( + name = "rules_kotlin", + sha256 = rules_kotlin_sha, + urls = ["https://github.com/bazelbuild/rules_kotlin/releases/download/v%s/rules_kotlin-v%s.tar.gz" % (rules_kotlin_version, rules_kotlin_version)], +) + +load("@rules_kotlin//kotlin:repositories.bzl", "kotlin_repositories") + +kotlin_repositories() + +load("@rules_kotlin//kotlin:core.bzl", "kt_register_toolchains") + +kt_register_toolchains() diff --git a/java/gazelle/testdata/kt_bin/maven_install.json b/java/gazelle/testdata/kt_bin/maven_install.json new file mode 100644 index 00000000..6c01c298 --- /dev/null +++ b/java/gazelle/testdata/kt_bin/maven_install.json @@ -0,0 +1 @@ +{"version": "2"} diff --git a/java/gazelle/testdata/kt_bin/src/main/java/com/example/hello/BUILD.in b/java/gazelle/testdata/kt_bin/src/main/java/com/example/hello/BUILD.in new file mode 100644 index 00000000..e69de29b diff --git a/java/gazelle/testdata/kt_bin/src/main/java/com/example/hello/BUILD.out b/java/gazelle/testdata/kt_bin/src/main/java/com/example/hello/BUILD.out new file mode 100644 index 00000000..10075df0 --- /dev/null +++ b/java/gazelle/testdata/kt_bin/src/main/java/com/example/hello/BUILD.out @@ -0,0 +1,16 @@ +load("@rules_java//java:defs.bzl", "java_binary") +load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") + +kt_jvm_library( + name = "hello", + srcs = ["Hello.kt"], + visibility = ["//:__subpackages__"], +) + +java_binary( + name = "HelloKt", + main_class = "com.example.hello.HelloKt", + visibility = ["//visibility:public"], + runtime_deps = [":hello"], +) + diff --git a/java/gazelle/testdata/kt_bin/src/main/java/com/example/hello/Hello.kt b/java/gazelle/testdata/kt_bin/src/main/java/com/example/hello/Hello.kt new file mode 100644 index 00000000..52bf8883 --- /dev/null +++ b/java/gazelle/testdata/kt_bin/src/main/java/com/example/hello/Hello.kt @@ -0,0 +1,7 @@ +package com.example.hello + +import kotlin.io.println + +fun main(vararg args: String) { + println("Hi!") +} diff --git a/java/gazelle/testdata/kt_destructure/BUILD.in b/java/gazelle/testdata/kt_destructure/BUILD.in new file mode 100644 index 00000000..feccf582 --- /dev/null +++ b/java/gazelle/testdata/kt_destructure/BUILD.in @@ -0,0 +1 @@ +# gazelle:jvm_kotlin_enabled true diff --git a/java/gazelle/testdata/kt_destructure/BUILD.out b/java/gazelle/testdata/kt_destructure/BUILD.out new file mode 100644 index 00000000..feccf582 --- /dev/null +++ b/java/gazelle/testdata/kt_destructure/BUILD.out @@ -0,0 +1 @@ +# gazelle:jvm_kotlin_enabled true diff --git a/java/gazelle/testdata/kt_destructure/WORKSPACE b/java/gazelle/testdata/kt_destructure/WORKSPACE new file mode 100644 index 00000000..6a9b9f54 --- /dev/null +++ b/java/gazelle/testdata/kt_destructure/WORKSPACE @@ -0,0 +1,41 @@ +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +rules_kotlin_version = "1.9.6" + +rules_kotlin_sha = "3b772976fec7bdcda1d84b9d39b176589424c047eb2175bed09aac630e50af43" + +http_archive( + name = "rules_kotlin", + sha256 = rules_kotlin_sha, + urls = ["https://github.com/bazelbuild/rules_kotlin/releases/download/v%s/rules_kotlin-v%s.tar.gz" % (rules_kotlin_version, rules_kotlin_version)], +) + +load("@rules_kotlin//kotlin:repositories.bzl", "kotlin_repositories") + +kotlin_repositories() + +load("@rules_kotlin//kotlin:core.bzl", "kt_register_toolchains") + +kt_register_toolchains() + +http_archive( + name = "rules_jvm_external", + sha256 = "23fe83890a77ac1a3ee143e2306ec12da4a845285b14ea13cb0df1b1e23658fe", + strip_prefix = "rules_jvm_external-4.3", + urls = ["https://github.com/bazelbuild/rules_jvm_external/archive/refs/tags/4.3.tar.gz"], +) + +load("@rules_jvm_external//:defs.bzl", "maven_install") + +maven_install( + name = "maven", + artifacts = [ + "com.google.code.gson:gson:2.8.9", + "com.google.guava:guava:30.0-jre", + ], + fetch_sources = True, + repositories = [ + "http://uk.maven.org/maven2", + "https://jcenter.bintray.com/", + ], +) diff --git a/java/gazelle/testdata/kt_destructure/maven_install.json b/java/gazelle/testdata/kt_destructure/maven_install.json new file mode 100644 index 00000000..12ea79b9 --- /dev/null +++ b/java/gazelle/testdata/kt_destructure/maven_install.json @@ -0,0 +1,162 @@ +{ + "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL", + "__INPUT_ARTIFACTS_HASH": 1811698626, + "__RESOLVED_ARTIFACTS_HASH": 1591722038, + "artifacts": { + "com.google.code.findbugs:jsr305": { + "shasums": { + "jar": "766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7" + }, + "version": "3.0.2" + }, + "com.google.code.gson:gson": { + "shasums": { + "jar": "dd0ce1b55a3ed2080cb70f9c655850cda86c206862310009dcb5e5c95265a5e0" + }, + "version": "2.13.2" + }, + "com.google.errorprone:error_prone_annotations": { + "shasums": { + "jar": "a56e782b5b50811ac204073a355a21d915a2107fce13ec711331ad036f660fcc" + }, + "version": "2.41.0" + }, + "com.google.guava:failureaccess": { + "shasums": { + "jar": "8a8f81cf9b359e3f6dfa691a1e776985c061ef2f223c9b2c80753e1b458e8064" + }, + "version": "1.0.2" + }, + "com.google.guava:guava": { + "shasums": { + "jar": "4bf0e2c5af8e4525c96e8fde17a4f7307f97f8478f11c4c8e35a0e3298ae4e90" + }, + "version": "33.3.1-jre" + }, + "com.google.guava:listenablefuture": { + "shasums": { + "jar": "b372a037d4230aa57fbeffdef30fd6123f9c0c2db85d0aced00c91b974f33f99" + }, + "version": "9999.0-empty-to-avoid-conflict-with-guava" + }, + "com.google.j2objc:j2objc-annotations": { + "shasums": { + "jar": "88241573467ddca44ffd4d74aa04c2bbfd11bf7c17e0c342c94c9de7a70a7c64" + }, + "version": "3.0.0" + }, + "org.checkerframework:checker-qual": { + "shasums": { + "jar": "3fbc2e98f05854c3df16df9abaa955b91b15b3ecac33623208ed6424640ef0f6" + }, + "version": "3.43.0" + } + }, + "dependencies": { + "com.google.code.gson:gson": [ + "com.google.errorprone:error_prone_annotations" + ], + "com.google.guava:guava": [ + "com.google.code.findbugs:jsr305", + "com.google.errorprone:error_prone_annotations", + "com.google.guava:failureaccess", + "com.google.guava:listenablefuture", + "com.google.j2objc:j2objc-annotations", + "org.checkerframework:checker-qual" + ] + }, + "packages": { + "com.google.code.findbugs:jsr305": [ + "javax.annotation", + "javax.annotation.concurrent", + "javax.annotation.meta" + ], + "com.google.code.gson:gson": [ + "com.google.gson", + "com.google.gson.annotations", + "com.google.gson.internal", + "com.google.gson.internal.bind", + "com.google.gson.internal.bind.util", + "com.google.gson.internal.reflect", + "com.google.gson.internal.sql", + "com.google.gson.reflect", + "com.google.gson.stream" + ], + "com.google.errorprone:error_prone_annotations": [ + "com.google.errorprone.annotations", + "com.google.errorprone.annotations.concurrent" + ], + "com.google.guava:failureaccess": [ + "com.google.common.util.concurrent.internal" + ], + "com.google.guava:guava": [ + "com.google.common.annotations", + "com.google.common.base", + "com.google.common.base.internal", + "com.google.common.cache", + "com.google.common.collect", + "com.google.common.escape", + "com.google.common.eventbus", + "com.google.common.graph", + "com.google.common.hash", + "com.google.common.html", + "com.google.common.io", + "com.google.common.math", + "com.google.common.net", + "com.google.common.primitives", + "com.google.common.reflect", + "com.google.common.util.concurrent", + "com.google.common.xml", + "com.google.thirdparty.publicsuffix" + ], + "com.google.j2objc:j2objc-annotations": [ + "com.google.j2objc.annotations" + ], + "org.checkerframework:checker-qual": [ + "org.checkerframework.checker.builder.qual", + "org.checkerframework.checker.calledmethods.qual", + "org.checkerframework.checker.compilermsgs.qual", + "org.checkerframework.checker.fenum.qual", + "org.checkerframework.checker.formatter.qual", + "org.checkerframework.checker.guieffect.qual", + "org.checkerframework.checker.i18n.qual", + "org.checkerframework.checker.i18nformatter.qual", + "org.checkerframework.checker.index.qual", + "org.checkerframework.checker.initialization.qual", + "org.checkerframework.checker.interning.qual", + "org.checkerframework.checker.lock.qual", + "org.checkerframework.checker.mustcall.qual", + "org.checkerframework.checker.nullness.qual", + "org.checkerframework.checker.optional.qual", + "org.checkerframework.checker.propkey.qual", + "org.checkerframework.checker.regex.qual", + "org.checkerframework.checker.signature.qual", + "org.checkerframework.checker.signedness.qual", + "org.checkerframework.checker.tainting.qual", + "org.checkerframework.checker.units.qual", + "org.checkerframework.common.aliasing.qual", + "org.checkerframework.common.initializedfields.qual", + "org.checkerframework.common.reflection.qual", + "org.checkerframework.common.returnsreceiver.qual", + "org.checkerframework.common.subtyping.qual", + "org.checkerframework.common.util.count.report.qual", + "org.checkerframework.common.value.qual", + "org.checkerframework.dataflow.qual", + "org.checkerframework.framework.qual" + ] + }, + "repositories": { + "https://repo1.maven.org/maven2/": [ + "com.google.code.findbugs:jsr305", + "com.google.code.gson:gson", + "com.google.errorprone:error_prone_annotations", + "com.google.guava:failureaccess", + "com.google.guava:guava", + "com.google.guava:listenablefuture", + "com.google.j2objc:j2objc-annotations", + "org.checkerframework:checker-qual" + ] + }, + "services": {}, + "version": "2" +} diff --git a/java/gazelle/testdata/kt_destructure/src/main/java/com/example/destructuring/BUILD.in b/java/gazelle/testdata/kt_destructure/src/main/java/com/example/destructuring/BUILD.in new file mode 100644 index 00000000..e69de29b diff --git a/java/gazelle/testdata/kt_destructure/src/main/java/com/example/destructuring/BUILD.out b/java/gazelle/testdata/kt_destructure/src/main/java/com/example/destructuring/BUILD.out new file mode 100644 index 00000000..50e2f39a --- /dev/null +++ b/java/gazelle/testdata/kt_destructure/src/main/java/com/example/destructuring/BUILD.out @@ -0,0 +1,7 @@ +load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") + +kt_jvm_library( + name = "destructuring", + srcs = ["SimpleDestructuring.kt"], + visibility = ["//:__subpackages__"], +) diff --git a/java/gazelle/testdata/kt_destructure/src/main/java/com/example/destructuring/SimpleDestructuring.kt b/java/gazelle/testdata/kt_destructure/src/main/java/com/example/destructuring/SimpleDestructuring.kt new file mode 100644 index 00000000..9b4d3e3f --- /dev/null +++ b/java/gazelle/testdata/kt_destructure/src/main/java/com/example/destructuring/SimpleDestructuring.kt @@ -0,0 +1,33 @@ +package com.example.destructuring + +/** + * Simple destructuring using data classes. Data classes automatically generate componentN() + * functions, so this should not add any external dependencies. + */ +data class Point(val x: Int, val y: Int) + +data class Person(val name: String, val age: Int, val email: String) + +class SimpleDestructuring { + + fun useDestructuring(): String { + val point = Point(10, 20) + val (x, y) = point // Uses component1() and component2() + + val person = Person("Alice", 30, "alice@example.com") + val (name, age, email) = person // Uses component1(), component2(), component3() + + // Partial destructuring + val (personName, _) = person // Only uses component1() + + return "Point: ($x, $y), Person: $name, $age, $email, Name only: $personName" + } + + fun useInLoop(): List { + val points = listOf(Point(1, 2), Point(3, 4), Point(5, 6)) + + return points.map { (x, y) -> // Destructuring in lambda + "($x, $y)" + } + } +} diff --git a/java/gazelle/testdata/kt_destructure_deps/BUILD.in b/java/gazelle/testdata/kt_destructure_deps/BUILD.in new file mode 100644 index 00000000..feccf582 --- /dev/null +++ b/java/gazelle/testdata/kt_destructure_deps/BUILD.in @@ -0,0 +1 @@ +# gazelle:jvm_kotlin_enabled true diff --git a/java/gazelle/testdata/kt_destructure_deps/BUILD.out b/java/gazelle/testdata/kt_destructure_deps/BUILD.out new file mode 100644 index 00000000..feccf582 --- /dev/null +++ b/java/gazelle/testdata/kt_destructure_deps/BUILD.out @@ -0,0 +1 @@ +# gazelle:jvm_kotlin_enabled true diff --git a/java/gazelle/testdata/kt_destructure_deps/WORKSPACE b/java/gazelle/testdata/kt_destructure_deps/WORKSPACE new file mode 100644 index 00000000..6a9b9f54 --- /dev/null +++ b/java/gazelle/testdata/kt_destructure_deps/WORKSPACE @@ -0,0 +1,41 @@ +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +rules_kotlin_version = "1.9.6" + +rules_kotlin_sha = "3b772976fec7bdcda1d84b9d39b176589424c047eb2175bed09aac630e50af43" + +http_archive( + name = "rules_kotlin", + sha256 = rules_kotlin_sha, + urls = ["https://github.com/bazelbuild/rules_kotlin/releases/download/v%s/rules_kotlin-v%s.tar.gz" % (rules_kotlin_version, rules_kotlin_version)], +) + +load("@rules_kotlin//kotlin:repositories.bzl", "kotlin_repositories") + +kotlin_repositories() + +load("@rules_kotlin//kotlin:core.bzl", "kt_register_toolchains") + +kt_register_toolchains() + +http_archive( + name = "rules_jvm_external", + sha256 = "23fe83890a77ac1a3ee143e2306ec12da4a845285b14ea13cb0df1b1e23658fe", + strip_prefix = "rules_jvm_external-4.3", + urls = ["https://github.com/bazelbuild/rules_jvm_external/archive/refs/tags/4.3.tar.gz"], +) + +load("@rules_jvm_external//:defs.bzl", "maven_install") + +maven_install( + name = "maven", + artifacts = [ + "com.google.code.gson:gson:2.8.9", + "com.google.guava:guava:30.0-jre", + ], + fetch_sources = True, + repositories = [ + "http://uk.maven.org/maven2", + "https://jcenter.bintray.com/", + ], +) diff --git a/java/gazelle/testdata/kt_destructure_deps/maven_install.json b/java/gazelle/testdata/kt_destructure_deps/maven_install.json new file mode 100644 index 00000000..12ea79b9 --- /dev/null +++ b/java/gazelle/testdata/kt_destructure_deps/maven_install.json @@ -0,0 +1,162 @@ +{ + "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL", + "__INPUT_ARTIFACTS_HASH": 1811698626, + "__RESOLVED_ARTIFACTS_HASH": 1591722038, + "artifacts": { + "com.google.code.findbugs:jsr305": { + "shasums": { + "jar": "766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7" + }, + "version": "3.0.2" + }, + "com.google.code.gson:gson": { + "shasums": { + "jar": "dd0ce1b55a3ed2080cb70f9c655850cda86c206862310009dcb5e5c95265a5e0" + }, + "version": "2.13.2" + }, + "com.google.errorprone:error_prone_annotations": { + "shasums": { + "jar": "a56e782b5b50811ac204073a355a21d915a2107fce13ec711331ad036f660fcc" + }, + "version": "2.41.0" + }, + "com.google.guava:failureaccess": { + "shasums": { + "jar": "8a8f81cf9b359e3f6dfa691a1e776985c061ef2f223c9b2c80753e1b458e8064" + }, + "version": "1.0.2" + }, + "com.google.guava:guava": { + "shasums": { + "jar": "4bf0e2c5af8e4525c96e8fde17a4f7307f97f8478f11c4c8e35a0e3298ae4e90" + }, + "version": "33.3.1-jre" + }, + "com.google.guava:listenablefuture": { + "shasums": { + "jar": "b372a037d4230aa57fbeffdef30fd6123f9c0c2db85d0aced00c91b974f33f99" + }, + "version": "9999.0-empty-to-avoid-conflict-with-guava" + }, + "com.google.j2objc:j2objc-annotations": { + "shasums": { + "jar": "88241573467ddca44ffd4d74aa04c2bbfd11bf7c17e0c342c94c9de7a70a7c64" + }, + "version": "3.0.0" + }, + "org.checkerframework:checker-qual": { + "shasums": { + "jar": "3fbc2e98f05854c3df16df9abaa955b91b15b3ecac33623208ed6424640ef0f6" + }, + "version": "3.43.0" + } + }, + "dependencies": { + "com.google.code.gson:gson": [ + "com.google.errorprone:error_prone_annotations" + ], + "com.google.guava:guava": [ + "com.google.code.findbugs:jsr305", + "com.google.errorprone:error_prone_annotations", + "com.google.guava:failureaccess", + "com.google.guava:listenablefuture", + "com.google.j2objc:j2objc-annotations", + "org.checkerframework:checker-qual" + ] + }, + "packages": { + "com.google.code.findbugs:jsr305": [ + "javax.annotation", + "javax.annotation.concurrent", + "javax.annotation.meta" + ], + "com.google.code.gson:gson": [ + "com.google.gson", + "com.google.gson.annotations", + "com.google.gson.internal", + "com.google.gson.internal.bind", + "com.google.gson.internal.bind.util", + "com.google.gson.internal.reflect", + "com.google.gson.internal.sql", + "com.google.gson.reflect", + "com.google.gson.stream" + ], + "com.google.errorprone:error_prone_annotations": [ + "com.google.errorprone.annotations", + "com.google.errorprone.annotations.concurrent" + ], + "com.google.guava:failureaccess": [ + "com.google.common.util.concurrent.internal" + ], + "com.google.guava:guava": [ + "com.google.common.annotations", + "com.google.common.base", + "com.google.common.base.internal", + "com.google.common.cache", + "com.google.common.collect", + "com.google.common.escape", + "com.google.common.eventbus", + "com.google.common.graph", + "com.google.common.hash", + "com.google.common.html", + "com.google.common.io", + "com.google.common.math", + "com.google.common.net", + "com.google.common.primitives", + "com.google.common.reflect", + "com.google.common.util.concurrent", + "com.google.common.xml", + "com.google.thirdparty.publicsuffix" + ], + "com.google.j2objc:j2objc-annotations": [ + "com.google.j2objc.annotations" + ], + "org.checkerframework:checker-qual": [ + "org.checkerframework.checker.builder.qual", + "org.checkerframework.checker.calledmethods.qual", + "org.checkerframework.checker.compilermsgs.qual", + "org.checkerframework.checker.fenum.qual", + "org.checkerframework.checker.formatter.qual", + "org.checkerframework.checker.guieffect.qual", + "org.checkerframework.checker.i18n.qual", + "org.checkerframework.checker.i18nformatter.qual", + "org.checkerframework.checker.index.qual", + "org.checkerframework.checker.initialization.qual", + "org.checkerframework.checker.interning.qual", + "org.checkerframework.checker.lock.qual", + "org.checkerframework.checker.mustcall.qual", + "org.checkerframework.checker.nullness.qual", + "org.checkerframework.checker.optional.qual", + "org.checkerframework.checker.propkey.qual", + "org.checkerframework.checker.regex.qual", + "org.checkerframework.checker.signature.qual", + "org.checkerframework.checker.signedness.qual", + "org.checkerframework.checker.tainting.qual", + "org.checkerframework.checker.units.qual", + "org.checkerframework.common.aliasing.qual", + "org.checkerframework.common.initializedfields.qual", + "org.checkerframework.common.reflection.qual", + "org.checkerframework.common.returnsreceiver.qual", + "org.checkerframework.common.subtyping.qual", + "org.checkerframework.common.util.count.report.qual", + "org.checkerframework.common.value.qual", + "org.checkerframework.dataflow.qual", + "org.checkerframework.framework.qual" + ] + }, + "repositories": { + "https://repo1.maven.org/maven2/": [ + "com.google.code.findbugs:jsr305", + "com.google.code.gson:gson", + "com.google.errorprone:error_prone_annotations", + "com.google.guava:failureaccess", + "com.google.guava:guava", + "com.google.guava:listenablefuture", + "com.google.j2objc:j2objc-annotations", + "org.checkerframework:checker-qual" + ] + }, + "services": {}, + "version": "2" +} diff --git a/java/gazelle/testdata/kt_destructure_deps/src/main/java/com/example/consumer/BUILD.in b/java/gazelle/testdata/kt_destructure_deps/src/main/java/com/example/consumer/BUILD.in new file mode 100644 index 00000000..e69de29b diff --git a/java/gazelle/testdata/kt_destructure_deps/src/main/java/com/example/consumer/BUILD.out b/java/gazelle/testdata/kt_destructure_deps/src/main/java/com/example/consumer/BUILD.out new file mode 100644 index 00000000..960b7154 --- /dev/null +++ b/java/gazelle/testdata/kt_destructure_deps/src/main/java/com/example/consumer/BUILD.out @@ -0,0 +1,8 @@ +load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") + +kt_jvm_library( + name = "consumer", + srcs = ["Consumer.kt"], + visibility = ["//:__subpackages__"], + deps = ["//src/main/java/com/example/provider"], +) diff --git a/java/gazelle/testdata/kt_destructure_deps/src/main/java/com/example/consumer/Consumer.kt b/java/gazelle/testdata/kt_destructure_deps/src/main/java/com/example/consumer/Consumer.kt new file mode 100644 index 00000000..c9312aad --- /dev/null +++ b/java/gazelle/testdata/kt_destructure_deps/src/main/java/com/example/consumer/Consumer.kt @@ -0,0 +1,30 @@ +package com.example.consumer + +import com.example.provider.CustomContainer + +/** + * Consumer that uses destructuring on CustomContainer. This should automatically get transitive + * dependencies from the provider's componentN() functions through the exports mechanism. + */ +class Consumer { + + fun useDestructuring(): String { + val container = + CustomContainer(mapOf("first" to "hello", "second" to "world", "third" to "test")) + + // Destructure the container - this should work because + // the provider exports its componentN() dependencies + val (first, second, third) = container + + return "First: $first, Second: $second, Third: $third" + } + + fun usePartialDestructuring(): String { + val container = CustomContainer(mapOf("first" to mapOf("key" to "value"), "second" to "simple")) + + // Partial destructuring - only uses component1() and component2() + val (jsonData, paddedData) = container + + return "JSON: $jsonData, Padded: $paddedData" + } +} diff --git a/java/gazelle/testdata/kt_destructure_deps/src/main/java/com/example/provider/BUILD.in b/java/gazelle/testdata/kt_destructure_deps/src/main/java/com/example/provider/BUILD.in new file mode 100644 index 00000000..e69de29b diff --git a/java/gazelle/testdata/kt_destructure_deps/src/main/java/com/example/provider/BUILD.out b/java/gazelle/testdata/kt_destructure_deps/src/main/java/com/example/provider/BUILD.out new file mode 100644 index 00000000..8ec1303b --- /dev/null +++ b/java/gazelle/testdata/kt_destructure_deps/src/main/java/com/example/provider/BUILD.out @@ -0,0 +1,15 @@ +load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") + +kt_jvm_library( + name = "provider", + srcs = ["CustomContainer.kt"], + visibility = ["//:__subpackages__"], + exports = [ + "@maven//:com_google_code_gson_gson", + "@maven//:com_google_guava_guava", + ], + deps = [ + "@maven//:com_google_code_gson_gson", + "@maven//:com_google_guava_guava", + ], +) diff --git a/java/gazelle/testdata/kt_destructure_deps/src/main/java/com/example/provider/CustomContainer.kt b/java/gazelle/testdata/kt_destructure_deps/src/main/java/com/example/provider/CustomContainer.kt new file mode 100644 index 00000000..b85c248a --- /dev/null +++ b/java/gazelle/testdata/kt_destructure_deps/src/main/java/com/example/provider/CustomContainer.kt @@ -0,0 +1,36 @@ +package com.example.provider + +import com.google.common.base.Strings +import com.google.gson.Gson + +/** + * Custom class with componentN() functions that use external dependencies. Any code that + * destructures this class should transitively depend on: + * - com.google.gson (for Gson) + * - com.google.guava (for Strings) + */ +class CustomContainer(private val data: Map) { + + /** component1() function that uses Gson. Destructuring should add Gson to exports. */ + operator fun component1(): String { + val gson = Gson() + return gson.toJson(data["first"] ?: "") + } + + /** component2() function that uses Guava Strings. Destructuring should add Guava to exports. */ + operator fun component2(): String { + val value = data["second"]?.toString() ?: "" + return Strings.padEnd(value, 20, '-') + } + + /** component3() function that uses both dependencies. */ + operator fun component3(): String { + val gson = Gson() + val rawValue = data["third"]?.toString() ?: "" + val paddedValue = Strings.padStart(rawValue, 15, '*') + return gson.toJson(mapOf("padded" to paddedValue)) + } + + /** Regular method for comparison - should not affect destructuring dependencies. */ + fun getSize(): Int = data.size +} diff --git a/java/gazelle/testdata/kt_extension/BUILD.in b/java/gazelle/testdata/kt_extension/BUILD.in new file mode 100644 index 00000000..feccf582 --- /dev/null +++ b/java/gazelle/testdata/kt_extension/BUILD.in @@ -0,0 +1 @@ +# gazelle:jvm_kotlin_enabled true diff --git a/java/gazelle/testdata/kt_extension/BUILD.out b/java/gazelle/testdata/kt_extension/BUILD.out new file mode 100644 index 00000000..feccf582 --- /dev/null +++ b/java/gazelle/testdata/kt_extension/BUILD.out @@ -0,0 +1 @@ +# gazelle:jvm_kotlin_enabled true diff --git a/java/gazelle/testdata/kt_extension/WORKSPACE b/java/gazelle/testdata/kt_extension/WORKSPACE new file mode 100644 index 00000000..974971ec --- /dev/null +++ b/java/gazelle/testdata/kt_extension/WORKSPACE @@ -0,0 +1,19 @@ +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +rules_kotlin_version = "1.9.6" + +rules_kotlin_sha = "3b772976fec7bdcda1d84b9d39b176589424c047eb2175bed09aac630e50af43" + +http_archive( + name = "rules_kotlin", + sha256 = rules_kotlin_sha, + urls = ["https://github.com/bazelbuild/rules_kotlin/releases/download/v%s/rules_kotlin-v%s.tar.gz" % (rules_kotlin_version, rules_kotlin_version)], +) + +load("@rules_kotlin//kotlin:repositories.bzl", "kotlin_repositories") + +kotlin_repositories() + +load("@rules_kotlin//kotlin:core.bzl", "kt_register_toolchains") + +kt_register_toolchains diff --git a/java/gazelle/testdata/kt_extension/maven_install.json b/java/gazelle/testdata/kt_extension/maven_install.json new file mode 100644 index 00000000..12ea79b9 --- /dev/null +++ b/java/gazelle/testdata/kt_extension/maven_install.json @@ -0,0 +1,162 @@ +{ + "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL", + "__INPUT_ARTIFACTS_HASH": 1811698626, + "__RESOLVED_ARTIFACTS_HASH": 1591722038, + "artifacts": { + "com.google.code.findbugs:jsr305": { + "shasums": { + "jar": "766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7" + }, + "version": "3.0.2" + }, + "com.google.code.gson:gson": { + "shasums": { + "jar": "dd0ce1b55a3ed2080cb70f9c655850cda86c206862310009dcb5e5c95265a5e0" + }, + "version": "2.13.2" + }, + "com.google.errorprone:error_prone_annotations": { + "shasums": { + "jar": "a56e782b5b50811ac204073a355a21d915a2107fce13ec711331ad036f660fcc" + }, + "version": "2.41.0" + }, + "com.google.guava:failureaccess": { + "shasums": { + "jar": "8a8f81cf9b359e3f6dfa691a1e776985c061ef2f223c9b2c80753e1b458e8064" + }, + "version": "1.0.2" + }, + "com.google.guava:guava": { + "shasums": { + "jar": "4bf0e2c5af8e4525c96e8fde17a4f7307f97f8478f11c4c8e35a0e3298ae4e90" + }, + "version": "33.3.1-jre" + }, + "com.google.guava:listenablefuture": { + "shasums": { + "jar": "b372a037d4230aa57fbeffdef30fd6123f9c0c2db85d0aced00c91b974f33f99" + }, + "version": "9999.0-empty-to-avoid-conflict-with-guava" + }, + "com.google.j2objc:j2objc-annotations": { + "shasums": { + "jar": "88241573467ddca44ffd4d74aa04c2bbfd11bf7c17e0c342c94c9de7a70a7c64" + }, + "version": "3.0.0" + }, + "org.checkerframework:checker-qual": { + "shasums": { + "jar": "3fbc2e98f05854c3df16df9abaa955b91b15b3ecac33623208ed6424640ef0f6" + }, + "version": "3.43.0" + } + }, + "dependencies": { + "com.google.code.gson:gson": [ + "com.google.errorprone:error_prone_annotations" + ], + "com.google.guava:guava": [ + "com.google.code.findbugs:jsr305", + "com.google.errorprone:error_prone_annotations", + "com.google.guava:failureaccess", + "com.google.guava:listenablefuture", + "com.google.j2objc:j2objc-annotations", + "org.checkerframework:checker-qual" + ] + }, + "packages": { + "com.google.code.findbugs:jsr305": [ + "javax.annotation", + "javax.annotation.concurrent", + "javax.annotation.meta" + ], + "com.google.code.gson:gson": [ + "com.google.gson", + "com.google.gson.annotations", + "com.google.gson.internal", + "com.google.gson.internal.bind", + "com.google.gson.internal.bind.util", + "com.google.gson.internal.reflect", + "com.google.gson.internal.sql", + "com.google.gson.reflect", + "com.google.gson.stream" + ], + "com.google.errorprone:error_prone_annotations": [ + "com.google.errorprone.annotations", + "com.google.errorprone.annotations.concurrent" + ], + "com.google.guava:failureaccess": [ + "com.google.common.util.concurrent.internal" + ], + "com.google.guava:guava": [ + "com.google.common.annotations", + "com.google.common.base", + "com.google.common.base.internal", + "com.google.common.cache", + "com.google.common.collect", + "com.google.common.escape", + "com.google.common.eventbus", + "com.google.common.graph", + "com.google.common.hash", + "com.google.common.html", + "com.google.common.io", + "com.google.common.math", + "com.google.common.net", + "com.google.common.primitives", + "com.google.common.reflect", + "com.google.common.util.concurrent", + "com.google.common.xml", + "com.google.thirdparty.publicsuffix" + ], + "com.google.j2objc:j2objc-annotations": [ + "com.google.j2objc.annotations" + ], + "org.checkerframework:checker-qual": [ + "org.checkerframework.checker.builder.qual", + "org.checkerframework.checker.calledmethods.qual", + "org.checkerframework.checker.compilermsgs.qual", + "org.checkerframework.checker.fenum.qual", + "org.checkerframework.checker.formatter.qual", + "org.checkerframework.checker.guieffect.qual", + "org.checkerframework.checker.i18n.qual", + "org.checkerframework.checker.i18nformatter.qual", + "org.checkerframework.checker.index.qual", + "org.checkerframework.checker.initialization.qual", + "org.checkerframework.checker.interning.qual", + "org.checkerframework.checker.lock.qual", + "org.checkerframework.checker.mustcall.qual", + "org.checkerframework.checker.nullness.qual", + "org.checkerframework.checker.optional.qual", + "org.checkerframework.checker.propkey.qual", + "org.checkerframework.checker.regex.qual", + "org.checkerframework.checker.signature.qual", + "org.checkerframework.checker.signedness.qual", + "org.checkerframework.checker.tainting.qual", + "org.checkerframework.checker.units.qual", + "org.checkerframework.common.aliasing.qual", + "org.checkerframework.common.initializedfields.qual", + "org.checkerframework.common.reflection.qual", + "org.checkerframework.common.returnsreceiver.qual", + "org.checkerframework.common.subtyping.qual", + "org.checkerframework.common.util.count.report.qual", + "org.checkerframework.common.value.qual", + "org.checkerframework.dataflow.qual", + "org.checkerframework.framework.qual" + ] + }, + "repositories": { + "https://repo1.maven.org/maven2/": [ + "com.google.code.findbugs:jsr305", + "com.google.code.gson:gson", + "com.google.errorprone:error_prone_annotations", + "com.google.guava:failureaccess", + "com.google.guava:guava", + "com.google.guava:listenablefuture", + "com.google.j2objc:j2objc-annotations", + "org.checkerframework:checker-qual" + ] + }, + "services": {}, + "version": "2" +} diff --git a/java/gazelle/testdata/kt_extension/src/main/java/com/example/consumer/BUILD.in b/java/gazelle/testdata/kt_extension/src/main/java/com/example/consumer/BUILD.in new file mode 100644 index 00000000..e69de29b diff --git a/java/gazelle/testdata/kt_extension/src/main/java/com/example/consumer/BUILD.out b/java/gazelle/testdata/kt_extension/src/main/java/com/example/consumer/BUILD.out new file mode 100644 index 00000000..b65e7858 --- /dev/null +++ b/java/gazelle/testdata/kt_extension/src/main/java/com/example/consumer/BUILD.out @@ -0,0 +1,8 @@ +load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") + +kt_jvm_library( + name = "consumer", + srcs = ["Consumer.kt"], + visibility = ["//:__subpackages__"], + deps = ["//src/main/java/com/example/extension"], +) diff --git a/java/gazelle/testdata/kt_extension/src/main/java/com/example/consumer/Consumer.kt b/java/gazelle/testdata/kt_extension/src/main/java/com/example/consumer/Consumer.kt new file mode 100644 index 00000000..5477be47 --- /dev/null +++ b/java/gazelle/testdata/kt_extension/src/main/java/com/example/consumer/Consumer.kt @@ -0,0 +1,25 @@ +package com.example.consumer + +import com.example.extension.parseToJsonObject +import com.example.extension.processJsonData +import com.example.extension.reverseString + +/** + * Consumer that uses extension functions from another package. This should get transitive + * dependencies from the extension functions. + */ +class Consumer { + fun useExtensions() { + // This should pull in Gson and JsonObject dependencies from the extension function + val jsonObj = "{" test ": " value "}".parseToJsonObject() + println("Parsed: ${jsonObj}") + + // This should pull in multiple Gson class dependencies + val processed = "hello".processJsonData() + println("Processed: $processed") + + // This should not add any extra dependencies + val reversed = "world".reverseString() + println("Reversed: $reversed") + } +} diff --git a/java/gazelle/testdata/kt_extension/src/main/java/com/example/extension/BUILD.in b/java/gazelle/testdata/kt_extension/src/main/java/com/example/extension/BUILD.in new file mode 100644 index 00000000..e69de29b diff --git a/java/gazelle/testdata/kt_extension/src/main/java/com/example/extension/BUILD.out b/java/gazelle/testdata/kt_extension/src/main/java/com/example/extension/BUILD.out new file mode 100644 index 00000000..fae11607 --- /dev/null +++ b/java/gazelle/testdata/kt_extension/src/main/java/com/example/extension/BUILD.out @@ -0,0 +1,9 @@ +load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") + +kt_jvm_library( + name = "extension", + srcs = ["ExtensionUtils.kt"], + visibility = ["//:__subpackages__"], + exports = ["@maven//:com_google_code_gson_gson"], + deps = ["@maven//:com_google_code_gson_gson"], +) diff --git a/java/gazelle/testdata/kt_extension/src/main/java/com/example/extension/ExtensionUtils.kt b/java/gazelle/testdata/kt_extension/src/main/java/com/example/extension/ExtensionUtils.kt new file mode 100644 index 00000000..08c805bc --- /dev/null +++ b/java/gazelle/testdata/kt_extension/src/main/java/com/example/extension/ExtensionUtils.kt @@ -0,0 +1,34 @@ +package com.example.extension + +import com.google.gson.Gson +import com.google.gson.JsonObject + +/** + * Simple extension function that uses Gson library. This should cause any code that calls this + * function to depend on Gson classes. + */ +fun String.parseToJsonObject(): JsonObject { + val gson = Gson() + return gson.fromJson(this, JsonObject::class.java) +} + +/** Extension function that uses multiple Gson classes. */ +fun String.processJsonData(): String { + val gson = Gson() + val jsonObj = JsonObject() + jsonObj.addProperty("input", this) + return gson.toJson(jsonObj) +} + +/** + * Simple extension function with no external dependencies. This should not add any extra + * dependencies to calling code. + */ +fun String.reverseString(): String { + return this.reversed() +} + +/** Regular function for comparison - should not affect dependencies. */ +fun regularFunction(data: String): String { + return data.uppercase() +} diff --git a/java/gazelle/testdata/kt_inline/BUILD.in b/java/gazelle/testdata/kt_inline/BUILD.in new file mode 100644 index 00000000..feccf582 --- /dev/null +++ b/java/gazelle/testdata/kt_inline/BUILD.in @@ -0,0 +1 @@ +# gazelle:jvm_kotlin_enabled true diff --git a/java/gazelle/testdata/kt_inline/BUILD.out b/java/gazelle/testdata/kt_inline/BUILD.out new file mode 100644 index 00000000..feccf582 --- /dev/null +++ b/java/gazelle/testdata/kt_inline/BUILD.out @@ -0,0 +1 @@ +# gazelle:jvm_kotlin_enabled true diff --git a/java/gazelle/testdata/kt_inline/WORKSPACE b/java/gazelle/testdata/kt_inline/WORKSPACE new file mode 100644 index 00000000..974971ec --- /dev/null +++ b/java/gazelle/testdata/kt_inline/WORKSPACE @@ -0,0 +1,19 @@ +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +rules_kotlin_version = "1.9.6" + +rules_kotlin_sha = "3b772976fec7bdcda1d84b9d39b176589424c047eb2175bed09aac630e50af43" + +http_archive( + name = "rules_kotlin", + sha256 = rules_kotlin_sha, + urls = ["https://github.com/bazelbuild/rules_kotlin/releases/download/v%s/rules_kotlin-v%s.tar.gz" % (rules_kotlin_version, rules_kotlin_version)], +) + +load("@rules_kotlin//kotlin:repositories.bzl", "kotlin_repositories") + +kotlin_repositories() + +load("@rules_kotlin//kotlin:core.bzl", "kt_register_toolchains") + +kt_register_toolchains diff --git a/java/gazelle/testdata/kt_inline/maven_install.json b/java/gazelle/testdata/kt_inline/maven_install.json new file mode 100644 index 00000000..12ea79b9 --- /dev/null +++ b/java/gazelle/testdata/kt_inline/maven_install.json @@ -0,0 +1,162 @@ +{ + "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL", + "__INPUT_ARTIFACTS_HASH": 1811698626, + "__RESOLVED_ARTIFACTS_HASH": 1591722038, + "artifacts": { + "com.google.code.findbugs:jsr305": { + "shasums": { + "jar": "766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7" + }, + "version": "3.0.2" + }, + "com.google.code.gson:gson": { + "shasums": { + "jar": "dd0ce1b55a3ed2080cb70f9c655850cda86c206862310009dcb5e5c95265a5e0" + }, + "version": "2.13.2" + }, + "com.google.errorprone:error_prone_annotations": { + "shasums": { + "jar": "a56e782b5b50811ac204073a355a21d915a2107fce13ec711331ad036f660fcc" + }, + "version": "2.41.0" + }, + "com.google.guava:failureaccess": { + "shasums": { + "jar": "8a8f81cf9b359e3f6dfa691a1e776985c061ef2f223c9b2c80753e1b458e8064" + }, + "version": "1.0.2" + }, + "com.google.guava:guava": { + "shasums": { + "jar": "4bf0e2c5af8e4525c96e8fde17a4f7307f97f8478f11c4c8e35a0e3298ae4e90" + }, + "version": "33.3.1-jre" + }, + "com.google.guava:listenablefuture": { + "shasums": { + "jar": "b372a037d4230aa57fbeffdef30fd6123f9c0c2db85d0aced00c91b974f33f99" + }, + "version": "9999.0-empty-to-avoid-conflict-with-guava" + }, + "com.google.j2objc:j2objc-annotations": { + "shasums": { + "jar": "88241573467ddca44ffd4d74aa04c2bbfd11bf7c17e0c342c94c9de7a70a7c64" + }, + "version": "3.0.0" + }, + "org.checkerframework:checker-qual": { + "shasums": { + "jar": "3fbc2e98f05854c3df16df9abaa955b91b15b3ecac33623208ed6424640ef0f6" + }, + "version": "3.43.0" + } + }, + "dependencies": { + "com.google.code.gson:gson": [ + "com.google.errorprone:error_prone_annotations" + ], + "com.google.guava:guava": [ + "com.google.code.findbugs:jsr305", + "com.google.errorprone:error_prone_annotations", + "com.google.guava:failureaccess", + "com.google.guava:listenablefuture", + "com.google.j2objc:j2objc-annotations", + "org.checkerframework:checker-qual" + ] + }, + "packages": { + "com.google.code.findbugs:jsr305": [ + "javax.annotation", + "javax.annotation.concurrent", + "javax.annotation.meta" + ], + "com.google.code.gson:gson": [ + "com.google.gson", + "com.google.gson.annotations", + "com.google.gson.internal", + "com.google.gson.internal.bind", + "com.google.gson.internal.bind.util", + "com.google.gson.internal.reflect", + "com.google.gson.internal.sql", + "com.google.gson.reflect", + "com.google.gson.stream" + ], + "com.google.errorprone:error_prone_annotations": [ + "com.google.errorprone.annotations", + "com.google.errorprone.annotations.concurrent" + ], + "com.google.guava:failureaccess": [ + "com.google.common.util.concurrent.internal" + ], + "com.google.guava:guava": [ + "com.google.common.annotations", + "com.google.common.base", + "com.google.common.base.internal", + "com.google.common.cache", + "com.google.common.collect", + "com.google.common.escape", + "com.google.common.eventbus", + "com.google.common.graph", + "com.google.common.hash", + "com.google.common.html", + "com.google.common.io", + "com.google.common.math", + "com.google.common.net", + "com.google.common.primitives", + "com.google.common.reflect", + "com.google.common.util.concurrent", + "com.google.common.xml", + "com.google.thirdparty.publicsuffix" + ], + "com.google.j2objc:j2objc-annotations": [ + "com.google.j2objc.annotations" + ], + "org.checkerframework:checker-qual": [ + "org.checkerframework.checker.builder.qual", + "org.checkerframework.checker.calledmethods.qual", + "org.checkerframework.checker.compilermsgs.qual", + "org.checkerframework.checker.fenum.qual", + "org.checkerframework.checker.formatter.qual", + "org.checkerframework.checker.guieffect.qual", + "org.checkerframework.checker.i18n.qual", + "org.checkerframework.checker.i18nformatter.qual", + "org.checkerframework.checker.index.qual", + "org.checkerframework.checker.initialization.qual", + "org.checkerframework.checker.interning.qual", + "org.checkerframework.checker.lock.qual", + "org.checkerframework.checker.mustcall.qual", + "org.checkerframework.checker.nullness.qual", + "org.checkerframework.checker.optional.qual", + "org.checkerframework.checker.propkey.qual", + "org.checkerframework.checker.regex.qual", + "org.checkerframework.checker.signature.qual", + "org.checkerframework.checker.signedness.qual", + "org.checkerframework.checker.tainting.qual", + "org.checkerframework.checker.units.qual", + "org.checkerframework.common.aliasing.qual", + "org.checkerframework.common.initializedfields.qual", + "org.checkerframework.common.reflection.qual", + "org.checkerframework.common.returnsreceiver.qual", + "org.checkerframework.common.subtyping.qual", + "org.checkerframework.common.util.count.report.qual", + "org.checkerframework.common.value.qual", + "org.checkerframework.dataflow.qual", + "org.checkerframework.framework.qual" + ] + }, + "repositories": { + "https://repo1.maven.org/maven2/": [ + "com.google.code.findbugs:jsr305", + "com.google.code.gson:gson", + "com.google.errorprone:error_prone_annotations", + "com.google.guava:failureaccess", + "com.google.guava:guava", + "com.google.guava:listenablefuture", + "com.google.j2objc:j2objc-annotations", + "org.checkerframework:checker-qual" + ] + }, + "services": {}, + "version": "2" +} diff --git a/java/gazelle/testdata/kt_inline/src/main/java/com/example/inline/BUILD.in b/java/gazelle/testdata/kt_inline/src/main/java/com/example/inline/BUILD.in new file mode 100644 index 00000000..e69de29b diff --git a/java/gazelle/testdata/kt_inline/src/main/java/com/example/inline/BUILD.out b/java/gazelle/testdata/kt_inline/src/main/java/com/example/inline/BUILD.out new file mode 100644 index 00000000..4de0784a --- /dev/null +++ b/java/gazelle/testdata/kt_inline/src/main/java/com/example/inline/BUILD.out @@ -0,0 +1,7 @@ +load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") + +kt_jvm_library( + name = "inline", + srcs = ["InlineUtils.kt"], + visibility = ["//:__subpackages__"], +) diff --git a/java/gazelle/testdata/kt_inline/src/main/java/com/example/inline/InlineUtils.kt b/java/gazelle/testdata/kt_inline/src/main/java/com/example/inline/InlineUtils.kt new file mode 100644 index 00000000..65e208ca --- /dev/null +++ b/java/gazelle/testdata/kt_inline/src/main/java/com/example/inline/InlineUtils.kt @@ -0,0 +1,25 @@ +package com.example.inline + +import java.util.ArrayList + +/** + * Simple inline function that uses Java stdlib. This should cause any code that calls this function + * to depend on java.util. + */ +inline fun withList(block: (ArrayList) -> Unit) { + val list = ArrayList() + block(list) +} + +/** + * Simple inline function with no external dependencies. This should not add any extra dependencies + * to calling code. + */ +inline fun simpleAdd(x: Int, y: Int): Int { + return x + y +} + +/** Regular function for comparison - should not affect dependencies. */ +fun regularFunction(data: String): String { + return data.uppercase() +} diff --git a/java/gazelle/testdata/kt_inline_deps/BUILD.in b/java/gazelle/testdata/kt_inline_deps/BUILD.in new file mode 100644 index 00000000..feccf582 --- /dev/null +++ b/java/gazelle/testdata/kt_inline_deps/BUILD.in @@ -0,0 +1 @@ +# gazelle:jvm_kotlin_enabled true diff --git a/java/gazelle/testdata/kt_inline_deps/BUILD.out b/java/gazelle/testdata/kt_inline_deps/BUILD.out new file mode 100644 index 00000000..feccf582 --- /dev/null +++ b/java/gazelle/testdata/kt_inline_deps/BUILD.out @@ -0,0 +1 @@ +# gazelle:jvm_kotlin_enabled true diff --git a/java/gazelle/testdata/kt_inline_deps/WORKSPACE b/java/gazelle/testdata/kt_inline_deps/WORKSPACE new file mode 100644 index 00000000..8292fe3f --- /dev/null +++ b/java/gazelle/testdata/kt_inline_deps/WORKSPACE @@ -0,0 +1,25 @@ +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive", "http_jar") + +rules_kotlin_version = "1.9.6" + +rules_kotlin_sha = "3b772976fec7bdcda1d84b9d39b176589424c047eb2175bed09aac630e50af43" + +http_archive( + name = "rules_kotlin", + sha256 = rules_kotlin_sha, + urls = ["https://github.com/bazelbuild/rules_kotlin/releases/download/v%s/rules_kotlin-v%s.tar.gz" % (rules_kotlin_version, rules_kotlin_version)], +) + +load("@rules_kotlin//kotlin:repositories.bzl", "kotlin_repositories") + +kotlin_repositories() + +load("@rules_kotlin//kotlin:core.bzl", "kt_register_toolchains") + +kt_register_toolchains() + +http_jar( + name = "gson", + sha256 = "cb91f2ac5a2e3b0d6b7e8e5c2b7b7b1e1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b", + url = "https://repo1.maven.org/maven2/com/google/code/gson/gson/2.8.9/gson-2.8.9.jar", +) diff --git a/java/gazelle/testdata/kt_inline_deps/maven_install.json b/java/gazelle/testdata/kt_inline_deps/maven_install.json new file mode 100644 index 00000000..12ea79b9 --- /dev/null +++ b/java/gazelle/testdata/kt_inline_deps/maven_install.json @@ -0,0 +1,162 @@ +{ + "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL", + "__INPUT_ARTIFACTS_HASH": 1811698626, + "__RESOLVED_ARTIFACTS_HASH": 1591722038, + "artifacts": { + "com.google.code.findbugs:jsr305": { + "shasums": { + "jar": "766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7" + }, + "version": "3.0.2" + }, + "com.google.code.gson:gson": { + "shasums": { + "jar": "dd0ce1b55a3ed2080cb70f9c655850cda86c206862310009dcb5e5c95265a5e0" + }, + "version": "2.13.2" + }, + "com.google.errorprone:error_prone_annotations": { + "shasums": { + "jar": "a56e782b5b50811ac204073a355a21d915a2107fce13ec711331ad036f660fcc" + }, + "version": "2.41.0" + }, + "com.google.guava:failureaccess": { + "shasums": { + "jar": "8a8f81cf9b359e3f6dfa691a1e776985c061ef2f223c9b2c80753e1b458e8064" + }, + "version": "1.0.2" + }, + "com.google.guava:guava": { + "shasums": { + "jar": "4bf0e2c5af8e4525c96e8fde17a4f7307f97f8478f11c4c8e35a0e3298ae4e90" + }, + "version": "33.3.1-jre" + }, + "com.google.guava:listenablefuture": { + "shasums": { + "jar": "b372a037d4230aa57fbeffdef30fd6123f9c0c2db85d0aced00c91b974f33f99" + }, + "version": "9999.0-empty-to-avoid-conflict-with-guava" + }, + "com.google.j2objc:j2objc-annotations": { + "shasums": { + "jar": "88241573467ddca44ffd4d74aa04c2bbfd11bf7c17e0c342c94c9de7a70a7c64" + }, + "version": "3.0.0" + }, + "org.checkerframework:checker-qual": { + "shasums": { + "jar": "3fbc2e98f05854c3df16df9abaa955b91b15b3ecac33623208ed6424640ef0f6" + }, + "version": "3.43.0" + } + }, + "dependencies": { + "com.google.code.gson:gson": [ + "com.google.errorprone:error_prone_annotations" + ], + "com.google.guava:guava": [ + "com.google.code.findbugs:jsr305", + "com.google.errorprone:error_prone_annotations", + "com.google.guava:failureaccess", + "com.google.guava:listenablefuture", + "com.google.j2objc:j2objc-annotations", + "org.checkerframework:checker-qual" + ] + }, + "packages": { + "com.google.code.findbugs:jsr305": [ + "javax.annotation", + "javax.annotation.concurrent", + "javax.annotation.meta" + ], + "com.google.code.gson:gson": [ + "com.google.gson", + "com.google.gson.annotations", + "com.google.gson.internal", + "com.google.gson.internal.bind", + "com.google.gson.internal.bind.util", + "com.google.gson.internal.reflect", + "com.google.gson.internal.sql", + "com.google.gson.reflect", + "com.google.gson.stream" + ], + "com.google.errorprone:error_prone_annotations": [ + "com.google.errorprone.annotations", + "com.google.errorprone.annotations.concurrent" + ], + "com.google.guava:failureaccess": [ + "com.google.common.util.concurrent.internal" + ], + "com.google.guava:guava": [ + "com.google.common.annotations", + "com.google.common.base", + "com.google.common.base.internal", + "com.google.common.cache", + "com.google.common.collect", + "com.google.common.escape", + "com.google.common.eventbus", + "com.google.common.graph", + "com.google.common.hash", + "com.google.common.html", + "com.google.common.io", + "com.google.common.math", + "com.google.common.net", + "com.google.common.primitives", + "com.google.common.reflect", + "com.google.common.util.concurrent", + "com.google.common.xml", + "com.google.thirdparty.publicsuffix" + ], + "com.google.j2objc:j2objc-annotations": [ + "com.google.j2objc.annotations" + ], + "org.checkerframework:checker-qual": [ + "org.checkerframework.checker.builder.qual", + "org.checkerframework.checker.calledmethods.qual", + "org.checkerframework.checker.compilermsgs.qual", + "org.checkerframework.checker.fenum.qual", + "org.checkerframework.checker.formatter.qual", + "org.checkerframework.checker.guieffect.qual", + "org.checkerframework.checker.i18n.qual", + "org.checkerframework.checker.i18nformatter.qual", + "org.checkerframework.checker.index.qual", + "org.checkerframework.checker.initialization.qual", + "org.checkerframework.checker.interning.qual", + "org.checkerframework.checker.lock.qual", + "org.checkerframework.checker.mustcall.qual", + "org.checkerframework.checker.nullness.qual", + "org.checkerframework.checker.optional.qual", + "org.checkerframework.checker.propkey.qual", + "org.checkerframework.checker.regex.qual", + "org.checkerframework.checker.signature.qual", + "org.checkerframework.checker.signedness.qual", + "org.checkerframework.checker.tainting.qual", + "org.checkerframework.checker.units.qual", + "org.checkerframework.common.aliasing.qual", + "org.checkerframework.common.initializedfields.qual", + "org.checkerframework.common.reflection.qual", + "org.checkerframework.common.returnsreceiver.qual", + "org.checkerframework.common.subtyping.qual", + "org.checkerframework.common.util.count.report.qual", + "org.checkerframework.common.value.qual", + "org.checkerframework.dataflow.qual", + "org.checkerframework.framework.qual" + ] + }, + "repositories": { + "https://repo1.maven.org/maven2/": [ + "com.google.code.findbugs:jsr305", + "com.google.code.gson:gson", + "com.google.errorprone:error_prone_annotations", + "com.google.guava:failureaccess", + "com.google.guava:guava", + "com.google.guava:listenablefuture", + "com.google.j2objc:j2objc-annotations", + "org.checkerframework:checker-qual" + ] + }, + "services": {}, + "version": "2" +} diff --git a/java/gazelle/testdata/kt_inline_deps/src/main/java/com/example/consumer/BUILD.in b/java/gazelle/testdata/kt_inline_deps/src/main/java/com/example/consumer/BUILD.in new file mode 100644 index 00000000..e69de29b diff --git a/java/gazelle/testdata/kt_inline_deps/src/main/java/com/example/consumer/BUILD.out b/java/gazelle/testdata/kt_inline_deps/src/main/java/com/example/consumer/BUILD.out new file mode 100644 index 00000000..960b7154 --- /dev/null +++ b/java/gazelle/testdata/kt_inline_deps/src/main/java/com/example/consumer/BUILD.out @@ -0,0 +1,8 @@ +load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") + +kt_jvm_library( + name = "consumer", + srcs = ["Consumer.kt"], + visibility = ["//:__subpackages__"], + deps = ["//src/main/java/com/example/provider"], +) diff --git a/java/gazelle/testdata/kt_inline_deps/src/main/java/com/example/consumer/Consumer.kt b/java/gazelle/testdata/kt_inline_deps/src/main/java/com/example/consumer/Consumer.kt new file mode 100644 index 00000000..ed28d5db --- /dev/null +++ b/java/gazelle/testdata/kt_inline_deps/src/main/java/com/example/consumer/Consumer.kt @@ -0,0 +1,19 @@ +package com.example.consumer + +import com.example.provider.processData +import com.example.provider.regularProcessData + +/** + * Consumer that calls both inline and regular functions. This should: + * - Have transitive dependencies from processData (inline function) + * - NOT have transitive dependencies from regularProcessData (regular function) + */ +class Consumer { + fun useInlineFunction(): String { + return processData("test data") + } + + fun useRegularFunction(): String { + return regularProcessData("test data") + } +} diff --git a/java/gazelle/testdata/kt_inline_deps/src/main/java/com/example/provider/BUILD.in b/java/gazelle/testdata/kt_inline_deps/src/main/java/com/example/provider/BUILD.in new file mode 100644 index 00000000..e69de29b diff --git a/java/gazelle/testdata/kt_inline_deps/src/main/java/com/example/provider/BUILD.out b/java/gazelle/testdata/kt_inline_deps/src/main/java/com/example/provider/BUILD.out new file mode 100644 index 00000000..139f476c --- /dev/null +++ b/java/gazelle/testdata/kt_inline_deps/src/main/java/com/example/provider/BUILD.out @@ -0,0 +1,9 @@ +load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") + +kt_jvm_library( + name = "provider", + srcs = ["InlineProvider.kt"], + visibility = ["//:__subpackages__"], + exports = ["@maven//:com_google_code_gson_gson"], + deps = ["@maven//:com_google_code_gson_gson"], +) diff --git a/java/gazelle/testdata/kt_inline_deps/src/main/java/com/example/provider/InlineProvider.kt b/java/gazelle/testdata/kt_inline_deps/src/main/java/com/example/provider/InlineProvider.kt new file mode 100644 index 00000000..e7f838ec --- /dev/null +++ b/java/gazelle/testdata/kt_inline_deps/src/main/java/com/example/provider/InlineProvider.kt @@ -0,0 +1,27 @@ +package com.example.provider + +import com.google.gson.Gson +import java.util.ArrayList + +/** + * Inline function that uses external dependencies. Any code that calls this function should + * transitively depend on: + * - java.util (for ArrayList) + * - com.google.gson (for Gson) + */ +inline fun processData(data: String): String { + val list = ArrayList() + list.add(data) + + val gson = Gson() + return gson.toJson(list) +} + +/** + * Regular function that also uses external dependencies. This should NOT cause transitive + * dependencies for callers. + */ +fun regularProcessData(data: String): String { + val gson = Gson() + return gson.toJson(data) +} diff --git a/java/gazelle/testdata/kt_inline_multi/BUILD.in b/java/gazelle/testdata/kt_inline_multi/BUILD.in new file mode 100644 index 00000000..feccf582 --- /dev/null +++ b/java/gazelle/testdata/kt_inline_multi/BUILD.in @@ -0,0 +1 @@ +# gazelle:jvm_kotlin_enabled true diff --git a/java/gazelle/testdata/kt_inline_multi/BUILD.out b/java/gazelle/testdata/kt_inline_multi/BUILD.out new file mode 100644 index 00000000..feccf582 --- /dev/null +++ b/java/gazelle/testdata/kt_inline_multi/BUILD.out @@ -0,0 +1 @@ +# gazelle:jvm_kotlin_enabled true diff --git a/java/gazelle/testdata/kt_inline_multi/WORKSPACE b/java/gazelle/testdata/kt_inline_multi/WORKSPACE new file mode 100644 index 00000000..b16824a2 --- /dev/null +++ b/java/gazelle/testdata/kt_inline_multi/WORKSPACE @@ -0,0 +1,19 @@ +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +rules_kotlin_version = "1.9.6" + +rules_kotlin_sha = "3b772976fec7bdcda1d84b9d39b176589424c047eb2175bed09aac630e50af43" + +http_archive( + name = "rules_kotlin", + sha256 = rules_kotlin_sha, + urls = ["https://github.com/bazelbuild/rules_kotlin/releases/download/v%s/rules_kotlin-v%s.tar.gz" % (rules_kotlin_version, rules_kotlin_version)], +) + +load("@rules_kotlin//kotlin:repositories.bzl", "kotlin_repositories") + +kotlin_repositories() + +load("@rules_kotlin//kotlin:core.bzl", "kt_register_toolchains") + +kt_register_toolchains() diff --git a/java/gazelle/testdata/kt_inline_multi/maven_install.json b/java/gazelle/testdata/kt_inline_multi/maven_install.json new file mode 100644 index 00000000..12ea79b9 --- /dev/null +++ b/java/gazelle/testdata/kt_inline_multi/maven_install.json @@ -0,0 +1,162 @@ +{ + "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL", + "__INPUT_ARTIFACTS_HASH": 1811698626, + "__RESOLVED_ARTIFACTS_HASH": 1591722038, + "artifacts": { + "com.google.code.findbugs:jsr305": { + "shasums": { + "jar": "766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7" + }, + "version": "3.0.2" + }, + "com.google.code.gson:gson": { + "shasums": { + "jar": "dd0ce1b55a3ed2080cb70f9c655850cda86c206862310009dcb5e5c95265a5e0" + }, + "version": "2.13.2" + }, + "com.google.errorprone:error_prone_annotations": { + "shasums": { + "jar": "a56e782b5b50811ac204073a355a21d915a2107fce13ec711331ad036f660fcc" + }, + "version": "2.41.0" + }, + "com.google.guava:failureaccess": { + "shasums": { + "jar": "8a8f81cf9b359e3f6dfa691a1e776985c061ef2f223c9b2c80753e1b458e8064" + }, + "version": "1.0.2" + }, + "com.google.guava:guava": { + "shasums": { + "jar": "4bf0e2c5af8e4525c96e8fde17a4f7307f97f8478f11c4c8e35a0e3298ae4e90" + }, + "version": "33.3.1-jre" + }, + "com.google.guava:listenablefuture": { + "shasums": { + "jar": "b372a037d4230aa57fbeffdef30fd6123f9c0c2db85d0aced00c91b974f33f99" + }, + "version": "9999.0-empty-to-avoid-conflict-with-guava" + }, + "com.google.j2objc:j2objc-annotations": { + "shasums": { + "jar": "88241573467ddca44ffd4d74aa04c2bbfd11bf7c17e0c342c94c9de7a70a7c64" + }, + "version": "3.0.0" + }, + "org.checkerframework:checker-qual": { + "shasums": { + "jar": "3fbc2e98f05854c3df16df9abaa955b91b15b3ecac33623208ed6424640ef0f6" + }, + "version": "3.43.0" + } + }, + "dependencies": { + "com.google.code.gson:gson": [ + "com.google.errorprone:error_prone_annotations" + ], + "com.google.guava:guava": [ + "com.google.code.findbugs:jsr305", + "com.google.errorprone:error_prone_annotations", + "com.google.guava:failureaccess", + "com.google.guava:listenablefuture", + "com.google.j2objc:j2objc-annotations", + "org.checkerframework:checker-qual" + ] + }, + "packages": { + "com.google.code.findbugs:jsr305": [ + "javax.annotation", + "javax.annotation.concurrent", + "javax.annotation.meta" + ], + "com.google.code.gson:gson": [ + "com.google.gson", + "com.google.gson.annotations", + "com.google.gson.internal", + "com.google.gson.internal.bind", + "com.google.gson.internal.bind.util", + "com.google.gson.internal.reflect", + "com.google.gson.internal.sql", + "com.google.gson.reflect", + "com.google.gson.stream" + ], + "com.google.errorprone:error_prone_annotations": [ + "com.google.errorprone.annotations", + "com.google.errorprone.annotations.concurrent" + ], + "com.google.guava:failureaccess": [ + "com.google.common.util.concurrent.internal" + ], + "com.google.guava:guava": [ + "com.google.common.annotations", + "com.google.common.base", + "com.google.common.base.internal", + "com.google.common.cache", + "com.google.common.collect", + "com.google.common.escape", + "com.google.common.eventbus", + "com.google.common.graph", + "com.google.common.hash", + "com.google.common.html", + "com.google.common.io", + "com.google.common.math", + "com.google.common.net", + "com.google.common.primitives", + "com.google.common.reflect", + "com.google.common.util.concurrent", + "com.google.common.xml", + "com.google.thirdparty.publicsuffix" + ], + "com.google.j2objc:j2objc-annotations": [ + "com.google.j2objc.annotations" + ], + "org.checkerframework:checker-qual": [ + "org.checkerframework.checker.builder.qual", + "org.checkerframework.checker.calledmethods.qual", + "org.checkerframework.checker.compilermsgs.qual", + "org.checkerframework.checker.fenum.qual", + "org.checkerframework.checker.formatter.qual", + "org.checkerframework.checker.guieffect.qual", + "org.checkerframework.checker.i18n.qual", + "org.checkerframework.checker.i18nformatter.qual", + "org.checkerframework.checker.index.qual", + "org.checkerframework.checker.initialization.qual", + "org.checkerframework.checker.interning.qual", + "org.checkerframework.checker.lock.qual", + "org.checkerframework.checker.mustcall.qual", + "org.checkerframework.checker.nullness.qual", + "org.checkerframework.checker.optional.qual", + "org.checkerframework.checker.propkey.qual", + "org.checkerframework.checker.regex.qual", + "org.checkerframework.checker.signature.qual", + "org.checkerframework.checker.signedness.qual", + "org.checkerframework.checker.tainting.qual", + "org.checkerframework.checker.units.qual", + "org.checkerframework.common.aliasing.qual", + "org.checkerframework.common.initializedfields.qual", + "org.checkerframework.common.reflection.qual", + "org.checkerframework.common.returnsreceiver.qual", + "org.checkerframework.common.subtyping.qual", + "org.checkerframework.common.util.count.report.qual", + "org.checkerframework.common.value.qual", + "org.checkerframework.dataflow.qual", + "org.checkerframework.framework.qual" + ] + }, + "repositories": { + "https://repo1.maven.org/maven2/": [ + "com.google.code.findbugs:jsr305", + "com.google.code.gson:gson", + "com.google.errorprone:error_prone_annotations", + "com.google.guava:failureaccess", + "com.google.guava:guava", + "com.google.guava:listenablefuture", + "com.google.j2objc:j2objc-annotations", + "org.checkerframework:checker-qual" + ] + }, + "services": {}, + "version": "2" +} diff --git a/java/gazelle/testdata/kt_inline_multi/src/main/java/com/example/consumer/BUILD.in b/java/gazelle/testdata/kt_inline_multi/src/main/java/com/example/consumer/BUILD.in new file mode 100644 index 00000000..e69de29b diff --git a/java/gazelle/testdata/kt_inline_multi/src/main/java/com/example/consumer/BUILD.out b/java/gazelle/testdata/kt_inline_multi/src/main/java/com/example/consumer/BUILD.out new file mode 100644 index 00000000..960b7154 --- /dev/null +++ b/java/gazelle/testdata/kt_inline_multi/src/main/java/com/example/consumer/BUILD.out @@ -0,0 +1,8 @@ +load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") + +kt_jvm_library( + name = "consumer", + srcs = ["Consumer.kt"], + visibility = ["//:__subpackages__"], + deps = ["//src/main/java/com/example/provider"], +) diff --git a/java/gazelle/testdata/kt_inline_multi/src/main/java/com/example/consumer/Consumer.kt b/java/gazelle/testdata/kt_inline_multi/src/main/java/com/example/consumer/Consumer.kt new file mode 100644 index 00000000..7a2ad3bd --- /dev/null +++ b/java/gazelle/testdata/kt_inline_multi/src/main/java/com/example/consumer/Consumer.kt @@ -0,0 +1,46 @@ +package com.example.consumer + +import com.example.provider.InlineProvider.calculate +import com.example.provider.InlineProvider.createGuavaList +import com.example.provider.InlineProvider.processAndSerialize +import com.example.provider.InlineProvider.regularFunction +import com.example.provider.InlineProvider.toJson +import com.example.provider.InlineProvider.withArrayList + +/** + * Consumer that uses multiple inline functions from the provider. This should get transitive + * dependencies from all the inline functions it uses. + */ +class Consumer { + + fun useArrayListInline(): String { + val result = + withArrayList { list -> + list.add("test") + list.add("data") + } + return result.toString() + } + + fun useJsonInline(): String { + val data = mapOf("key" to "value") + return toJson(data) + } + + fun useGuavaInline(): List { + return createGuavaList("a", "b", "c") + } + + fun useComplexInline(): String { + val data = listOf("item1", "item2", "item3") + return processAndSerialize(data) + } + + fun useSimpleInline(): Int { + return calculate(5, 10) + } + + fun useRegularFunction(): String { + return regularFunction("test") + } +} diff --git a/java/gazelle/testdata/kt_inline_multi/src/main/java/com/example/provider/BUILD.in b/java/gazelle/testdata/kt_inline_multi/src/main/java/com/example/provider/BUILD.in new file mode 100644 index 00000000..e69de29b diff --git a/java/gazelle/testdata/kt_inline_multi/src/main/java/com/example/provider/BUILD.out b/java/gazelle/testdata/kt_inline_multi/src/main/java/com/example/provider/BUILD.out new file mode 100644 index 00000000..c5b18486 --- /dev/null +++ b/java/gazelle/testdata/kt_inline_multi/src/main/java/com/example/provider/BUILD.out @@ -0,0 +1,15 @@ +load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") + +kt_jvm_library( + name = "provider", + srcs = ["InlineProvider.kt"], + visibility = ["//:__subpackages__"], + exports = [ + "@maven//:com_google_code_gson_gson", + "@maven//:com_google_guava_guava", + ], + deps = [ + "@maven//:com_google_code_gson_gson", + "@maven//:com_google_guava_guava", + ], +) diff --git a/java/gazelle/testdata/kt_inline_multi/src/main/java/com/example/provider/InlineProvider.kt b/java/gazelle/testdata/kt_inline_multi/src/main/java/com/example/provider/InlineProvider.kt new file mode 100644 index 00000000..25378130 --- /dev/null +++ b/java/gazelle/testdata/kt_inline_multi/src/main/java/com/example/provider/InlineProvider.kt @@ -0,0 +1,49 @@ +package com.example.provider + +import com.google.common.collect.Lists +import com.google.gson.Gson +import java.util.ArrayList + +/** + * Multiple inline functions with different dependency patterns. This tests the system's ability to + * handle multiple inline functions in the same file with different transitive dependencies. + */ +object InlineProvider { + + /** Inline function that uses Java stdlib. */ + inline fun withArrayList(block: (ArrayList) -> Unit): ArrayList { + val list = ArrayList() + block(list) + return list + } + + /** Inline function that uses Gson for JSON processing. */ + inline fun toJson(data: Any): String { + val gson = Gson() + return gson.toJson(data) + } + + /** Inline function that uses Guava collections. */ + inline fun createGuavaList(vararg items: T): List { + return Lists.newArrayList(*items) + } + + /** Inline function that combines multiple dependencies. */ + inline fun processAndSerialize(data: List): String { + val arrayList = ArrayList(data) + val guavaList = Lists.newArrayList(arrayList) + val gson = Gson() + return gson.toJson(guavaList) + } + + /** Simple inline function with no external dependencies. */ + inline fun calculate(x: Int, y: Int): Int { + return x * y + 42 + } + + /** Regular function - should not affect transitive dependencies. */ + fun regularFunction(input: String): String { + val gson = Gson() + return gson.toJson(input) + } +} diff --git a/java/gazelle/testdata/kt_inline_trans/BUILD.in b/java/gazelle/testdata/kt_inline_trans/BUILD.in new file mode 100644 index 00000000..feccf582 --- /dev/null +++ b/java/gazelle/testdata/kt_inline_trans/BUILD.in @@ -0,0 +1 @@ +# gazelle:jvm_kotlin_enabled true diff --git a/java/gazelle/testdata/kt_inline_trans/BUILD.out b/java/gazelle/testdata/kt_inline_trans/BUILD.out new file mode 100644 index 00000000..feccf582 --- /dev/null +++ b/java/gazelle/testdata/kt_inline_trans/BUILD.out @@ -0,0 +1 @@ +# gazelle:jvm_kotlin_enabled true diff --git a/java/gazelle/testdata/kt_inline_trans/WORKSPACE b/java/gazelle/testdata/kt_inline_trans/WORKSPACE new file mode 100644 index 00000000..8292fe3f --- /dev/null +++ b/java/gazelle/testdata/kt_inline_trans/WORKSPACE @@ -0,0 +1,25 @@ +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive", "http_jar") + +rules_kotlin_version = "1.9.6" + +rules_kotlin_sha = "3b772976fec7bdcda1d84b9d39b176589424c047eb2175bed09aac630e50af43" + +http_archive( + name = "rules_kotlin", + sha256 = rules_kotlin_sha, + urls = ["https://github.com/bazelbuild/rules_kotlin/releases/download/v%s/rules_kotlin-v%s.tar.gz" % (rules_kotlin_version, rules_kotlin_version)], +) + +load("@rules_kotlin//kotlin:repositories.bzl", "kotlin_repositories") + +kotlin_repositories() + +load("@rules_kotlin//kotlin:core.bzl", "kt_register_toolchains") + +kt_register_toolchains() + +http_jar( + name = "gson", + sha256 = "cb91f2ac5a2e3b0d6b7e8e5c2b7b7b1e1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b", + url = "https://repo1.maven.org/maven2/com/google/code/gson/gson/2.8.9/gson-2.8.9.jar", +) diff --git a/java/gazelle/testdata/kt_inline_trans/maven_install.json b/java/gazelle/testdata/kt_inline_trans/maven_install.json new file mode 100644 index 00000000..12ea79b9 --- /dev/null +++ b/java/gazelle/testdata/kt_inline_trans/maven_install.json @@ -0,0 +1,162 @@ +{ + "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL", + "__INPUT_ARTIFACTS_HASH": 1811698626, + "__RESOLVED_ARTIFACTS_HASH": 1591722038, + "artifacts": { + "com.google.code.findbugs:jsr305": { + "shasums": { + "jar": "766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7" + }, + "version": "3.0.2" + }, + "com.google.code.gson:gson": { + "shasums": { + "jar": "dd0ce1b55a3ed2080cb70f9c655850cda86c206862310009dcb5e5c95265a5e0" + }, + "version": "2.13.2" + }, + "com.google.errorprone:error_prone_annotations": { + "shasums": { + "jar": "a56e782b5b50811ac204073a355a21d915a2107fce13ec711331ad036f660fcc" + }, + "version": "2.41.0" + }, + "com.google.guava:failureaccess": { + "shasums": { + "jar": "8a8f81cf9b359e3f6dfa691a1e776985c061ef2f223c9b2c80753e1b458e8064" + }, + "version": "1.0.2" + }, + "com.google.guava:guava": { + "shasums": { + "jar": "4bf0e2c5af8e4525c96e8fde17a4f7307f97f8478f11c4c8e35a0e3298ae4e90" + }, + "version": "33.3.1-jre" + }, + "com.google.guava:listenablefuture": { + "shasums": { + "jar": "b372a037d4230aa57fbeffdef30fd6123f9c0c2db85d0aced00c91b974f33f99" + }, + "version": "9999.0-empty-to-avoid-conflict-with-guava" + }, + "com.google.j2objc:j2objc-annotations": { + "shasums": { + "jar": "88241573467ddca44ffd4d74aa04c2bbfd11bf7c17e0c342c94c9de7a70a7c64" + }, + "version": "3.0.0" + }, + "org.checkerframework:checker-qual": { + "shasums": { + "jar": "3fbc2e98f05854c3df16df9abaa955b91b15b3ecac33623208ed6424640ef0f6" + }, + "version": "3.43.0" + } + }, + "dependencies": { + "com.google.code.gson:gson": [ + "com.google.errorprone:error_prone_annotations" + ], + "com.google.guava:guava": [ + "com.google.code.findbugs:jsr305", + "com.google.errorprone:error_prone_annotations", + "com.google.guava:failureaccess", + "com.google.guava:listenablefuture", + "com.google.j2objc:j2objc-annotations", + "org.checkerframework:checker-qual" + ] + }, + "packages": { + "com.google.code.findbugs:jsr305": [ + "javax.annotation", + "javax.annotation.concurrent", + "javax.annotation.meta" + ], + "com.google.code.gson:gson": [ + "com.google.gson", + "com.google.gson.annotations", + "com.google.gson.internal", + "com.google.gson.internal.bind", + "com.google.gson.internal.bind.util", + "com.google.gson.internal.reflect", + "com.google.gson.internal.sql", + "com.google.gson.reflect", + "com.google.gson.stream" + ], + "com.google.errorprone:error_prone_annotations": [ + "com.google.errorprone.annotations", + "com.google.errorprone.annotations.concurrent" + ], + "com.google.guava:failureaccess": [ + "com.google.common.util.concurrent.internal" + ], + "com.google.guava:guava": [ + "com.google.common.annotations", + "com.google.common.base", + "com.google.common.base.internal", + "com.google.common.cache", + "com.google.common.collect", + "com.google.common.escape", + "com.google.common.eventbus", + "com.google.common.graph", + "com.google.common.hash", + "com.google.common.html", + "com.google.common.io", + "com.google.common.math", + "com.google.common.net", + "com.google.common.primitives", + "com.google.common.reflect", + "com.google.common.util.concurrent", + "com.google.common.xml", + "com.google.thirdparty.publicsuffix" + ], + "com.google.j2objc:j2objc-annotations": [ + "com.google.j2objc.annotations" + ], + "org.checkerframework:checker-qual": [ + "org.checkerframework.checker.builder.qual", + "org.checkerframework.checker.calledmethods.qual", + "org.checkerframework.checker.compilermsgs.qual", + "org.checkerframework.checker.fenum.qual", + "org.checkerframework.checker.formatter.qual", + "org.checkerframework.checker.guieffect.qual", + "org.checkerframework.checker.i18n.qual", + "org.checkerframework.checker.i18nformatter.qual", + "org.checkerframework.checker.index.qual", + "org.checkerframework.checker.initialization.qual", + "org.checkerframework.checker.interning.qual", + "org.checkerframework.checker.lock.qual", + "org.checkerframework.checker.mustcall.qual", + "org.checkerframework.checker.nullness.qual", + "org.checkerframework.checker.optional.qual", + "org.checkerframework.checker.propkey.qual", + "org.checkerframework.checker.regex.qual", + "org.checkerframework.checker.signature.qual", + "org.checkerframework.checker.signedness.qual", + "org.checkerframework.checker.tainting.qual", + "org.checkerframework.checker.units.qual", + "org.checkerframework.common.aliasing.qual", + "org.checkerframework.common.initializedfields.qual", + "org.checkerframework.common.reflection.qual", + "org.checkerframework.common.returnsreceiver.qual", + "org.checkerframework.common.subtyping.qual", + "org.checkerframework.common.util.count.report.qual", + "org.checkerframework.common.value.qual", + "org.checkerframework.dataflow.qual", + "org.checkerframework.framework.qual" + ] + }, + "repositories": { + "https://repo1.maven.org/maven2/": [ + "com.google.code.findbugs:jsr305", + "com.google.code.gson:gson", + "com.google.errorprone:error_prone_annotations", + "com.google.guava:failureaccess", + "com.google.guava:guava", + "com.google.guava:listenablefuture", + "com.google.j2objc:j2objc-annotations", + "org.checkerframework:checker-qual" + ] + }, + "services": {}, + "version": "2" +} diff --git a/java/gazelle/testdata/kt_inline_trans/src/main/java/com/example/level1/Level1.kt b/java/gazelle/testdata/kt_inline_trans/src/main/java/com/example/level1/Level1.kt new file mode 100644 index 00000000..9a3b04cb --- /dev/null +++ b/java/gazelle/testdata/kt_inline_trans/src/main/java/com/example/level1/Level1.kt @@ -0,0 +1,21 @@ +package com.example.level1 + +import java.util.ArrayList + +/** Level 1 inline functions - these use basic dependencies. */ +object Level1 { + + /** Basic inline function that uses ArrayList. */ + inline fun createList(vararg items: T): ArrayList { + val list = ArrayList() + for (item in items) { + list.add(item) + } + return list + } + + /** Simple inline function with no external dependencies. */ + inline fun multiply(x: Int, y: Int): Int { + return x * y + } +} diff --git a/java/gazelle/testdata/kt_inline_trans/src/main/java/com/example/level2/Level2.kt b/java/gazelle/testdata/kt_inline_trans/src/main/java/com/example/level2/Level2.kt new file mode 100644 index 00000000..185361e7 --- /dev/null +++ b/java/gazelle/testdata/kt_inline_trans/src/main/java/com/example/level2/Level2.kt @@ -0,0 +1,38 @@ +package com.example.level2 + +import com.example.level1.Level1.createList +import com.example.level1.Level1.multiply +import com.google.gson.Gson + +/** + * Level 2 inline functions - these call other inline functions AND use their own dependencies. This + * tests transitive inline function calls. + */ +object Level2 { + + /** + * Inline function that calls another inline function (createList) AND uses Gson. This should + * cause transitive dependencies from both: + * 1. Its own Gson usage + * 2. The ArrayList dependency from createList + */ + inline fun createAndSerialize(vararg items: String): String { + val list = createList(*items) // Calls inline function from Level1 + val gson = Gson() // Uses its own dependency + return gson.toJson(list) + } + + /** Inline function that calls multiple other inline functions. */ + inline fun processNumbers(x: Int, y: Int): String { + val result = multiply(x, y) // Calls inline function from Level1 + val list = createList(result) // Calls another inline function from Level1 + val gson = Gson() // Uses its own dependency + return gson.toJson(list) + } + + /** Regular function for comparison. */ + fun regularProcess(data: String): String { + val gson = Gson() + return gson.toJson(data) + } +} diff --git a/java/gazelle/testdata/kt_lib/BUILD.in b/java/gazelle/testdata/kt_lib/BUILD.in new file mode 100644 index 00000000..feccf582 --- /dev/null +++ b/java/gazelle/testdata/kt_lib/BUILD.in @@ -0,0 +1 @@ +# gazelle:jvm_kotlin_enabled true diff --git a/java/gazelle/testdata/kt_lib/BUILD.out b/java/gazelle/testdata/kt_lib/BUILD.out new file mode 100644 index 00000000..feccf582 --- /dev/null +++ b/java/gazelle/testdata/kt_lib/BUILD.out @@ -0,0 +1 @@ +# gazelle:jvm_kotlin_enabled true diff --git a/java/gazelle/testdata/kt_lib/WORKSPACE b/java/gazelle/testdata/kt_lib/WORKSPACE new file mode 100644 index 00000000..b16824a2 --- /dev/null +++ b/java/gazelle/testdata/kt_lib/WORKSPACE @@ -0,0 +1,19 @@ +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +rules_kotlin_version = "1.9.6" + +rules_kotlin_sha = "3b772976fec7bdcda1d84b9d39b176589424c047eb2175bed09aac630e50af43" + +http_archive( + name = "rules_kotlin", + sha256 = rules_kotlin_sha, + urls = ["https://github.com/bazelbuild/rules_kotlin/releases/download/v%s/rules_kotlin-v%s.tar.gz" % (rules_kotlin_version, rules_kotlin_version)], +) + +load("@rules_kotlin//kotlin:repositories.bzl", "kotlin_repositories") + +kotlin_repositories() + +load("@rules_kotlin//kotlin:core.bzl", "kt_register_toolchains") + +kt_register_toolchains() diff --git a/java/gazelle/testdata/kt_lib/maven_install.json b/java/gazelle/testdata/kt_lib/maven_install.json new file mode 100644 index 00000000..6c01c298 --- /dev/null +++ b/java/gazelle/testdata/kt_lib/maven_install.json @@ -0,0 +1 @@ +{"version": "2"} diff --git a/java/gazelle/testdata/kt_lib/src/main/java/com/example/hello/BUILD.in b/java/gazelle/testdata/kt_lib/src/main/java/com/example/hello/BUILD.in new file mode 100644 index 00000000..e69de29b diff --git a/java/gazelle/testdata/kt_lib/src/main/java/com/example/hello/BUILD.out b/java/gazelle/testdata/kt_lib/src/main/java/com/example/hello/BUILD.out new file mode 100644 index 00000000..81e09432 --- /dev/null +++ b/java/gazelle/testdata/kt_lib/src/main/java/com/example/hello/BUILD.out @@ -0,0 +1,7 @@ +load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") + +kt_jvm_library( + name = "hello", + srcs = ["Hello.kt"], + visibility = ["//:__subpackages__"], +) diff --git a/java/gazelle/testdata/kt_lib/src/main/java/com/example/hello/Hello.kt b/java/gazelle/testdata/kt_lib/src/main/java/com/example/hello/Hello.kt new file mode 100644 index 00000000..45f276e4 --- /dev/null +++ b/java/gazelle/testdata/kt_lib/src/main/java/com/example/hello/Hello.kt @@ -0,0 +1,7 @@ +package com.example.hello + +import kotlin.io.println + +fun sayHi() { + println("Hi!") +} diff --git a/java/gazelle/testdata/kt_lib_java_dep/BUILD.in b/java/gazelle/testdata/kt_lib_java_dep/BUILD.in new file mode 100644 index 00000000..feccf582 --- /dev/null +++ b/java/gazelle/testdata/kt_lib_java_dep/BUILD.in @@ -0,0 +1 @@ +# gazelle:jvm_kotlin_enabled true diff --git a/java/gazelle/testdata/kt_lib_java_dep/BUILD.out b/java/gazelle/testdata/kt_lib_java_dep/BUILD.out new file mode 100644 index 00000000..feccf582 --- /dev/null +++ b/java/gazelle/testdata/kt_lib_java_dep/BUILD.out @@ -0,0 +1 @@ +# gazelle:jvm_kotlin_enabled true diff --git a/java/gazelle/testdata/kt_lib_java_dep/WORKSPACE b/java/gazelle/testdata/kt_lib_java_dep/WORKSPACE new file mode 100644 index 00000000..b16824a2 --- /dev/null +++ b/java/gazelle/testdata/kt_lib_java_dep/WORKSPACE @@ -0,0 +1,19 @@ +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +rules_kotlin_version = "1.9.6" + +rules_kotlin_sha = "3b772976fec7bdcda1d84b9d39b176589424c047eb2175bed09aac630e50af43" + +http_archive( + name = "rules_kotlin", + sha256 = rules_kotlin_sha, + urls = ["https://github.com/bazelbuild/rules_kotlin/releases/download/v%s/rules_kotlin-v%s.tar.gz" % (rules_kotlin_version, rules_kotlin_version)], +) + +load("@rules_kotlin//kotlin:repositories.bzl", "kotlin_repositories") + +kotlin_repositories() + +load("@rules_kotlin//kotlin:core.bzl", "kt_register_toolchains") + +kt_register_toolchains() diff --git a/java/gazelle/testdata/kt_lib_java_dep/maven_install.json b/java/gazelle/testdata/kt_lib_java_dep/maven_install.json new file mode 100644 index 00000000..6c01c298 --- /dev/null +++ b/java/gazelle/testdata/kt_lib_java_dep/maven_install.json @@ -0,0 +1 @@ +{"version": "2"} diff --git a/java/gazelle/testdata/kt_lib_java_dep/src/main/java/com/example/hello/BUILD.in b/java/gazelle/testdata/kt_lib_java_dep/src/main/java/com/example/hello/BUILD.in new file mode 100644 index 00000000..e69de29b diff --git a/java/gazelle/testdata/kt_lib_java_dep/src/main/java/com/example/hello/BUILD.out b/java/gazelle/testdata/kt_lib_java_dep/src/main/java/com/example/hello/BUILD.out new file mode 100644 index 00000000..998c5b2e --- /dev/null +++ b/java/gazelle/testdata/kt_lib_java_dep/src/main/java/com/example/hello/BUILD.out @@ -0,0 +1,8 @@ +load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") + +kt_jvm_library( + name = "hello", + srcs = ["Hello.kt"], + visibility = ["//:__subpackages__"], + deps = ["//src/main/java/com/example/hello/greeter"], +) diff --git a/java/gazelle/testdata/kt_lib_java_dep/src/main/java/com/example/hello/Hello.kt b/java/gazelle/testdata/kt_lib_java_dep/src/main/java/com/example/hello/Hello.kt new file mode 100644 index 00000000..e935d9ef --- /dev/null +++ b/java/gazelle/testdata/kt_lib_java_dep/src/main/java/com/example/hello/Hello.kt @@ -0,0 +1,8 @@ +package com.example.hello + +import com.example.hello.greeter.Greeter + +fun sayHi() { + val greeter = Greeter() + println("${greeter.greet()}") +} diff --git a/java/gazelle/testdata/kt_lib_java_dep/src/main/java/com/example/hello/greeter/BUILD.in b/java/gazelle/testdata/kt_lib_java_dep/src/main/java/com/example/hello/greeter/BUILD.in new file mode 100644 index 00000000..e69de29b diff --git a/java/gazelle/testdata/kt_lib_java_dep/src/main/java/com/example/hello/greeter/BUILD.out b/java/gazelle/testdata/kt_lib_java_dep/src/main/java/com/example/hello/greeter/BUILD.out new file mode 100644 index 00000000..c555694b --- /dev/null +++ b/java/gazelle/testdata/kt_lib_java_dep/src/main/java/com/example/hello/greeter/BUILD.out @@ -0,0 +1,7 @@ +load("@rules_java//java:defs.bzl", "java_library") + +java_library( + name = "greeter", + srcs = ["Greeter.java"], + visibility = ["//:__subpackages__"], +) diff --git a/java/gazelle/testdata/kt_lib_java_dep/src/main/java/com/example/hello/greeter/Greeter.java b/java/gazelle/testdata/kt_lib_java_dep/src/main/java/com/example/hello/greeter/Greeter.java new file mode 100644 index 00000000..3f0a9a38 --- /dev/null +++ b/java/gazelle/testdata/kt_lib_java_dep/src/main/java/com/example/hello/greeter/Greeter.java @@ -0,0 +1,7 @@ +package com.example.hello.greeter; + +public class Greeter { + public String greet() { + return "Hello, World!"; + } +} \ No newline at end of file diff --git a/java/gazelle/testdata/kt_lib_java_src/BUILD.in b/java/gazelle/testdata/kt_lib_java_src/BUILD.in new file mode 100644 index 00000000..feccf582 --- /dev/null +++ b/java/gazelle/testdata/kt_lib_java_src/BUILD.in @@ -0,0 +1 @@ +# gazelle:jvm_kotlin_enabled true diff --git a/java/gazelle/testdata/kt_lib_java_src/BUILD.out b/java/gazelle/testdata/kt_lib_java_src/BUILD.out new file mode 100644 index 00000000..feccf582 --- /dev/null +++ b/java/gazelle/testdata/kt_lib_java_src/BUILD.out @@ -0,0 +1 @@ +# gazelle:jvm_kotlin_enabled true diff --git a/java/gazelle/testdata/kt_lib_java_src/WORKSPACE b/java/gazelle/testdata/kt_lib_java_src/WORKSPACE new file mode 100644 index 00000000..b16824a2 --- /dev/null +++ b/java/gazelle/testdata/kt_lib_java_src/WORKSPACE @@ -0,0 +1,19 @@ +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +rules_kotlin_version = "1.9.6" + +rules_kotlin_sha = "3b772976fec7bdcda1d84b9d39b176589424c047eb2175bed09aac630e50af43" + +http_archive( + name = "rules_kotlin", + sha256 = rules_kotlin_sha, + urls = ["https://github.com/bazelbuild/rules_kotlin/releases/download/v%s/rules_kotlin-v%s.tar.gz" % (rules_kotlin_version, rules_kotlin_version)], +) + +load("@rules_kotlin//kotlin:repositories.bzl", "kotlin_repositories") + +kotlin_repositories() + +load("@rules_kotlin//kotlin:core.bzl", "kt_register_toolchains") + +kt_register_toolchains() diff --git a/java/gazelle/testdata/kt_lib_java_src/maven_install.json b/java/gazelle/testdata/kt_lib_java_src/maven_install.json new file mode 100644 index 00000000..6c01c298 --- /dev/null +++ b/java/gazelle/testdata/kt_lib_java_src/maven_install.json @@ -0,0 +1 @@ +{"version": "2"} diff --git a/java/gazelle/testdata/kt_lib_java_src/src/main/java/com/example/hello/BUILD.in b/java/gazelle/testdata/kt_lib_java_src/src/main/java/com/example/hello/BUILD.in new file mode 100644 index 00000000..e69de29b diff --git a/java/gazelle/testdata/kt_lib_java_src/src/main/java/com/example/hello/BUILD.out b/java/gazelle/testdata/kt_lib_java_src/src/main/java/com/example/hello/BUILD.out new file mode 100644 index 00000000..400f45e0 --- /dev/null +++ b/java/gazelle/testdata/kt_lib_java_src/src/main/java/com/example/hello/BUILD.out @@ -0,0 +1,10 @@ +load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") + +kt_jvm_library( + name = "hello", + srcs = [ + "Goodbye.java", + "Hello.kt", + ], + visibility = ["//:__subpackages__"], +) diff --git a/java/gazelle/testdata/kt_lib_java_src/src/main/java/com/example/hello/Goodbye.java b/java/gazelle/testdata/kt_lib_java_src/src/main/java/com/example/hello/Goodbye.java new file mode 100644 index 00000000..befcf703 --- /dev/null +++ b/java/gazelle/testdata/kt_lib_java_src/src/main/java/com/example/hello/Goodbye.java @@ -0,0 +1,7 @@ +package com.example.hello; + +public class Goodbye { + public void goodbye() { + System.out.println("Goodbye!"); + } +} \ No newline at end of file diff --git a/java/gazelle/testdata/kt_lib_java_src/src/main/java/com/example/hello/Hello.kt b/java/gazelle/testdata/kt_lib_java_src/src/main/java/com/example/hello/Hello.kt new file mode 100644 index 00000000..45f276e4 --- /dev/null +++ b/java/gazelle/testdata/kt_lib_java_src/src/main/java/com/example/hello/Hello.kt @@ -0,0 +1,7 @@ +package com.example.hello + +import kotlin.io.println + +fun sayHi() { + println("Hi!") +} diff --git a/java/gazelle/testdata/kt_prop_delegate/BUILD.in b/java/gazelle/testdata/kt_prop_delegate/BUILD.in new file mode 100644 index 00000000..feccf582 --- /dev/null +++ b/java/gazelle/testdata/kt_prop_delegate/BUILD.in @@ -0,0 +1 @@ +# gazelle:jvm_kotlin_enabled true diff --git a/java/gazelle/testdata/kt_prop_delegate/BUILD.out b/java/gazelle/testdata/kt_prop_delegate/BUILD.out new file mode 100644 index 00000000..feccf582 --- /dev/null +++ b/java/gazelle/testdata/kt_prop_delegate/BUILD.out @@ -0,0 +1 @@ +# gazelle:jvm_kotlin_enabled true diff --git a/java/gazelle/testdata/kt_prop_delegate/WORKSPACE b/java/gazelle/testdata/kt_prop_delegate/WORKSPACE new file mode 100644 index 00000000..6a9b9f54 --- /dev/null +++ b/java/gazelle/testdata/kt_prop_delegate/WORKSPACE @@ -0,0 +1,41 @@ +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +rules_kotlin_version = "1.9.6" + +rules_kotlin_sha = "3b772976fec7bdcda1d84b9d39b176589424c047eb2175bed09aac630e50af43" + +http_archive( + name = "rules_kotlin", + sha256 = rules_kotlin_sha, + urls = ["https://github.com/bazelbuild/rules_kotlin/releases/download/v%s/rules_kotlin-v%s.tar.gz" % (rules_kotlin_version, rules_kotlin_version)], +) + +load("@rules_kotlin//kotlin:repositories.bzl", "kotlin_repositories") + +kotlin_repositories() + +load("@rules_kotlin//kotlin:core.bzl", "kt_register_toolchains") + +kt_register_toolchains() + +http_archive( + name = "rules_jvm_external", + sha256 = "23fe83890a77ac1a3ee143e2306ec12da4a845285b14ea13cb0df1b1e23658fe", + strip_prefix = "rules_jvm_external-4.3", + urls = ["https://github.com/bazelbuild/rules_jvm_external/archive/refs/tags/4.3.tar.gz"], +) + +load("@rules_jvm_external//:defs.bzl", "maven_install") + +maven_install( + name = "maven", + artifacts = [ + "com.google.code.gson:gson:2.8.9", + "com.google.guava:guava:30.0-jre", + ], + fetch_sources = True, + repositories = [ + "http://uk.maven.org/maven2", + "https://jcenter.bintray.com/", + ], +) diff --git a/java/gazelle/testdata/kt_prop_delegate/maven_install.json b/java/gazelle/testdata/kt_prop_delegate/maven_install.json new file mode 100644 index 00000000..12ea79b9 --- /dev/null +++ b/java/gazelle/testdata/kt_prop_delegate/maven_install.json @@ -0,0 +1,162 @@ +{ + "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL", + "__INPUT_ARTIFACTS_HASH": 1811698626, + "__RESOLVED_ARTIFACTS_HASH": 1591722038, + "artifacts": { + "com.google.code.findbugs:jsr305": { + "shasums": { + "jar": "766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7" + }, + "version": "3.0.2" + }, + "com.google.code.gson:gson": { + "shasums": { + "jar": "dd0ce1b55a3ed2080cb70f9c655850cda86c206862310009dcb5e5c95265a5e0" + }, + "version": "2.13.2" + }, + "com.google.errorprone:error_prone_annotations": { + "shasums": { + "jar": "a56e782b5b50811ac204073a355a21d915a2107fce13ec711331ad036f660fcc" + }, + "version": "2.41.0" + }, + "com.google.guava:failureaccess": { + "shasums": { + "jar": "8a8f81cf9b359e3f6dfa691a1e776985c061ef2f223c9b2c80753e1b458e8064" + }, + "version": "1.0.2" + }, + "com.google.guava:guava": { + "shasums": { + "jar": "4bf0e2c5af8e4525c96e8fde17a4f7307f97f8478f11c4c8e35a0e3298ae4e90" + }, + "version": "33.3.1-jre" + }, + "com.google.guava:listenablefuture": { + "shasums": { + "jar": "b372a037d4230aa57fbeffdef30fd6123f9c0c2db85d0aced00c91b974f33f99" + }, + "version": "9999.0-empty-to-avoid-conflict-with-guava" + }, + "com.google.j2objc:j2objc-annotations": { + "shasums": { + "jar": "88241573467ddca44ffd4d74aa04c2bbfd11bf7c17e0c342c94c9de7a70a7c64" + }, + "version": "3.0.0" + }, + "org.checkerframework:checker-qual": { + "shasums": { + "jar": "3fbc2e98f05854c3df16df9abaa955b91b15b3ecac33623208ed6424640ef0f6" + }, + "version": "3.43.0" + } + }, + "dependencies": { + "com.google.code.gson:gson": [ + "com.google.errorprone:error_prone_annotations" + ], + "com.google.guava:guava": [ + "com.google.code.findbugs:jsr305", + "com.google.errorprone:error_prone_annotations", + "com.google.guava:failureaccess", + "com.google.guava:listenablefuture", + "com.google.j2objc:j2objc-annotations", + "org.checkerframework:checker-qual" + ] + }, + "packages": { + "com.google.code.findbugs:jsr305": [ + "javax.annotation", + "javax.annotation.concurrent", + "javax.annotation.meta" + ], + "com.google.code.gson:gson": [ + "com.google.gson", + "com.google.gson.annotations", + "com.google.gson.internal", + "com.google.gson.internal.bind", + "com.google.gson.internal.bind.util", + "com.google.gson.internal.reflect", + "com.google.gson.internal.sql", + "com.google.gson.reflect", + "com.google.gson.stream" + ], + "com.google.errorprone:error_prone_annotations": [ + "com.google.errorprone.annotations", + "com.google.errorprone.annotations.concurrent" + ], + "com.google.guava:failureaccess": [ + "com.google.common.util.concurrent.internal" + ], + "com.google.guava:guava": [ + "com.google.common.annotations", + "com.google.common.base", + "com.google.common.base.internal", + "com.google.common.cache", + "com.google.common.collect", + "com.google.common.escape", + "com.google.common.eventbus", + "com.google.common.graph", + "com.google.common.hash", + "com.google.common.html", + "com.google.common.io", + "com.google.common.math", + "com.google.common.net", + "com.google.common.primitives", + "com.google.common.reflect", + "com.google.common.util.concurrent", + "com.google.common.xml", + "com.google.thirdparty.publicsuffix" + ], + "com.google.j2objc:j2objc-annotations": [ + "com.google.j2objc.annotations" + ], + "org.checkerframework:checker-qual": [ + "org.checkerframework.checker.builder.qual", + "org.checkerframework.checker.calledmethods.qual", + "org.checkerframework.checker.compilermsgs.qual", + "org.checkerframework.checker.fenum.qual", + "org.checkerframework.checker.formatter.qual", + "org.checkerframework.checker.guieffect.qual", + "org.checkerframework.checker.i18n.qual", + "org.checkerframework.checker.i18nformatter.qual", + "org.checkerframework.checker.index.qual", + "org.checkerframework.checker.initialization.qual", + "org.checkerframework.checker.interning.qual", + "org.checkerframework.checker.lock.qual", + "org.checkerframework.checker.mustcall.qual", + "org.checkerframework.checker.nullness.qual", + "org.checkerframework.checker.optional.qual", + "org.checkerframework.checker.propkey.qual", + "org.checkerframework.checker.regex.qual", + "org.checkerframework.checker.signature.qual", + "org.checkerframework.checker.signedness.qual", + "org.checkerframework.checker.tainting.qual", + "org.checkerframework.checker.units.qual", + "org.checkerframework.common.aliasing.qual", + "org.checkerframework.common.initializedfields.qual", + "org.checkerframework.common.reflection.qual", + "org.checkerframework.common.returnsreceiver.qual", + "org.checkerframework.common.subtyping.qual", + "org.checkerframework.common.util.count.report.qual", + "org.checkerframework.common.value.qual", + "org.checkerframework.dataflow.qual", + "org.checkerframework.framework.qual" + ] + }, + "repositories": { + "https://repo1.maven.org/maven2/": [ + "com.google.code.findbugs:jsr305", + "com.google.code.gson:gson", + "com.google.errorprone:error_prone_annotations", + "com.google.guava:failureaccess", + "com.google.guava:guava", + "com.google.guava:listenablefuture", + "com.google.j2objc:j2objc-annotations", + "org.checkerframework:checker-qual" + ] + }, + "services": {}, + "version": "2" +} diff --git a/java/gazelle/testdata/kt_prop_delegate/src/main/java/com/example/delegate/BUILD.in b/java/gazelle/testdata/kt_prop_delegate/src/main/java/com/example/delegate/BUILD.in new file mode 100644 index 00000000..e69de29b diff --git a/java/gazelle/testdata/kt_prop_delegate/src/main/java/com/example/delegate/BUILD.out b/java/gazelle/testdata/kt_prop_delegate/src/main/java/com/example/delegate/BUILD.out new file mode 100644 index 00000000..e0c17d4f --- /dev/null +++ b/java/gazelle/testdata/kt_prop_delegate/src/main/java/com/example/delegate/BUILD.out @@ -0,0 +1,7 @@ +load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") + +kt_jvm_library( + name = "delegate", + srcs = ["DelegateUtils.kt"], + visibility = ["//:__subpackages__"], +) diff --git a/java/gazelle/testdata/kt_prop_delegate/src/main/java/com/example/delegate/DelegateUtils.kt b/java/gazelle/testdata/kt_prop_delegate/src/main/java/com/example/delegate/DelegateUtils.kt new file mode 100644 index 00000000..6bb890c9 --- /dev/null +++ b/java/gazelle/testdata/kt_prop_delegate/src/main/java/com/example/delegate/DelegateUtils.kt @@ -0,0 +1,21 @@ +package com.example.delegate + +/** + * Simple property delegates using Kotlin stdlib. These should not add any external dependencies + * since lazy is part of Kotlin stdlib. + */ +class DelegateUtils { + + /** Lazy property delegate - should not add external dependencies. */ + val lazyValue: String by lazy { "computed value" } + + /** Another lazy property with computation. */ + val lazyNumber: Int by lazy { 42 * 2 } + + /** Regular property for comparison - should not affect dependencies. */ + val regularProperty: String = "regular value" + + fun useProperties(): String { + return "Lazy: $lazyValue, Number: $lazyNumber, Regular: $regularProperty" + } +} diff --git a/java/gazelle/testdata/kt_prop_delegate_deps/BUILD.in b/java/gazelle/testdata/kt_prop_delegate_deps/BUILD.in new file mode 100644 index 00000000..feccf582 --- /dev/null +++ b/java/gazelle/testdata/kt_prop_delegate_deps/BUILD.in @@ -0,0 +1 @@ +# gazelle:jvm_kotlin_enabled true diff --git a/java/gazelle/testdata/kt_prop_delegate_deps/BUILD.out b/java/gazelle/testdata/kt_prop_delegate_deps/BUILD.out new file mode 100644 index 00000000..feccf582 --- /dev/null +++ b/java/gazelle/testdata/kt_prop_delegate_deps/BUILD.out @@ -0,0 +1 @@ +# gazelle:jvm_kotlin_enabled true diff --git a/java/gazelle/testdata/kt_prop_delegate_deps/WORKSPACE b/java/gazelle/testdata/kt_prop_delegate_deps/WORKSPACE new file mode 100644 index 00000000..6a9b9f54 --- /dev/null +++ b/java/gazelle/testdata/kt_prop_delegate_deps/WORKSPACE @@ -0,0 +1,41 @@ +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +rules_kotlin_version = "1.9.6" + +rules_kotlin_sha = "3b772976fec7bdcda1d84b9d39b176589424c047eb2175bed09aac630e50af43" + +http_archive( + name = "rules_kotlin", + sha256 = rules_kotlin_sha, + urls = ["https://github.com/bazelbuild/rules_kotlin/releases/download/v%s/rules_kotlin-v%s.tar.gz" % (rules_kotlin_version, rules_kotlin_version)], +) + +load("@rules_kotlin//kotlin:repositories.bzl", "kotlin_repositories") + +kotlin_repositories() + +load("@rules_kotlin//kotlin:core.bzl", "kt_register_toolchains") + +kt_register_toolchains() + +http_archive( + name = "rules_jvm_external", + sha256 = "23fe83890a77ac1a3ee143e2306ec12da4a845285b14ea13cb0df1b1e23658fe", + strip_prefix = "rules_jvm_external-4.3", + urls = ["https://github.com/bazelbuild/rules_jvm_external/archive/refs/tags/4.3.tar.gz"], +) + +load("@rules_jvm_external//:defs.bzl", "maven_install") + +maven_install( + name = "maven", + artifacts = [ + "com.google.code.gson:gson:2.8.9", + "com.google.guava:guava:30.0-jre", + ], + fetch_sources = True, + repositories = [ + "http://uk.maven.org/maven2", + "https://jcenter.bintray.com/", + ], +) diff --git a/java/gazelle/testdata/kt_prop_delegate_deps/maven_install.json b/java/gazelle/testdata/kt_prop_delegate_deps/maven_install.json new file mode 100644 index 00000000..12ea79b9 --- /dev/null +++ b/java/gazelle/testdata/kt_prop_delegate_deps/maven_install.json @@ -0,0 +1,162 @@ +{ + "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL", + "__INPUT_ARTIFACTS_HASH": 1811698626, + "__RESOLVED_ARTIFACTS_HASH": 1591722038, + "artifacts": { + "com.google.code.findbugs:jsr305": { + "shasums": { + "jar": "766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7" + }, + "version": "3.0.2" + }, + "com.google.code.gson:gson": { + "shasums": { + "jar": "dd0ce1b55a3ed2080cb70f9c655850cda86c206862310009dcb5e5c95265a5e0" + }, + "version": "2.13.2" + }, + "com.google.errorprone:error_prone_annotations": { + "shasums": { + "jar": "a56e782b5b50811ac204073a355a21d915a2107fce13ec711331ad036f660fcc" + }, + "version": "2.41.0" + }, + "com.google.guava:failureaccess": { + "shasums": { + "jar": "8a8f81cf9b359e3f6dfa691a1e776985c061ef2f223c9b2c80753e1b458e8064" + }, + "version": "1.0.2" + }, + "com.google.guava:guava": { + "shasums": { + "jar": "4bf0e2c5af8e4525c96e8fde17a4f7307f97f8478f11c4c8e35a0e3298ae4e90" + }, + "version": "33.3.1-jre" + }, + "com.google.guava:listenablefuture": { + "shasums": { + "jar": "b372a037d4230aa57fbeffdef30fd6123f9c0c2db85d0aced00c91b974f33f99" + }, + "version": "9999.0-empty-to-avoid-conflict-with-guava" + }, + "com.google.j2objc:j2objc-annotations": { + "shasums": { + "jar": "88241573467ddca44ffd4d74aa04c2bbfd11bf7c17e0c342c94c9de7a70a7c64" + }, + "version": "3.0.0" + }, + "org.checkerframework:checker-qual": { + "shasums": { + "jar": "3fbc2e98f05854c3df16df9abaa955b91b15b3ecac33623208ed6424640ef0f6" + }, + "version": "3.43.0" + } + }, + "dependencies": { + "com.google.code.gson:gson": [ + "com.google.errorprone:error_prone_annotations" + ], + "com.google.guava:guava": [ + "com.google.code.findbugs:jsr305", + "com.google.errorprone:error_prone_annotations", + "com.google.guava:failureaccess", + "com.google.guava:listenablefuture", + "com.google.j2objc:j2objc-annotations", + "org.checkerframework:checker-qual" + ] + }, + "packages": { + "com.google.code.findbugs:jsr305": [ + "javax.annotation", + "javax.annotation.concurrent", + "javax.annotation.meta" + ], + "com.google.code.gson:gson": [ + "com.google.gson", + "com.google.gson.annotations", + "com.google.gson.internal", + "com.google.gson.internal.bind", + "com.google.gson.internal.bind.util", + "com.google.gson.internal.reflect", + "com.google.gson.internal.sql", + "com.google.gson.reflect", + "com.google.gson.stream" + ], + "com.google.errorprone:error_prone_annotations": [ + "com.google.errorprone.annotations", + "com.google.errorprone.annotations.concurrent" + ], + "com.google.guava:failureaccess": [ + "com.google.common.util.concurrent.internal" + ], + "com.google.guava:guava": [ + "com.google.common.annotations", + "com.google.common.base", + "com.google.common.base.internal", + "com.google.common.cache", + "com.google.common.collect", + "com.google.common.escape", + "com.google.common.eventbus", + "com.google.common.graph", + "com.google.common.hash", + "com.google.common.html", + "com.google.common.io", + "com.google.common.math", + "com.google.common.net", + "com.google.common.primitives", + "com.google.common.reflect", + "com.google.common.util.concurrent", + "com.google.common.xml", + "com.google.thirdparty.publicsuffix" + ], + "com.google.j2objc:j2objc-annotations": [ + "com.google.j2objc.annotations" + ], + "org.checkerframework:checker-qual": [ + "org.checkerframework.checker.builder.qual", + "org.checkerframework.checker.calledmethods.qual", + "org.checkerframework.checker.compilermsgs.qual", + "org.checkerframework.checker.fenum.qual", + "org.checkerframework.checker.formatter.qual", + "org.checkerframework.checker.guieffect.qual", + "org.checkerframework.checker.i18n.qual", + "org.checkerframework.checker.i18nformatter.qual", + "org.checkerframework.checker.index.qual", + "org.checkerframework.checker.initialization.qual", + "org.checkerframework.checker.interning.qual", + "org.checkerframework.checker.lock.qual", + "org.checkerframework.checker.mustcall.qual", + "org.checkerframework.checker.nullness.qual", + "org.checkerframework.checker.optional.qual", + "org.checkerframework.checker.propkey.qual", + "org.checkerframework.checker.regex.qual", + "org.checkerframework.checker.signature.qual", + "org.checkerframework.checker.signedness.qual", + "org.checkerframework.checker.tainting.qual", + "org.checkerframework.checker.units.qual", + "org.checkerframework.common.aliasing.qual", + "org.checkerframework.common.initializedfields.qual", + "org.checkerframework.common.reflection.qual", + "org.checkerframework.common.returnsreceiver.qual", + "org.checkerframework.common.subtyping.qual", + "org.checkerframework.common.util.count.report.qual", + "org.checkerframework.common.value.qual", + "org.checkerframework.dataflow.qual", + "org.checkerframework.framework.qual" + ] + }, + "repositories": { + "https://repo1.maven.org/maven2/": [ + "com.google.code.findbugs:jsr305", + "com.google.code.gson:gson", + "com.google.errorprone:error_prone_annotations", + "com.google.guava:failureaccess", + "com.google.guava:guava", + "com.google.guava:listenablefuture", + "com.google.j2objc:j2objc-annotations", + "org.checkerframework:checker-qual" + ] + }, + "services": {}, + "version": "2" +} diff --git a/java/gazelle/testdata/kt_prop_delegate_deps/src/main/java/com/example/consumer/BUILD.in b/java/gazelle/testdata/kt_prop_delegate_deps/src/main/java/com/example/consumer/BUILD.in new file mode 100644 index 00000000..e69de29b diff --git a/java/gazelle/testdata/kt_prop_delegate_deps/src/main/java/com/example/consumer/BUILD.out b/java/gazelle/testdata/kt_prop_delegate_deps/src/main/java/com/example/consumer/BUILD.out new file mode 100644 index 00000000..960b7154 --- /dev/null +++ b/java/gazelle/testdata/kt_prop_delegate_deps/src/main/java/com/example/consumer/BUILD.out @@ -0,0 +1,8 @@ +load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") + +kt_jvm_library( + name = "consumer", + srcs = ["Consumer.kt"], + visibility = ["//:__subpackages__"], + deps = ["//src/main/java/com/example/provider"], +) diff --git a/java/gazelle/testdata/kt_prop_delegate_deps/src/main/java/com/example/consumer/Consumer.kt b/java/gazelle/testdata/kt_prop_delegate_deps/src/main/java/com/example/consumer/Consumer.kt new file mode 100644 index 00000000..87d5c9de --- /dev/null +++ b/java/gazelle/testdata/kt_prop_delegate_deps/src/main/java/com/example/consumer/Consumer.kt @@ -0,0 +1,26 @@ +package com.example.consumer + +import com.example.provider.DelegateProvider + +/** + * Consumer that uses property delegates from another package. This should automatically get + * transitive dependencies from the provider's delegates through the exports mechanism. + */ +class Consumer { + + private val provider = DelegateProvider() + + fun useDelegate(): String { + // Access the delegated properties - this should work because + // the provider exports its delegate dependencies + val processed = provider.processData("test data") + + // Modify observable property + provider.dataList = listOf("item1", "item2") + + // Try to set vetoable property + provider.validatedData = "\"valid json string\"" + + return "Processed: $processed, Data: ${provider.dataList}, Validated: ${provider.validatedData}" + } +} diff --git a/java/gazelle/testdata/kt_prop_delegate_deps/src/main/java/com/example/provider/BUILD.in b/java/gazelle/testdata/kt_prop_delegate_deps/src/main/java/com/example/provider/BUILD.in new file mode 100644 index 00000000..e69de29b diff --git a/java/gazelle/testdata/kt_prop_delegate_deps/src/main/java/com/example/provider/BUILD.out b/java/gazelle/testdata/kt_prop_delegate_deps/src/main/java/com/example/provider/BUILD.out new file mode 100644 index 00000000..0f583def --- /dev/null +++ b/java/gazelle/testdata/kt_prop_delegate_deps/src/main/java/com/example/provider/BUILD.out @@ -0,0 +1,9 @@ +load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") + +kt_jvm_library( + name = "provider", + srcs = ["DelegateProvider.kt"], + visibility = ["//:__subpackages__"], + exports = ["@maven//:com_google_code_gson_gson"], + deps = ["@maven//:com_google_code_gson_gson"], +) diff --git a/java/gazelle/testdata/kt_prop_delegate_deps/src/main/java/com/example/provider/DelegateProvider.kt b/java/gazelle/testdata/kt_prop_delegate_deps/src/main/java/com/example/provider/DelegateProvider.kt new file mode 100644 index 00000000..387b08ba --- /dev/null +++ b/java/gazelle/testdata/kt_prop_delegate_deps/src/main/java/com/example/provider/DelegateProvider.kt @@ -0,0 +1,52 @@ +package com.example.provider + +import com.google.gson.Gson +import java.util.ArrayList +import kotlin.properties.Delegates + +/** + * Property delegates that use external dependencies. Any code that uses these properties should + * transitively depend on: + * - com.google.gson (for Gson) + * - java.util (for ArrayList) + */ +class DelegateProvider { + + /** + * Lazy property delegate that uses external dependencies. The lazy block uses Gson, so consumers + * should get Gson transitively. + */ + val jsonProcessor: Gson by lazy { Gson() } + + /** + * Observable property delegate that uses external dependencies. The observer uses ArrayList, so + * consumers should get java.util transitively. + */ + var dataList: List by + Delegates.observable(emptyList()) { _, oldValue, newValue -> + val list = ArrayList() + list.addAll(oldValue) + list.addAll(newValue) + println("Data changed from ${list.size - newValue.size} to ${list.size} items") + } + + /** Vetoable property delegate with external dependencies. */ + var validatedData: String by + Delegates.vetoable("") { _, _, newValue -> + val gson = Gson() + // Use Gson to validate the new value + try { + gson.fromJson(newValue, String::class.java) + true + } catch (e: Exception) { + false + } + } + + /** Regular property for comparison - should not affect dependencies. */ + val regularProperty: String = "regular value" + + fun processData(input: String): String { + return jsonProcessor.toJson(input) + } +} diff --git a/java/gazelle/testdata/kt_prop_delegate_multi/BUILD.in b/java/gazelle/testdata/kt_prop_delegate_multi/BUILD.in new file mode 100644 index 00000000..feccf582 --- /dev/null +++ b/java/gazelle/testdata/kt_prop_delegate_multi/BUILD.in @@ -0,0 +1 @@ +# gazelle:jvm_kotlin_enabled true diff --git a/java/gazelle/testdata/kt_prop_delegate_multi/BUILD.out b/java/gazelle/testdata/kt_prop_delegate_multi/BUILD.out new file mode 100644 index 00000000..feccf582 --- /dev/null +++ b/java/gazelle/testdata/kt_prop_delegate_multi/BUILD.out @@ -0,0 +1 @@ +# gazelle:jvm_kotlin_enabled true diff --git a/java/gazelle/testdata/kt_prop_delegate_multi/WORKSPACE b/java/gazelle/testdata/kt_prop_delegate_multi/WORKSPACE new file mode 100644 index 00000000..6a9b9f54 --- /dev/null +++ b/java/gazelle/testdata/kt_prop_delegate_multi/WORKSPACE @@ -0,0 +1,41 @@ +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +rules_kotlin_version = "1.9.6" + +rules_kotlin_sha = "3b772976fec7bdcda1d84b9d39b176589424c047eb2175bed09aac630e50af43" + +http_archive( + name = "rules_kotlin", + sha256 = rules_kotlin_sha, + urls = ["https://github.com/bazelbuild/rules_kotlin/releases/download/v%s/rules_kotlin-v%s.tar.gz" % (rules_kotlin_version, rules_kotlin_version)], +) + +load("@rules_kotlin//kotlin:repositories.bzl", "kotlin_repositories") + +kotlin_repositories() + +load("@rules_kotlin//kotlin:core.bzl", "kt_register_toolchains") + +kt_register_toolchains() + +http_archive( + name = "rules_jvm_external", + sha256 = "23fe83890a77ac1a3ee143e2306ec12da4a845285b14ea13cb0df1b1e23658fe", + strip_prefix = "rules_jvm_external-4.3", + urls = ["https://github.com/bazelbuild/rules_jvm_external/archive/refs/tags/4.3.tar.gz"], +) + +load("@rules_jvm_external//:defs.bzl", "maven_install") + +maven_install( + name = "maven", + artifacts = [ + "com.google.code.gson:gson:2.8.9", + "com.google.guava:guava:30.0-jre", + ], + fetch_sources = True, + repositories = [ + "http://uk.maven.org/maven2", + "https://jcenter.bintray.com/", + ], +) diff --git a/java/gazelle/testdata/kt_prop_delegate_multi/maven_install.json b/java/gazelle/testdata/kt_prop_delegate_multi/maven_install.json new file mode 100644 index 00000000..12ea79b9 --- /dev/null +++ b/java/gazelle/testdata/kt_prop_delegate_multi/maven_install.json @@ -0,0 +1,162 @@ +{ + "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL", + "__INPUT_ARTIFACTS_HASH": 1811698626, + "__RESOLVED_ARTIFACTS_HASH": 1591722038, + "artifacts": { + "com.google.code.findbugs:jsr305": { + "shasums": { + "jar": "766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7" + }, + "version": "3.0.2" + }, + "com.google.code.gson:gson": { + "shasums": { + "jar": "dd0ce1b55a3ed2080cb70f9c655850cda86c206862310009dcb5e5c95265a5e0" + }, + "version": "2.13.2" + }, + "com.google.errorprone:error_prone_annotations": { + "shasums": { + "jar": "a56e782b5b50811ac204073a355a21d915a2107fce13ec711331ad036f660fcc" + }, + "version": "2.41.0" + }, + "com.google.guava:failureaccess": { + "shasums": { + "jar": "8a8f81cf9b359e3f6dfa691a1e776985c061ef2f223c9b2c80753e1b458e8064" + }, + "version": "1.0.2" + }, + "com.google.guava:guava": { + "shasums": { + "jar": "4bf0e2c5af8e4525c96e8fde17a4f7307f97f8478f11c4c8e35a0e3298ae4e90" + }, + "version": "33.3.1-jre" + }, + "com.google.guava:listenablefuture": { + "shasums": { + "jar": "b372a037d4230aa57fbeffdef30fd6123f9c0c2db85d0aced00c91b974f33f99" + }, + "version": "9999.0-empty-to-avoid-conflict-with-guava" + }, + "com.google.j2objc:j2objc-annotations": { + "shasums": { + "jar": "88241573467ddca44ffd4d74aa04c2bbfd11bf7c17e0c342c94c9de7a70a7c64" + }, + "version": "3.0.0" + }, + "org.checkerframework:checker-qual": { + "shasums": { + "jar": "3fbc2e98f05854c3df16df9abaa955b91b15b3ecac33623208ed6424640ef0f6" + }, + "version": "3.43.0" + } + }, + "dependencies": { + "com.google.code.gson:gson": [ + "com.google.errorprone:error_prone_annotations" + ], + "com.google.guava:guava": [ + "com.google.code.findbugs:jsr305", + "com.google.errorprone:error_prone_annotations", + "com.google.guava:failureaccess", + "com.google.guava:listenablefuture", + "com.google.j2objc:j2objc-annotations", + "org.checkerframework:checker-qual" + ] + }, + "packages": { + "com.google.code.findbugs:jsr305": [ + "javax.annotation", + "javax.annotation.concurrent", + "javax.annotation.meta" + ], + "com.google.code.gson:gson": [ + "com.google.gson", + "com.google.gson.annotations", + "com.google.gson.internal", + "com.google.gson.internal.bind", + "com.google.gson.internal.bind.util", + "com.google.gson.internal.reflect", + "com.google.gson.internal.sql", + "com.google.gson.reflect", + "com.google.gson.stream" + ], + "com.google.errorprone:error_prone_annotations": [ + "com.google.errorprone.annotations", + "com.google.errorprone.annotations.concurrent" + ], + "com.google.guava:failureaccess": [ + "com.google.common.util.concurrent.internal" + ], + "com.google.guava:guava": [ + "com.google.common.annotations", + "com.google.common.base", + "com.google.common.base.internal", + "com.google.common.cache", + "com.google.common.collect", + "com.google.common.escape", + "com.google.common.eventbus", + "com.google.common.graph", + "com.google.common.hash", + "com.google.common.html", + "com.google.common.io", + "com.google.common.math", + "com.google.common.net", + "com.google.common.primitives", + "com.google.common.reflect", + "com.google.common.util.concurrent", + "com.google.common.xml", + "com.google.thirdparty.publicsuffix" + ], + "com.google.j2objc:j2objc-annotations": [ + "com.google.j2objc.annotations" + ], + "org.checkerframework:checker-qual": [ + "org.checkerframework.checker.builder.qual", + "org.checkerframework.checker.calledmethods.qual", + "org.checkerframework.checker.compilermsgs.qual", + "org.checkerframework.checker.fenum.qual", + "org.checkerframework.checker.formatter.qual", + "org.checkerframework.checker.guieffect.qual", + "org.checkerframework.checker.i18n.qual", + "org.checkerframework.checker.i18nformatter.qual", + "org.checkerframework.checker.index.qual", + "org.checkerframework.checker.initialization.qual", + "org.checkerframework.checker.interning.qual", + "org.checkerframework.checker.lock.qual", + "org.checkerframework.checker.mustcall.qual", + "org.checkerframework.checker.nullness.qual", + "org.checkerframework.checker.optional.qual", + "org.checkerframework.checker.propkey.qual", + "org.checkerframework.checker.regex.qual", + "org.checkerframework.checker.signature.qual", + "org.checkerframework.checker.signedness.qual", + "org.checkerframework.checker.tainting.qual", + "org.checkerframework.checker.units.qual", + "org.checkerframework.common.aliasing.qual", + "org.checkerframework.common.initializedfields.qual", + "org.checkerframework.common.reflection.qual", + "org.checkerframework.common.returnsreceiver.qual", + "org.checkerframework.common.subtyping.qual", + "org.checkerframework.common.util.count.report.qual", + "org.checkerframework.common.value.qual", + "org.checkerframework.dataflow.qual", + "org.checkerframework.framework.qual" + ] + }, + "repositories": { + "https://repo1.maven.org/maven2/": [ + "com.google.code.findbugs:jsr305", + "com.google.code.gson:gson", + "com.google.errorprone:error_prone_annotations", + "com.google.guava:failureaccess", + "com.google.guava:guava", + "com.google.guava:listenablefuture", + "com.google.j2objc:j2objc-annotations", + "org.checkerframework:checker-qual" + ] + }, + "services": {}, + "version": "2" +} diff --git a/java/gazelle/testdata/kt_prop_delegate_multi/src/main/java/com/example/multiple/BUILD.in b/java/gazelle/testdata/kt_prop_delegate_multi/src/main/java/com/example/multiple/BUILD.in new file mode 100644 index 00000000..e69de29b diff --git a/java/gazelle/testdata/kt_prop_delegate_multi/src/main/java/com/example/multiple/BUILD.out b/java/gazelle/testdata/kt_prop_delegate_multi/src/main/java/com/example/multiple/BUILD.out new file mode 100644 index 00000000..e6e7afea --- /dev/null +++ b/java/gazelle/testdata/kt_prop_delegate_multi/src/main/java/com/example/multiple/BUILD.out @@ -0,0 +1,15 @@ +load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") + +kt_jvm_library( + name = "multiple", + srcs = ["MultipleDelegates.kt"], + visibility = ["//:__subpackages__"], + exports = [ + "@maven//:com_google_code_gson_gson", + "@maven//:com_google_guava_guava", + ], + deps = [ + "@maven//:com_google_code_gson_gson", + "@maven//:com_google_guava_guava", + ], +) diff --git a/java/gazelle/testdata/kt_prop_delegate_multi/src/main/java/com/example/multiple/MultipleDelegates.kt b/java/gazelle/testdata/kt_prop_delegate_multi/src/main/java/com/example/multiple/MultipleDelegates.kt new file mode 100644 index 00000000..348af046 --- /dev/null +++ b/java/gazelle/testdata/kt_prop_delegate_multi/src/main/java/com/example/multiple/MultipleDelegates.kt @@ -0,0 +1,64 @@ +package com.example.multiple + +import com.google.common.base.Strings +import com.google.gson.Gson +import java.util.concurrent.ConcurrentHashMap +import kotlin.properties.Delegates + +/** + * Class with multiple property delegates using different external dependencies. This should export + * all the dependencies used by the delegates: + * - com.google.gson (for Gson) + * - com.google.guava (for Strings) + * - java.util.concurrent (for ConcurrentHashMap) + */ +class MultipleDelegates { + + /** Lazy delegate using Gson. */ + val jsonProcessor: Gson by lazy { Gson() } + + /** Lazy delegate using Guava Strings utility. */ + val stringProcessor: String by lazy { Strings.padEnd("hello", 10, ' ') } + + /** Observable delegate using ConcurrentHashMap. */ + var dataMap: Map by + Delegates.observable(emptyMap()) { _, _, newValue -> + val concurrentMap = ConcurrentHashMap() + concurrentMap.putAll(newValue) + println("Map updated with ${concurrentMap.size} entries") + } + + /** Vetoable delegate using multiple dependencies. */ + var validatedJson: String by + Delegates.vetoable("{}") { _, _, newValue -> + // Use Guava Strings to check if not null or empty + if (Strings.isNullOrEmpty(newValue)) { + false + } else { + // Use Gson to validate JSON + try { + val gson = Gson() + gson.fromJson(newValue, Map::class.java) + true + } catch (e: Exception) { + false + } + } + } + + /** Another lazy delegate with complex initialization. */ + val complexProcessor: String by lazy { + val gson = Gson() + val processed = Strings.padStart("hello world", 20, '*') + gson.toJson(mapOf("processed" to processed)) + } + + fun processAll(): String { + val json = jsonProcessor.toJson("test") + val padded = Strings.padEnd("test", 10, '-') + dataMap = mapOf("key1" to "value1", "key2" to "value2") + validatedJson = "{\"test\": \"value\"}" + + return "JSON: $json, Padded: $padded, Complex: $complexProcessor" + } +} diff --git a/java/gazelle/testdata/kt_prop_delegate_trans/BUILD.in b/java/gazelle/testdata/kt_prop_delegate_trans/BUILD.in new file mode 100644 index 00000000..feccf582 --- /dev/null +++ b/java/gazelle/testdata/kt_prop_delegate_trans/BUILD.in @@ -0,0 +1 @@ +# gazelle:jvm_kotlin_enabled true diff --git a/java/gazelle/testdata/kt_prop_delegate_trans/BUILD.out b/java/gazelle/testdata/kt_prop_delegate_trans/BUILD.out new file mode 100644 index 00000000..feccf582 --- /dev/null +++ b/java/gazelle/testdata/kt_prop_delegate_trans/BUILD.out @@ -0,0 +1 @@ +# gazelle:jvm_kotlin_enabled true diff --git a/java/gazelle/testdata/kt_prop_delegate_trans/WORKSPACE b/java/gazelle/testdata/kt_prop_delegate_trans/WORKSPACE new file mode 100644 index 00000000..6a9b9f54 --- /dev/null +++ b/java/gazelle/testdata/kt_prop_delegate_trans/WORKSPACE @@ -0,0 +1,41 @@ +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +rules_kotlin_version = "1.9.6" + +rules_kotlin_sha = "3b772976fec7bdcda1d84b9d39b176589424c047eb2175bed09aac630e50af43" + +http_archive( + name = "rules_kotlin", + sha256 = rules_kotlin_sha, + urls = ["https://github.com/bazelbuild/rules_kotlin/releases/download/v%s/rules_kotlin-v%s.tar.gz" % (rules_kotlin_version, rules_kotlin_version)], +) + +load("@rules_kotlin//kotlin:repositories.bzl", "kotlin_repositories") + +kotlin_repositories() + +load("@rules_kotlin//kotlin:core.bzl", "kt_register_toolchains") + +kt_register_toolchains() + +http_archive( + name = "rules_jvm_external", + sha256 = "23fe83890a77ac1a3ee143e2306ec12da4a845285b14ea13cb0df1b1e23658fe", + strip_prefix = "rules_jvm_external-4.3", + urls = ["https://github.com/bazelbuild/rules_jvm_external/archive/refs/tags/4.3.tar.gz"], +) + +load("@rules_jvm_external//:defs.bzl", "maven_install") + +maven_install( + name = "maven", + artifacts = [ + "com.google.code.gson:gson:2.8.9", + "com.google.guava:guava:30.0-jre", + ], + fetch_sources = True, + repositories = [ + "http://uk.maven.org/maven2", + "https://jcenter.bintray.com/", + ], +) diff --git a/java/gazelle/testdata/kt_prop_delegate_trans/maven_install.json b/java/gazelle/testdata/kt_prop_delegate_trans/maven_install.json new file mode 100644 index 00000000..12ea79b9 --- /dev/null +++ b/java/gazelle/testdata/kt_prop_delegate_trans/maven_install.json @@ -0,0 +1,162 @@ +{ + "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL", + "__INPUT_ARTIFACTS_HASH": 1811698626, + "__RESOLVED_ARTIFACTS_HASH": 1591722038, + "artifacts": { + "com.google.code.findbugs:jsr305": { + "shasums": { + "jar": "766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7" + }, + "version": "3.0.2" + }, + "com.google.code.gson:gson": { + "shasums": { + "jar": "dd0ce1b55a3ed2080cb70f9c655850cda86c206862310009dcb5e5c95265a5e0" + }, + "version": "2.13.2" + }, + "com.google.errorprone:error_prone_annotations": { + "shasums": { + "jar": "a56e782b5b50811ac204073a355a21d915a2107fce13ec711331ad036f660fcc" + }, + "version": "2.41.0" + }, + "com.google.guava:failureaccess": { + "shasums": { + "jar": "8a8f81cf9b359e3f6dfa691a1e776985c061ef2f223c9b2c80753e1b458e8064" + }, + "version": "1.0.2" + }, + "com.google.guava:guava": { + "shasums": { + "jar": "4bf0e2c5af8e4525c96e8fde17a4f7307f97f8478f11c4c8e35a0e3298ae4e90" + }, + "version": "33.3.1-jre" + }, + "com.google.guava:listenablefuture": { + "shasums": { + "jar": "b372a037d4230aa57fbeffdef30fd6123f9c0c2db85d0aced00c91b974f33f99" + }, + "version": "9999.0-empty-to-avoid-conflict-with-guava" + }, + "com.google.j2objc:j2objc-annotations": { + "shasums": { + "jar": "88241573467ddca44ffd4d74aa04c2bbfd11bf7c17e0c342c94c9de7a70a7c64" + }, + "version": "3.0.0" + }, + "org.checkerframework:checker-qual": { + "shasums": { + "jar": "3fbc2e98f05854c3df16df9abaa955b91b15b3ecac33623208ed6424640ef0f6" + }, + "version": "3.43.0" + } + }, + "dependencies": { + "com.google.code.gson:gson": [ + "com.google.errorprone:error_prone_annotations" + ], + "com.google.guava:guava": [ + "com.google.code.findbugs:jsr305", + "com.google.errorprone:error_prone_annotations", + "com.google.guava:failureaccess", + "com.google.guava:listenablefuture", + "com.google.j2objc:j2objc-annotations", + "org.checkerframework:checker-qual" + ] + }, + "packages": { + "com.google.code.findbugs:jsr305": [ + "javax.annotation", + "javax.annotation.concurrent", + "javax.annotation.meta" + ], + "com.google.code.gson:gson": [ + "com.google.gson", + "com.google.gson.annotations", + "com.google.gson.internal", + "com.google.gson.internal.bind", + "com.google.gson.internal.bind.util", + "com.google.gson.internal.reflect", + "com.google.gson.internal.sql", + "com.google.gson.reflect", + "com.google.gson.stream" + ], + "com.google.errorprone:error_prone_annotations": [ + "com.google.errorprone.annotations", + "com.google.errorprone.annotations.concurrent" + ], + "com.google.guava:failureaccess": [ + "com.google.common.util.concurrent.internal" + ], + "com.google.guava:guava": [ + "com.google.common.annotations", + "com.google.common.base", + "com.google.common.base.internal", + "com.google.common.cache", + "com.google.common.collect", + "com.google.common.escape", + "com.google.common.eventbus", + "com.google.common.graph", + "com.google.common.hash", + "com.google.common.html", + "com.google.common.io", + "com.google.common.math", + "com.google.common.net", + "com.google.common.primitives", + "com.google.common.reflect", + "com.google.common.util.concurrent", + "com.google.common.xml", + "com.google.thirdparty.publicsuffix" + ], + "com.google.j2objc:j2objc-annotations": [ + "com.google.j2objc.annotations" + ], + "org.checkerframework:checker-qual": [ + "org.checkerframework.checker.builder.qual", + "org.checkerframework.checker.calledmethods.qual", + "org.checkerframework.checker.compilermsgs.qual", + "org.checkerframework.checker.fenum.qual", + "org.checkerframework.checker.formatter.qual", + "org.checkerframework.checker.guieffect.qual", + "org.checkerframework.checker.i18n.qual", + "org.checkerframework.checker.i18nformatter.qual", + "org.checkerframework.checker.index.qual", + "org.checkerframework.checker.initialization.qual", + "org.checkerframework.checker.interning.qual", + "org.checkerframework.checker.lock.qual", + "org.checkerframework.checker.mustcall.qual", + "org.checkerframework.checker.nullness.qual", + "org.checkerframework.checker.optional.qual", + "org.checkerframework.checker.propkey.qual", + "org.checkerframework.checker.regex.qual", + "org.checkerframework.checker.signature.qual", + "org.checkerframework.checker.signedness.qual", + "org.checkerframework.checker.tainting.qual", + "org.checkerframework.checker.units.qual", + "org.checkerframework.common.aliasing.qual", + "org.checkerframework.common.initializedfields.qual", + "org.checkerframework.common.reflection.qual", + "org.checkerframework.common.returnsreceiver.qual", + "org.checkerframework.common.subtyping.qual", + "org.checkerframework.common.util.count.report.qual", + "org.checkerframework.common.value.qual", + "org.checkerframework.dataflow.qual", + "org.checkerframework.framework.qual" + ] + }, + "repositories": { + "https://repo1.maven.org/maven2/": [ + "com.google.code.findbugs:jsr305", + "com.google.code.gson:gson", + "com.google.errorprone:error_prone_annotations", + "com.google.guava:failureaccess", + "com.google.guava:guava", + "com.google.guava:listenablefuture", + "com.google.j2objc:j2objc-annotations", + "org.checkerframework:checker-qual" + ] + }, + "services": {}, + "version": "2" +} diff --git a/java/gazelle/testdata/kt_prop_delegate_trans/src/main/java/com/example/base/BUILD.in b/java/gazelle/testdata/kt_prop_delegate_trans/src/main/java/com/example/base/BUILD.in new file mode 100644 index 00000000..e69de29b diff --git a/java/gazelle/testdata/kt_prop_delegate_trans/src/main/java/com/example/base/BUILD.out b/java/gazelle/testdata/kt_prop_delegate_trans/src/main/java/com/example/base/BUILD.out new file mode 100644 index 00000000..cd81eb32 --- /dev/null +++ b/java/gazelle/testdata/kt_prop_delegate_trans/src/main/java/com/example/base/BUILD.out @@ -0,0 +1,9 @@ +load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") + +kt_jvm_library( + name = "base", + srcs = ["BaseDelegate.kt"], + visibility = ["//:__subpackages__"], + exports = ["@maven//:com_google_code_gson_gson"], + deps = ["@maven//:com_google_code_gson_gson"], +) diff --git a/java/gazelle/testdata/kt_prop_delegate_trans/src/main/java/com/example/base/BaseDelegate.kt b/java/gazelle/testdata/kt_prop_delegate_trans/src/main/java/com/example/base/BaseDelegate.kt new file mode 100644 index 00000000..865bed73 --- /dev/null +++ b/java/gazelle/testdata/kt_prop_delegate_trans/src/main/java/com/example/base/BaseDelegate.kt @@ -0,0 +1,17 @@ +package com.example.base + +import com.google.gson.Gson + +/** + * Base class with property delegates that use external dependencies. This exports Gson so that + * consumers can use it transitively. + */ +class BaseDelegate { + + /** Lazy property delegate that uses Gson. */ + val jsonProcessor: Gson by lazy { Gson() } + + fun toJson(data: Any): String { + return jsonProcessor.toJson(data) + } +} diff --git a/java/gazelle/testdata/kt_prop_delegate_trans/src/main/java/com/example/middle/BUILD.in b/java/gazelle/testdata/kt_prop_delegate_trans/src/main/java/com/example/middle/BUILD.in new file mode 100644 index 00000000..e69de29b diff --git a/java/gazelle/testdata/kt_prop_delegate_trans/src/main/java/com/example/middle/BUILD.out b/java/gazelle/testdata/kt_prop_delegate_trans/src/main/java/com/example/middle/BUILD.out new file mode 100644 index 00000000..3c0f6c7e --- /dev/null +++ b/java/gazelle/testdata/kt_prop_delegate_trans/src/main/java/com/example/middle/BUILD.out @@ -0,0 +1,12 @@ +load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") + +kt_jvm_library( + name = "middle", + srcs = ["MiddleDelegate.kt"], + visibility = ["//:__subpackages__"], + exports = ["@maven//:com_google_guava_guava"], + deps = [ + "//src/main/java/com/example/base", + "@maven//:com_google_guava_guava", + ], +) diff --git a/java/gazelle/testdata/kt_prop_delegate_trans/src/main/java/com/example/middle/MiddleDelegate.kt b/java/gazelle/testdata/kt_prop_delegate_trans/src/main/java/com/example/middle/MiddleDelegate.kt new file mode 100644 index 00000000..f94b76fe --- /dev/null +++ b/java/gazelle/testdata/kt_prop_delegate_trans/src/main/java/com/example/middle/MiddleDelegate.kt @@ -0,0 +1,23 @@ +package com.example.middle + +import com.example.base.BaseDelegate +import com.google.common.base.Strings + +/** + * Middle layer that uses BaseDelegate and adds its own property delegates. This should export its + * own delegate dependencies (guava) while inheriting transitive dependencies from BaseDelegate + * (gson). + */ +class MiddleDelegate { + + private val base = BaseDelegate() + + /** Lazy property delegate that uses Guava Strings. */ + val stringProcessor: String by lazy { Strings.padEnd("processed", 20, '-') } + + fun processString(input: String): String { + val padded = Strings.padStart(input, 15, '*') + val json = base.toJson(mapOf("processed" to padded)) + return json + } +} diff --git a/java/gazelle/testdata/kt_prop_delegate_trans/src/main/java/com/example/top/BUILD.in b/java/gazelle/testdata/kt_prop_delegate_trans/src/main/java/com/example/top/BUILD.in new file mode 100644 index 00000000..e69de29b diff --git a/java/gazelle/testdata/kt_prop_delegate_trans/src/main/java/com/example/top/BUILD.out b/java/gazelle/testdata/kt_prop_delegate_trans/src/main/java/com/example/top/BUILD.out new file mode 100644 index 00000000..5d209360 --- /dev/null +++ b/java/gazelle/testdata/kt_prop_delegate_trans/src/main/java/com/example/top/BUILD.out @@ -0,0 +1,8 @@ +load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") + +kt_jvm_library( + name = "top", + srcs = ["TopConsumer.kt"], + visibility = ["//:__subpackages__"], + deps = ["//src/main/java/com/example/middle"], +) diff --git a/java/gazelle/testdata/kt_prop_delegate_trans/src/main/java/com/example/top/TopConsumer.kt b/java/gazelle/testdata/kt_prop_delegate_trans/src/main/java/com/example/top/TopConsumer.kt new file mode 100644 index 00000000..4a472bc2 --- /dev/null +++ b/java/gazelle/testdata/kt_prop_delegate_trans/src/main/java/com/example/top/TopConsumer.kt @@ -0,0 +1,23 @@ +package com.example.top + +import com.example.middle.MiddleDelegate + +/** + * Top-level consumer that uses MiddleDelegate. This should automatically get transitive access to + * both: + * - commons-lang3 (from MiddleDelegate's exports) + * - gson (from BaseDelegate's exports via MiddleDelegate's dependency) + * + * This demonstrates the transitive nature of the exports mechanism for property delegate + * dependencies. + */ +class TopConsumer { + + private val middle = MiddleDelegate() + + fun processData(): String { + // This should work because we get transitive access to all dependencies + // through the exports mechanism + return middle.processString("hello world") + } +} diff --git a/java/src/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/BUILD.bazel b/java/src/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/BUILD.bazel index ac3adc7d..2889cdce 100644 --- a/java/src/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/BUILD.bazel +++ b/java/src/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/BUILD.bazel @@ -6,8 +6,11 @@ java_library( "ClasspathParser.java", "GrpcServer.java", "JavaIdentifier.java", + "KtParser.java", "LifecycleService.java", "Main.java", + "ParsedPackageData.java", + "PerClassData.java", "TimeoutHandler.java", ], visibility = ["//java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser:__subpackages__"], @@ -24,6 +27,7 @@ java_library( "@contrib_rules_jvm_deps//:io_grpc_grpc_api", "@contrib_rules_jvm_deps//:io_grpc_grpc_services", "@contrib_rules_jvm_deps//:io_grpc_grpc_stub", + "@contrib_rules_jvm_deps//:org_jetbrains_kotlin_kotlin_compiler", "@contrib_rules_jvm_deps//:org_slf4j_slf4j_api", ], ) diff --git a/java/src/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/ClasspathParser.java b/java/src/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/ClasspathParser.java index c2e25abd..7b29c836 100644 --- a/java/src/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/ClasspathParser.java +++ b/java/src/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/ClasspathParser.java @@ -35,11 +35,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; -import java.util.SortedMap; -import java.util.SortedSet; -import java.util.TreeMap; import java.util.TreeSet; import java.util.stream.Collectors; import javax.annotation.Nullable; @@ -54,90 +50,39 @@ public class ClasspathParser { private static final Logger logger = LoggerFactory.getLogger(ClasspathParser.class); - private final Set usedTypes = new TreeSet<>(); - private final Set usedPackagesWithoutSpecificTypes = new TreeSet<>(); - - private final Set exportedTypes = new TreeSet<>(); - private final Set packages = new TreeSet<>(); - private final Set mainClasses = new TreeSet<>(); - - // Mapping from fully-qualified class-name to class-names of annotations on that class. - // Annotations will be fully-qualified where that's known, and not where not known. - final Map perClassData = new TreeMap<>(); + private final ParsedPackageData data = new ParsedPackageData(); // get the system java compiler instance private static final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); - private static final List OPTIONS = List.of("--release=" + Runtime.version().feature()); + private static final List OPTIONS = + List.of("--release=" + Runtime.version().feature(), "-proc:none"); public ClasspathParser() { // Doesn't need to do anything currently } - static class PerClassData { - public PerClassData() { - this(new TreeSet<>(), new TreeMap<>(), new TreeMap<>()); - } - - @Override - public String toString() { - return "PerClassData{" - + "annotations=" - + annotations - + ", perMethodAnnotations=" - + perMethodAnnotations - + ", perFieldAnnotations=" - + perFieldAnnotations - + '}'; - } - - public PerClassData( - SortedSet annotations, - SortedMap> perMethodAnnotations, - SortedMap> perFieldAnnotations) { - this.annotations = annotations; - this.perMethodAnnotations = perMethodAnnotations; - this.perFieldAnnotations = perFieldAnnotations; - } - - final SortedSet annotations; - - final SortedMap> perMethodAnnotations; - final SortedMap> perFieldAnnotations; - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - PerClassData that = (PerClassData) o; - return Objects.equals(annotations, that.annotations) - && Objects.equals(perMethodAnnotations, that.perMethodAnnotations) - && Objects.equals(perFieldAnnotations, that.perFieldAnnotations); - } - - @Override - public int hashCode() { - return Objects.hash(annotations, perMethodAnnotations, perFieldAnnotations); - } + public ParsedPackageData getParsedPackageData() { + return data; } public ImmutableSet getUsedTypes() { - return ImmutableSet.copyOf(usedTypes); + return ImmutableSet.copyOf(data.usedTypes); } public ImmutableSet getUsedPackagesWithoutSpecificTypes() { - return ImmutableSet.copyOf(usedPackagesWithoutSpecificTypes); + return ImmutableSet.copyOf(data.usedPackagesWithoutSpecificTypes); } public ImmutableSet getExportedTypes() { - return ImmutableSet.copyOf(exportedTypes); + return ImmutableSet.copyOf(data.exportedTypes); } public ImmutableSet getPackages() { - return ImmutableSet.copyOf(packages); + return ImmutableSet.copyOf(data.packages); } public ImmutableSet getMainClasses() { - return ImmutableSet.copyOf(mainClasses); + return ImmutableSet.copyOf(data.mainClasses); } public void parseClasses(Path directory, List files) throws IOException { @@ -214,7 +159,7 @@ public Void visitCompilationUnit(CompilationUnitTree t, Void v) { @Override public Void visitPackage(PackageTree p, Void v) { logger.debug("JavaTools: Got package {} for class: {}", p.getPackageName(), fileName); - packages.add(p.getPackageName().toString()); + data.packages.add(p.getPackageName().toString()); currentPackage = p.getPackageName().toString(); return super.visitPackage(p, v); } @@ -235,14 +180,14 @@ public Void visitImport(ImportTree i, Void v) { String name = i.getQualifiedIdentifier().toString(); if (i.isStatic()) { String staticPackage = name.substring(0, name.lastIndexOf('.')); - usedTypes.add(staticPackage); + data.usedTypes.add(staticPackage); } else if (name.endsWith(".*")) { String wildcardPackage = name.substring(0, name.lastIndexOf('.')); - usedPackagesWithoutSpecificTypes.add(wildcardPackage); + data.usedPackagesWithoutSpecificTypes.add(wildcardPackage); } else { String[] parts = i.getQualifiedIdentifier().toString().split("\\."); currentFileImports.put(parts[parts.length - 1], i.getQualifiedIdentifier().toString()); - usedTypes.add(name); + data.usedTypes.add(name); } return super.visitImport(i, v); } @@ -281,7 +226,7 @@ public Void visitMethod(com.sun.source.tree.MethodTree m, Void v) { } else if (m.getReturnType() != null) { Set types = checkFullyQualifiedType(m.getReturnType()); if (!m.getModifiers().getFlags().contains(PRIVATE)) { - exportedTypes.addAll(types); + data.exportedTypes.addAll(types); } } @@ -293,7 +238,7 @@ public Void visitMethod(com.sun.source.tree.MethodTree m, Void v) { && isVoidReturn) { String currentClassName = currentNestedClassNameWithoutPackage(); logger.debug("JavaTools: Found main method for {}", currentClassName); - mainClasses.add(currentClassName); + data.mainClasses.add(currentClassName); } // Check the parameters for the method @@ -399,10 +344,10 @@ private Set checkFullyQualifiedType(Tree identifier) { List components = Splitter.on(".").splitToList(typeName); if (currentFileImports.containsKey(components.get(0))) { String importedType = currentFileImports.get(components.get(0)); - usedTypes.add(importedType); + data.usedTypes.add(importedType); types.add(importedType); } else if (components.size() > 1) { - usedTypes.add(typeName); + data.usedTypes.add(typeName); types.add(typeName); } } else if (identifier.getKind() == Tree.Kind.PARAMETERIZED_TYPE) { @@ -466,10 +411,10 @@ private String currentFullyQualifiedClassName() { private void noteAnnotatedClass( String annotatedFullyQualifiedClassName, String annotationFullyQualifiedClassName) { - if (!perClassData.containsKey(annotatedFullyQualifiedClassName)) { - perClassData.put(annotatedFullyQualifiedClassName, new PerClassData()); + if (!data.perClassData.containsKey(annotatedFullyQualifiedClassName)) { + data.perClassData.put(annotatedFullyQualifiedClassName, new PerClassData()); } - perClassData + data.perClassData .get(annotatedFullyQualifiedClassName) .annotations .add(annotationFullyQualifiedClassName); @@ -479,27 +424,27 @@ private void noteAnnotatedMethod( String annotatedFullyQualifiedClassName, String methodName, String annotationFullyQualifiedClassName) { - if (!perClassData.containsKey(annotatedFullyQualifiedClassName)) { - perClassData.put(annotatedFullyQualifiedClassName, new PerClassData()); + if (!data.perClassData.containsKey(annotatedFullyQualifiedClassName)) { + data.perClassData.put(annotatedFullyQualifiedClassName, new PerClassData()); } - PerClassData data = perClassData.get(annotatedFullyQualifiedClassName); - if (!data.perMethodAnnotations.containsKey(methodName)) { - data.perMethodAnnotations.put(methodName, new TreeSet<>()); + PerClassData classData = data.perClassData.get(annotatedFullyQualifiedClassName); + if (!classData.perMethodAnnotations.containsKey(methodName)) { + classData.perMethodAnnotations.put(methodName, new TreeSet<>()); } - data.perMethodAnnotations.get(methodName).add(annotationFullyQualifiedClassName); + classData.perMethodAnnotations.get(methodName).add(annotationFullyQualifiedClassName); } private void noteAnnotatedField( String annotatedFullyQualifiedClassName, String fieldName, String annotationFullyQualifiedClassName) { - if (!perClassData.containsKey(annotatedFullyQualifiedClassName)) { - perClassData.put(annotatedFullyQualifiedClassName, new PerClassData()); + if (!data.perClassData.containsKey(annotatedFullyQualifiedClassName)) { + data.perClassData.put(annotatedFullyQualifiedClassName, new PerClassData()); } - PerClassData data = perClassData.get(annotatedFullyQualifiedClassName); - if (!data.perFieldAnnotations.containsKey(fieldName)) { - data.perFieldAnnotations.put(fieldName, new TreeSet<>()); + PerClassData classData = data.perClassData.get(annotatedFullyQualifiedClassName); + if (!classData.perFieldAnnotations.containsKey(fieldName)) { + classData.perFieldAnnotations.put(fieldName, new TreeSet<>()); } - data.perFieldAnnotations.get(fieldName).add(annotationFullyQualifiedClassName); + classData.perFieldAnnotations.get(fieldName).add(annotationFullyQualifiedClassName); } } diff --git a/java/src/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/GrpcServer.java b/java/src/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/GrpcServer.java index 62f85c7b..0445ed3e 100644 --- a/java/src/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/GrpcServer.java +++ b/java/src/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/GrpcServer.java @@ -29,6 +29,7 @@ import java.util.Set; import java.util.SortedSet; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -120,17 +121,55 @@ private Package getImports(ParsePackageRequest request) { logger.debug("Working relative directory: {}", request.getRel()); logger.debug("processing files: {}", files); - ClasspathParser parser = new ClasspathParser(); Path directory = workspace.resolve(request.getRel()); + // TODO: Make this tidier. + List kotlinFiles = + files.stream() + .filter(file -> file.endsWith(".kt")) + .map(directory::resolve) + .collect(Collectors.toList()); + List javaFiles = + files.stream().filter(file -> file.endsWith(".java")).collect(Collectors.toList()); + + ParsedPackageData data = new ParsedPackageData(); + if (!kotlinFiles.isEmpty()) { + try { + KtParser parser = new KtParser(); + ParsedPackageData kotlinData = parser.parseClasses(kotlinFiles); + data.merge(kotlinData); + } catch (Exception ex) { + logger.error("Error parsing Kotlin files", ex); + throw ex; + } + } - try { - parser.parseClasses(directory, files); - } catch (IOException exception) { - // If we fail to process a directory, which can happen with the module level processing - // or can't parse any of the files, just return an empty response. + ClasspathParser parser = new ClasspathParser(); + + if (!javaFiles.isEmpty()) { + try { + parser.parseClasses(directory, javaFiles); + ParsedPackageData javaData = parser.getParsedPackageData(); + data.merge(javaData); + } catch (IOException exception) { + // If we fail to process a directory, which can happen with the module level processing + // or can't parse any of the files, just return an empty response. + logger.debug("IOException occurred, returning empty package: {}", exception.getMessage()); + return Package.newBuilder().setName("").build(); + } catch (Exception ex) { + logger.error("Error parsing Java files", ex); + throw ex; + } + } else { + logger.debug("No Java files to process, skipping Java parser"); + } + + // If we have no files to process, return empty package early + if (kotlinFiles.isEmpty() && javaFiles.isEmpty()) { + logger.debug("No files to process, returning empty package"); return Package.newBuilder().setName("").build(); } - Set packages = parser.getPackages(); + + Set packages = data.packages; if (packages.size() > 1) { throw new StatusRuntimeException( Status.INVALID_ARGUMENT.withDescription( @@ -143,21 +182,18 @@ private Package getImports(ParsePackageRequest request) { packages = ImmutableSet.of(""); } logger.debug("Got package: {}", Iterables.getOnlyElement(packages)); - logger.debug("Got used types: {}", parser.getUsedTypes()); + logger.debug("Got used types: {}", data.usedTypes); logger.debug( - "Got used packages without specific types: {}", - parser.getUsedPackagesWithoutSpecificTypes()); + "Got used packages without specific types: {}", data.usedPackagesWithoutSpecificTypes); Builder packageBuilder = Package.newBuilder() .setName(Iterables.getOnlyElement(packages)) - .addAllImportedClasses(parser.getUsedTypes()) - .addAllExportedClasses(parser.getExportedTypes()) - .addAllImportedPackagesWithoutSpecificClasses( - parser.getUsedPackagesWithoutSpecificTypes()) - .addAllMains(parser.getMainClasses()); - for (Map.Entry classEntry : - parser.perClassData.entrySet()) { + .addAllImportedClasses(data.usedTypes) + .addAllExportedClasses(data.exportedTypes) + .addAllImportedPackagesWithoutSpecificClasses(data.usedPackagesWithoutSpecificTypes) + .addAllMains(data.mainClasses); + for (Map.Entry classEntry : data.perClassData.entrySet()) { PerClassMetadata.Builder perClassMetadata = PerClassMetadata.newBuilder() .addAllAnnotationClassNames(classEntry.getValue().annotations); @@ -179,6 +215,7 @@ private Package getImports(ParsePackageRequest request) { } packageBuilder.putPerClassMetadata(classEntry.getKey(), perClassMetadata.build()); } + return packageBuilder.build(); } } diff --git a/java/src/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/JavaIdentifier.java b/java/src/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/JavaIdentifier.java index a175af9a..b696ede0 100644 --- a/java/src/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/JavaIdentifier.java +++ b/java/src/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/JavaIdentifier.java @@ -9,11 +9,15 @@ public class JavaIdentifier implements Comparable { /** * Copied from the KnowTypeSolvers, this is the bazel dependency string where this package/class - * will be found: The dependency name will be of the form: - - * artifact("com.example.library:library") - For a dependency external to the repo and found in - * the maven cache - //src/java/com/example/library - For a dependency in the repo the root of thw - * source tree for searching - null - For all dependencies in the default java library or from - * generated code + * will be found: The dependency name will be of the form: + * + *
    + *
  • `artifact("com.example.library:library")` - For a dependency external to the repo and + * found in the maven cache * + *
  • `//src/java/com/example/library` - For a dependency in the repo the root of the source + * tree for searching + *
  • `null` - For all dependencies in the default java library or from generated code + *
*/ private final String sourceLibrary; diff --git a/java/src/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/KtParser.java b/java/src/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/KtParser.java new file mode 100644 index 00000000..841449f7 --- /dev/null +++ b/java/src/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/KtParser.java @@ -0,0 +1,1121 @@ +package com.github.bazel_contrib.contrib_rules_jvm.javaparser.generators; + +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.VirtualFileManager; +import com.intellij.psi.PsiManager; +import com.intellij.psi.tree.IElementType; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.Stack; +import java.util.TreeSet; +import java.util.stream.Collectors; +import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles; +import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment; +import org.jetbrains.kotlin.config.CommonConfigurationKeys; +import org.jetbrains.kotlin.config.CompilerConfiguration; +import org.jetbrains.kotlin.lexer.KtTokens; +import org.jetbrains.kotlin.name.FqName; +import org.jetbrains.kotlin.name.FqNamesUtilKt; +import org.jetbrains.kotlin.name.Name; +import org.jetbrains.kotlin.name.NameUtils; +import org.jetbrains.kotlin.psi.KtAnnotated; +import org.jetbrains.kotlin.psi.KtBinaryExpression; +import org.jetbrains.kotlin.psi.KtCallExpression; +import org.jetbrains.kotlin.psi.KtClass; +import org.jetbrains.kotlin.psi.KtClassBody; +import org.jetbrains.kotlin.psi.KtDestructuringDeclaration; +import org.jetbrains.kotlin.psi.KtDestructuringDeclarationEntry; +import org.jetbrains.kotlin.psi.KtDotQualifiedExpression; +import org.jetbrains.kotlin.psi.KtElement; +import org.jetbrains.kotlin.psi.KtExpression; +import org.jetbrains.kotlin.psi.KtFile; +import org.jetbrains.kotlin.psi.KtImportDirective; +import org.jetbrains.kotlin.psi.KtModifierListOwner; +import org.jetbrains.kotlin.psi.KtNamedFunction; +import org.jetbrains.kotlin.psi.KtNullableType; +import org.jetbrains.kotlin.psi.KtObjectDeclaration; +import org.jetbrains.kotlin.psi.KtPackageDirective; +import org.jetbrains.kotlin.psi.KtParameter; +import org.jetbrains.kotlin.psi.KtProperty; +import org.jetbrains.kotlin.psi.KtQualifiedExpression; +import org.jetbrains.kotlin.psi.KtReferenceExpression; +import org.jetbrains.kotlin.psi.KtSafeQualifiedExpression; +import org.jetbrains.kotlin.psi.KtSimpleNameExpression; +import org.jetbrains.kotlin.psi.KtTreeVisitorVoid; +import org.jetbrains.kotlin.psi.KtTypeElement; +import org.jetbrains.kotlin.psi.KtTypeReference; +import org.jetbrains.kotlin.psi.KtUnaryExpression; +import org.jetbrains.kotlin.psi.KtUserType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class KtParser { + private static final Logger logger = LoggerFactory.getLogger(GrpcServer.class); + + private final CompilerConfiguration compilerConf = createCompilerConfiguration(); + private final KotlinCoreEnvironment env = + KotlinCoreEnvironment.createForProduction( + Disposer.newDisposable(), compilerConf, EnvironmentConfigFiles.JVM_CONFIG_FILES); + private final VirtualFileManager vfm = VirtualFileManager.getInstance(); + private final PsiManager psiManager = PsiManager.getInstance(env.getProject()); + + public ParsedPackageData parseClasses(List files) { + KtFileVisitor visitor = new KtFileVisitor(); + List virtualFiles = + files.stream().map(f -> vfm.findFileByNioPath(f)).collect(Collectors.toUnmodifiableList()); + + for (VirtualFile virtualFile : virtualFiles) { + if (virtualFile == null) { + throw new IllegalArgumentException("File not found: " + files.get(0)); + } + KtFile ktFile = (KtFile) psiManager.findFile(virtualFile); + + logger.debug("ktFile: {}", ktFile); + logger.debug("name: {}", ktFile.getName()); + logger.debug("script: {}", ktFile.getScript()); + logger.debug("declarations: {}", ktFile.getDeclarations()); + logger.debug( + "classes: {}", + Arrays.stream(ktFile.getClasses()) + .map(Object::toString) + .collect(Collectors.joining(", "))); + logger.debug("import directives: {}", ktFile.getImportDirectives()); + logger.debug("import list: {}", ktFile.getImportList()); + + ktFile.accept(visitor); + } + + return visitor.packageData; + } + + private static CompilerConfiguration createCompilerConfiguration() { + CompilerConfiguration conf = new CompilerConfiguration(); + conf.put(CommonConfigurationKeys.MODULE_NAME, "bazel-module"); + + return conf; + } + + public static class KtFileVisitor extends KtTreeVisitorVoid { + final ParsedPackageData packageData = new ParsedPackageData(); + + private Stack visibilityStack = new Stack<>(); + private HashMap fqImportByNameOrAlias = new HashMap<>(); + + // Track inline functions and their dependencies + private String currentInlineFunction = null; + private Set currentInlineFunctionDeps = null; + + // Track extension functions and their dependencies + private String currentExtensionFunction = null; + private String currentExtensionReceiverType = null; + private Set currentExtensionFunctionDeps = null; + + // Track property delegates and their dependencies + private boolean currentlyInPropertyDelegate = false; + private Set currentPropertyDelegateDeps = null; + + // Track destructuring declarations and their dependencies + private Set detectedDestructuringDeclarations = new TreeSet<>(); + + // Track componentN() functions and their dependencies for destructuring analysis + private Map> componentFunctionDeps = new HashMap<>(); + + // Track when we're inside a componentN() function to detect its dependencies + private String currentComponentFunction = null; + private Set currentComponentFunctionDeps = null; + + @Override + public void visitPackageDirective(KtPackageDirective packageDirective) { + packageData.packages.add(packageDirective.getQualifiedName()); + super.visitPackageDirective(packageDirective); + } + + @Override + public void visitKtFile(KtFile file) { + if (file.hasTopLevelCallables()) { + FqName filePackage = file.getPackageFqName(); + String outerClassName = NameUtils.getScriptNameForFile(file.getName()) + "Kt"; + FqName outerClassFqName = filePackage.child(Name.identifier(outerClassName)); + packageData.perClassData.put(outerClassFqName.toString(), new PerClassData()); + } + super.visitKtFile(file); + + // Log AST-based usage statistics + logger.debug( + "AST: Destructuring declarations detected: " + detectedDestructuringDeclarations.size()); + } + + @Override + public void visitImportDirective(KtImportDirective importDirective) { + FqName importName = importDirective.getImportedFqName(); + boolean foundClass = false; + int segmentCount = 0; + List pathSegments = importName.pathSegments(); + for (Name importPart : pathSegments) { + segmentCount++; + // If there is a PascalCase component, assume it's a class. + if (isLikelyClassName(importPart.asString())) { + foundClass = true; + break; + } + } + if (foundClass) { + FqName className = importName; + if (!isLikelyClassName(importName.shortName().asString())) { + // If we're directly importing a function from a parent class or object, use the parent. + className = importName.parent(); + } + String localName = importDirective.getAliasName(); + if (localName == null) { + localName = className.shortName().toString(); + } + packageData.usedTypes.add(className.toString()); + fqImportByNameOrAlias.put(localName, className); + } else { + if (importDirective.isAllUnder()) { + // If it's a wildcard import with no obvious class name, assume it's a package. + packageData.usedPackagesWithoutSpecificTypes.add(importName.asString()); + } else { + // If it's not a wildcard import and lacks an obvious class name, assume it's a function + // in a package. + packageData.usedPackagesWithoutSpecificTypes.add(importName.parent().asString()); + } + } + super.visitImportDirective(importDirective); + } + + @Override + public void visitClass(KtClass clazz) { + pushState(clazz); + if (clazz.isLocal() || !isVisible()) { + super.visitClass(clazz); + popState(clazz); + return; + } + packageData.perClassData.put(clazz.getFqName().toString(), new PerClassData()); + super.visitClass(clazz); + popState(clazz); + } + + @Override + public void visitObjectDeclaration(KtObjectDeclaration object) { + pushState(object); + if (object.isLocal() || !isVisible()) { + super.visitObjectDeclaration(object); + popState(object); + return; + } + + packageData.perClassData.put(object.getFqName().toString(), new PerClassData()); + + super.visitObjectDeclaration(object); + popState(object); + } + + @Override + public void visitProperty(KtProperty property) { + pushState(property); + if (property.isLocal() || !isVisible()) { + super.visitProperty(property); + popState(property); + return; + } + + KtTypeReference typeReference = property.getTypeReference(); + if (typeReference != null) { + addExportedTypeIfNeeded(typeReference); + } + + // Check if this property has a delegate (uses 'by' keyword) + if (property.hasDelegate()) { + FqName propertyFqName = getPropertyFqName(property); + currentlyInPropertyDelegate = true; + currentPropertyDelegateDeps = new TreeSet<>(); + + // Get delegate expression to determine delegate type + KtExpression delegateExpression = property.getDelegateExpression(); + String delegateType = "unknown"; + if (delegateExpression != null) { + delegateType = getDelegateType(delegateExpression); + logger.debug( + "Found property delegate: " + + propertyFqName + + " with delegate type: " + + delegateType); + } + } + + super.visitProperty(property); + + // If this was a property delegate, add its dependencies to exported types + if (property.hasDelegate() && currentlyInPropertyDelegate) { + packageData.exportedTypes.addAll(currentPropertyDelegateDeps); + logger.debug( + "Property delegate " + + getPropertyFqName(property) + + " added " + + currentPropertyDelegateDeps.size() + + " exported types (from property delegate): " + + currentPropertyDelegateDeps); + currentlyInPropertyDelegate = false; + currentPropertyDelegateDeps = null; + } + + popState(property); + } + + @Override + public void visitNamedFunction(KtNamedFunction function) { + pushState(function); + if (function.isLocal() || !isVisible()) { + super.visitNamedFunction(function); + popState(function); + return; + } + + // Check if this is a componentN() function for destructuring + boolean isComponentFunction = + function.getName() != null + && function.getName().startsWith("component") + && function.hasModifier(KtTokens.OPERATOR_KEYWORD); + if (isComponentFunction) { + // Start tracking dependencies for this componentN() function + FqName functionFqName = getFunctionFqName(function); + currentComponentFunction = functionFqName.toString(); + currentComponentFunctionDeps = new TreeSet<>(); + logger.debug("Found componentN() function: " + currentComponentFunction); + } + + // Check if this is an inline function + boolean isInline = function.hasModifier(KtTokens.INLINE_KEYWORD); + logger.debug( + "Checking function: " + + function.getName() + + ", isInline: " + + isInline + + ", modifiers: " + + function.getModifierList()); + if (isInline) { + // Start tracking dependencies for this inline function + FqName functionFqName = getFunctionFqName(function); + currentInlineFunction = functionFqName.toString(); + currentInlineFunctionDeps = new TreeSet<>(); + logger.debug("Found inline function: " + currentInlineFunction); + } + // Check if this is an extension function + boolean isExtension = function.getReceiverTypeReference() != null; + logger.debug("Checking function: " + function.getName() + ", isExtension: " + isExtension); + if (isExtension) { + // Start tracking dependencies for this extension function + FqName functionFqName = getFunctionFqName(function); + currentExtensionFunction = functionFqName.toString(); + currentExtensionFunctionDeps = new TreeSet<>(); + + // Get receiver type + KtTypeReference receiverTypeRef = function.getReceiverTypeReference(); + if (receiverTypeRef != null) { + currentExtensionReceiverType = getTypeString(receiverTypeRef); + logger.debug( + "Found extension function: " + + currentExtensionFunction + + " extending " + + currentExtensionReceiverType); + } else { + currentExtensionReceiverType = "unknown"; + } + } + + if (hasMainFunctionShape(function)) { + if (function.isTopLevel()) { + FqName outerClassFqName = javaClassNameForKtFile(function.getContainingKtFile()); + String outerClassName = outerClassFqName.shortName().asString(); + packageData.mainClasses.add(outerClassName); + } else if (function.getParent().getParent() instanceof KtObjectDeclaration) { + // The parent is a class/object body, then the next parent should be a class or object. + KtObjectDeclaration object = (KtObjectDeclaration) function.getParent().getParent(); + FqName relativeFqName = + packageRelativeName(object.getFqName(), object.getContainingKtFile()); + if (isJvmStatic(function)) { + packageData.mainClasses.add(relativeFqName.parent().toString()); + } else { + packageData.mainClasses.add(relativeFqName.asString()); + } + } + } + + KtTypeReference returnType = function.getTypeReference(); + if (returnType != null) { + addExportedTypeIfNeeded(returnType); + } + + // Visit the function body to collect dependencies + super.visitNamedFunction(function); + + // If this was an inline function, add its dependencies to exported types + if (isInline) { + packageData.exportedTypes.addAll(currentInlineFunctionDeps); + logger.debug( + "Inline function " + + currentInlineFunction + + " added " + + currentInlineFunctionDeps.size() + + " exported types (from inline function): " + + currentInlineFunctionDeps); + currentInlineFunction = null; + currentInlineFunctionDeps = null; + } + // If this was an extension function, add its dependencies to exported types + if (isExtension) { + packageData.exportedTypes.addAll(currentExtensionFunctionDeps); + logger.debug( + "Extension function " + + currentExtensionFunction + + " extending " + + currentExtensionReceiverType + + " added " + + currentExtensionFunctionDeps.size() + + " exported types (from extension function): " + + currentExtensionFunctionDeps); + currentExtensionFunction = null; + currentExtensionReceiverType = null; + currentExtensionFunctionDeps = null; + } + + // If this was a componentN() function, save its dependencies + if (isComponentFunction) { + componentFunctionDeps.put(currentComponentFunction, currentComponentFunctionDeps); + // Also add these dependencies to the exported types since they'll be needed for + // destructuring + packageData.exportedTypes.addAll(currentComponentFunctionDeps); + logger.debug( + "ComponentN function " + + currentComponentFunction + + " uses: " + + currentComponentFunctionDeps); + currentComponentFunction = null; + currentComponentFunctionDeps = null; + } + + popState(function); + } + + @Override + public void visitQualifiedExpression(KtQualifiedExpression expression) { + logger.debug("Qualified expression: " + expression.getText()); + + super.visitQualifiedExpression(expression); + } + + @Override + public void visitTypeReference(KtTypeReference reference) { + logger.debug("Type reference: " + reference.getText()); + + super.visitTypeReference(reference); + } + + @Override + public void visitReferenceExpression(KtReferenceExpression reference) { + logger.debug("Reference expression: " + reference.getText()); + + super.visitReferenceExpression(reference); + } + + @Override + public void visitSimpleNameExpression(KtSimpleNameExpression expression) { + logger.debug( + "Simple name expression: {}, Referenced name: {}", + expression.getText(), + expression.getReferencedName()); + + // If we're inside an inline function, track class usage + if (currentInlineFunction != null) { + String referencedName = expression.getReferencedName(); + + // Check if this is a class reference (constructor call or type reference) + // This includes both PascalCase class names and constructor calls + if (isLikelyClassName(referencedName)) { + // Try to resolve to fully qualified name + if (fqImportByNameOrAlias.containsKey(referencedName)) { + String fqName = fqImportByNameOrAlias.get(referencedName).toString(); + currentInlineFunctionDeps.add(fqName); + logger.debug("Inline function " + currentInlineFunction + " uses class: " + fqName); + } else if (referencedName.contains(".")) { + // Already fully qualified + currentInlineFunctionDeps.add(referencedName); + logger.debug( + "Inline function " + currentInlineFunction + " uses class: " + referencedName); + } else { + // For unresolved class names, add them as potential dependencies + // This handles cases where imports might not be fully resolved + logger.debug( + "Inline function " + + currentInlineFunction + + " uses unresolved class: " + + referencedName); + } + } + } + + // If we're inside an extension function, track class usage + if (currentExtensionFunction != null) { + String referencedName = expression.getReferencedName(); + + // Check if this is a class reference (constructor call or type reference) + if (isLikelyClassName(referencedName)) { + // Try to resolve to fully qualified name + if (fqImportByNameOrAlias.containsKey(referencedName)) { + String fqName = fqImportByNameOrAlias.get(referencedName).toString(); + currentExtensionFunctionDeps.add(fqName); + logger.debug( + "Extension function " + currentExtensionFunction + " uses class: " + fqName); + } else if (referencedName.contains(".")) { + // Already fully qualified + currentExtensionFunctionDeps.add(referencedName); + logger.debug( + "Extension function " + + currentExtensionFunction + + " uses class: " + + referencedName); + } else { + logger.debug( + "Extension function " + + currentExtensionFunction + + " uses unresolved class: " + + referencedName); + } + } + } + + // If we're inside a property delegate, track class usage + if (currentlyInPropertyDelegate) { + String referencedName = expression.getReferencedName(); + + // Check if this is a class reference (constructor call or type reference) + if (isLikelyClassName(referencedName)) { + // Try to resolve to fully qualified name + if (fqImportByNameOrAlias.containsKey(referencedName)) { + String fqName = fqImportByNameOrAlias.get(referencedName).toString(); + currentPropertyDelegateDeps.add(fqName); + logger.debug("Property delegate uses class: " + fqName); + } else if (referencedName.contains(".")) { + // Already fully qualified + currentPropertyDelegateDeps.add(referencedName); + logger.debug("Property delegate uses class: " + referencedName); + } else { + logger.debug("Property delegate uses unresolved class: " + referencedName); + } + } + } + + // If we're inside a componentN() function, track class usage + if (currentComponentFunction != null) { + String referencedName = expression.getReferencedName(); + + // Check if this is a class reference (constructor call or type reference) + if (isLikelyClassName(referencedName)) { + // Try to resolve to fully qualified name + if (fqImportByNameOrAlias.containsKey(referencedName)) { + String fqName = fqImportByNameOrAlias.get(referencedName).toString(); + currentComponentFunctionDeps.add(fqName); + logger.debug( + "ComponentN function " + currentComponentFunction + " uses class: " + fqName); + } else if (referencedName.contains(".")) { + // Already fully qualified + currentComponentFunctionDeps.add(referencedName); + logger.debug( + "ComponentN function " + + currentComponentFunction + + " uses class: " + + referencedName); + } else { + logger.debug( + "ComponentN function " + + currentComponentFunction + + " uses unresolved class: " + + referencedName); + } + } + } + + super.visitSimpleNameExpression(expression); + } + + @Override + public void visitCallExpression(KtCallExpression expression) { + logger.debug("AST: Call expression: " + expression.getText()); + + KtExpression calleeExpression = expression.getCalleeExpression(); + if (calleeExpression != null) { + String functionName = calleeExpression.getText(); + logger.debug("AST: Function call detected: " + functionName); + + // Check if this is a call to a known inline function + checkInlineFunctionUsage(functionName); + } + + super.visitCallExpression(expression); + } + + @Override + public void visitBinaryExpression(KtBinaryExpression expression) { + logger.debug("AST: Binary expression: " + expression.getText()); + + IElementType operationToken = expression.getOperationToken(); + if (operationToken != null) { + String operator = operationToken.toString(); + logger.debug("AST: Operator usage detected: " + operator); + + checkExtensionOperatorUsage(operator, expression); + } + + super.visitBinaryExpression(expression); + } + + @Override + public void visitUnaryExpression(KtUnaryExpression expression) { + logger.debug("AST: Unary expression: " + expression.getText()); + + IElementType operationToken = expression.getOperationToken(); + if (operationToken != null) { + String operator = operationToken.toString(); + logger.debug("AST: Unary operator usage detected: " + operator); + + checkExtensionOperatorUsage(operator, expression); + } + + super.visitUnaryExpression(expression); + } + + @Override + public void visitDotQualifiedExpression(KtDotQualifiedExpression expression) { + logger.debug("AST: Dot qualified expression: " + expression.getText()); + + KtExpression selectorExpression = expression.getSelectorExpression(); + if (selectorExpression instanceof KtCallExpression) { + KtCallExpression callExpr = (KtCallExpression) selectorExpression; + KtExpression receiverExpression = expression.getReceiverExpression(); + if (receiverExpression != null && callExpr.getCalleeExpression() != null) { + String receiverType = getSimpleExpressionType(receiverExpression); + String functionName = callExpr.getCalleeExpression().getText(); + + checkExtensionFunctionCall(receiverType, functionName); + } + } + + super.visitDotQualifiedExpression(expression); + } + + @Override + public void visitSafeQualifiedExpression(KtSafeQualifiedExpression expression) { + logger.debug("AST: Safe qualified expression: " + expression.getText()); + + KtExpression selectorExpression = expression.getSelectorExpression(); + if (selectorExpression instanceof KtCallExpression) { + KtCallExpression callExpr = (KtCallExpression) selectorExpression; + KtExpression receiverExpression = expression.getReceiverExpression(); + if (receiverExpression != null && callExpr.getCalleeExpression() != null) { + String receiverType = getSimpleExpressionType(receiverExpression); + String functionName = callExpr.getCalleeExpression().getText(); + + checkExtensionFunctionCall(receiverType, functionName); + } + } + + super.visitSafeQualifiedExpression(expression); + } + + @Override + public void visitDestructuringDeclaration(KtDestructuringDeclaration declaration) { + logger.debug("AST: Destructuring declaration: " + declaration.getText()); + + String destructuringText = declaration.getText(); + detectedDestructuringDeclarations.add(destructuringText); + + // Get the initializer expression (the right side of the assignment) + KtExpression initializer = declaration.getInitializer(); + if (initializer != null) { + String initializerType = getSimpleExpressionType(initializer); + logger.debug("AST: Destructuring initializer type: " + initializerType); + + // Get the number of components being destructured + List entries = declaration.getEntries(); + int componentCount = entries.size(); + + logger.debug( + "AST: Destructuring " + componentCount + " components from " + initializerType); + + // Check if this destructuring uses custom componentN() functions that might have + // dependencies + checkDestructuringDependencies(initializerType, componentCount, declaration); + } + + super.visitDestructuringDeclaration(declaration); + } + + /** Check if destructuring uses custom componentN() functions with external dependencies. */ + private void checkDestructuringDependencies( + String initializerType, int componentCount, KtDestructuringDeclaration declaration) { + logger.debug( + "AST: Checking destructuring dependencies for " + + initializerType + + " with " + + componentCount + + " components"); + + // Skip built-in types that have automatic componentN() functions + if (isBuiltInDataType(initializerType)) { + logger.debug("AST: Skipping built-in data type: " + initializerType); + return; + } + + // Try to resolve the initializer type to see if it's a custom class + String resolvedType = resolveTypeToFqName(initializerType); + if (resolvedType != null && !resolvedType.startsWith("kotlin.")) { + // This appears to be a custom class - check if it defines componentN() functions + logger.debug("AST: Custom type detected for destructuring: " + resolvedType); + + // For destructuring, we don't need to match specific componentN() functions + // The componentN() functions have already been processed and their dependencies + // added to exportedTypes when we visited them. The destructuring just triggers + // the need for those dependencies to be available. + + // Since we're in the same package as the componentN() functions, and those + // functions have already added their dependencies to exportedTypes, we don't + // need to do anything additional here. The dependencies are already captured. + + logger.debug( + "AST: Destructuring of " + + resolvedType + + " will use componentN() dependencies already captured"); + } + } + + /** + * Check if a type is a built-in data type that automatically provides componentN() functions. + */ + private boolean isBuiltInDataType(String typeName) { + // Built-in types that automatically provide componentN() functions + return typeName.equals("Pair") + || typeName.equals("Triple") + || typeName.startsWith("kotlin.Pair") + || typeName.startsWith("kotlin.Triple") + || + // Data classes automatically generate componentN() functions, + // but we can't easily detect if a class is a data class from just the type name + // So we'll be conservative and only skip obviously built-in types + false; + } + + /** Resolve a type name to its fully qualified name using imports. */ + private String resolveTypeToFqName(String typeName) { + // Try to resolve using imports + if (fqImportByNameOrAlias.containsKey(typeName)) { + return fqImportByNameOrAlias.get(typeName).toString(); + } + + // If it's already fully qualified, return as-is + if (typeName.contains(".")) { + return typeName; + } + + // For unresolved types, return null + return null; + } + + /** Get statistics about destructuring declarations detected during parsing. */ + public int getDestructuringDeclarationCount() { + return detectedDestructuringDeclarations.size(); + } + + /** Check if a function call is to a known inline function and track its usage. */ + private void checkInlineFunctionUsage(String functionName) { + // This method is kept for potential future use but currently does nothing + // since inline functions are now handled through implicit deps + logger.debug("AST: Function call detected: " + functionName); + } + + /** Check if an extension function is called on a specific receiver type. */ + private void checkExtensionFunctionCall(String receiverType, String functionName) { + // This method is kept for potential future use but currently does nothing + // since extension functions are now handled through implicit deps + logger.debug("AST: Checking extension function call: " + receiverType + "." + functionName); + } + + /** Check if an operator usage corresponds to an extension operator. */ + private void checkExtensionOperatorUsage(String operator, KtExpression expression) { + // This method is kept for potential future use but currently does nothing + // since extension operators are now handled through implicit deps + logger.debug("AST: Checking extension operator usage: " + operator); + } + + /** Map operator tokens to their corresponding function names. */ + private String mapOperatorToFunctionName(String operator) { + switch (operator) { + case "PLUS": + return "plus"; + case "MINUS": + return "minus"; + case "MUL": + return "times"; + case "DIV": + return "div"; + case "PERC": + return "rem"; + case "PLUSPLUS": + return "inc"; + case "MINUSMINUS": + return "dec"; + case "EXCL": + return "not"; + case "EQEQ": + return "equals"; + case "GT": + case "LT": + case "GTEQ": + case "LTEQ": + return "compareTo"; + case "IN_KEYWORD": + case "NOT_IN": + return "contains"; + case "RANGE": + return "rangeTo"; + case "RANGE_UNTIL": + return "rangeUntil"; + case "PLUSEQ": + return "plusAssign"; + case "MINUSEQ": + return "minusAssign"; + case "MULTEQ": + return "timesAssign"; + case "DIVEQ": + return "divAssign"; + case "PERCEQ": + return "remAssign"; + default: + return null; + } + } + + /** Get the type of an expression using simple heuristics. */ + private String getSimpleExpressionType(KtExpression expression) { + if (expression instanceof KtSimpleNameExpression) { + KtSimpleNameExpression simpleExpr = (KtSimpleNameExpression) expression; + String name = simpleExpr.getReferencedName(); + + if (fqImportByNameOrAlias.containsKey(name)) { + return fqImportByNameOrAlias.get(name).toString(); + } + + switch (name) { + case "String": + return "java.lang.String"; + case "Int": + return "java.lang.Integer"; + case "Double": + return "java.lang.Double"; + case "Boolean": + return "java.lang.Boolean"; + case "List": + return "java.util.List"; + case "Set": + return "java.util.Set"; + case "Map": + return "java.util.Map"; + default: + return name; + } + } + + return expression.getText(); + } + + private FqName packageRelativeName(FqName name, KtFile file) { + return FqNamesUtilKt.tail(name, file.getPackageFqName()); + } + + /** Returns true if this simple name is PascalCase. */ + private boolean isLikelyClassName(String name) { + if (name.isEmpty() || !firstLetterIsUppercase(name)) { + return false; + } + // If the name is all uppercase, assume it's a constant. At worst, we'll still + // import the package, which seems a safer default than assuming it's a class + // that we then can't find. + for (int i = 1; i < name.length(); i++) { + char c = name.charAt(i); + if (Character.isLetter(c) && Character.isLowerCase(c)) { + return true; + } + } + return false; + } + + private boolean firstLetterIsUppercase(String value) { + for (int i = 0; i < value.length(); i++) { + char c = value.charAt(i); + if (Character.isLetter(c)) { + return Character.isUpperCase(c); + } + } + return false; + } + + private Optional retrievePossibleMainFunction(KtObjectDeclaration object) { + KtClassBody body = object.getBody(); + for (KtNamedFunction function : body.getFunctions()) { + if (hasMainFunctionShape(function)) { + return Optional.of(function); + } + } + return Optional.empty(); + } + + private boolean hasMainFunctionShape(KtNamedFunction function) { + // TODO: Check return type + if (!"main".equals(function.getName()) + || function.hasModifier(KtTokens.INTERNAL_KEYWORD) + || function.hasModifier(KtTokens.PROTECTED_KEYWORD) + || function.hasModifier(KtTokens.PRIVATE_KEYWORD)) { + return false; + } + List parameters = function.getValueParameters(); + if (parameters.size() > 1) { + return false; + } else if (parameters.size() == 1) { + KtParameter parameter = parameters.get(0); + boolean isValidMainArgs = + (parameter.isVarArg() && parameter.getTypeReference().getTypeText().equals("String")) + || parameter.getTypeReference().getTypeText().equals("Array"); + if (!isValidMainArgs) { + return false; + } + } + return true; + } + + private boolean isVisible() { + return !visibilityStack.contains(Visibility.PRIVATE); + } + + private boolean isJvmStatic(KtAnnotated annotatedThing) { + return annotatedThing.getAnnotationEntries().stream() + .anyMatch(entry -> entry.getTypeReference().getTypeText().equals("JvmStatic")); + } + + private void addExportedTypeIfNeeded(KtTypeReference theType) { + KtTypeElement typeElement = getRootType(theType); + Optional maybeQualifiedType = tryGetFullyQualifiedName(typeElement); + // TODO: Check for java and Kotlin standard library types. + maybeQualifiedType.ifPresent(packageData.exportedTypes::add); + } + + private KtTypeElement getRootType(KtTypeReference typeReference) { + KtTypeElement typeElement = typeReference.getTypeElement(); + if (typeElement instanceof KtNullableType) { + KtNullableType nullableType = (KtNullableType) typeElement; + return nullableType.getInnerType(); + } + return typeElement; + } + + private Optional tryGetFullyQualifiedName(KtTypeElement typeElement) { + if (typeElement instanceof KtUserType) { + KtUserType userType = (KtUserType) typeElement; + String identifier = userType.getReferencedName(); + if (identifier.contains(".")) { + return Optional.of(identifier); + } else { + if (fqImportByNameOrAlias.containsKey(identifier)) { + return Optional.of(fqImportByNameOrAlias.get(identifier).toString()); + } else { + return Optional.empty(); + } + } + } else { + return Optional.empty(); + } + } + + private FqName javaClassNameForKtFile(KtFile file) { + FqName filePackage = file.getPackageFqName(); + String outerClassName = NameUtils.getScriptNameForFile(file.getName()) + "Kt"; + return filePackage.child(Name.identifier(outerClassName)); + } + + /** Get the string representation of a type reference. */ + private String getTypeString(KtTypeReference typeRef) { + if (typeRef == null) { + return "unknown"; + } + + // Get the text representation and try to resolve it + String typeText = typeRef.getText(); + + // Try to resolve to fully qualified name if it's in imports + if (fqImportByNameOrAlias.containsKey(typeText)) { + return fqImportByNameOrAlias.get(typeText).toString(); + } + + // For built-in types like String, Int, etc., add java.lang prefix if needed + if (typeText.equals("String")) { + return "java.lang.String"; + } else if (typeText.equals("Int")) { + return "java.lang.Integer"; + } else if (typeText.equals("Double")) { + return "java.lang.Double"; + } else if (typeText.equals("Boolean")) { + return "java.lang.Boolean"; + } + + // Return as-is for now (could be a local class or unresolved type) + return typeText; + } + + private FqName getFunctionFqName(KtNamedFunction function) { + String functionName = function.getName(); + + // Check if it's a top-level function + if (function.isTopLevel()) { + // Top-level functions belong to the file's generated class + FqName fileFqName = javaClassNameForKtFile(function.getContainingKtFile()); + return fileFqName.child(Name.identifier(functionName)); + } + + // Check if it's inside a class + if (function.getParent().getParent() instanceof KtClass) { + KtClass clazz = (KtClass) function.getParent().getParent(); + FqName classFqName = clazz.getFqName(); + return classFqName.child(Name.identifier(functionName)); + } + + // Check if it's inside an object + if (function.getParent().getParent() instanceof KtObjectDeclaration) { + KtObjectDeclaration object = (KtObjectDeclaration) function.getParent().getParent(); + FqName objectFqName = object.getFqName(); + return objectFqName.child(Name.identifier(functionName)); + } + + // Fallback: use the file package and function name + FqName packageFqName = function.getContainingKtFile().getPackageFqName(); + return packageFqName.child(Name.identifier(functionName)); + } + + /** Get the fully qualified name for a property. */ + private FqName getPropertyFqName(KtProperty property) { + String propertyName = property.getName(); + + // Check if it's a top-level property + if (property.isTopLevel()) { + // Top-level properties belong to the file's generated class + FqName fileFqName = javaClassNameForKtFile(property.getContainingKtFile()); + return fileFqName.child(Name.identifier(propertyName)); + } + + // Check if it's inside a class + if (property.getParent().getParent() instanceof KtClass) { + KtClass clazz = (KtClass) property.getParent().getParent(); + FqName classFqName = clazz.getFqName(); + return classFqName.child(Name.identifier(propertyName)); + } + + // Check if it's inside an object + if (property.getParent().getParent() instanceof KtObjectDeclaration) { + KtObjectDeclaration object = (KtObjectDeclaration) property.getParent().getParent(); + FqName objectFqName = object.getFqName(); + return objectFqName.child(Name.identifier(propertyName)); + } + + // Fallback: use the file package and property name + FqName packageFqName = property.getContainingKtFile().getPackageFqName(); + return packageFqName.child(Name.identifier(propertyName)); + } + + /** Determine the delegate type from a delegate expression. */ + private String getDelegateType(KtExpression delegateExpression) { + String expressionText = delegateExpression.getText(); + + // Handle common delegate patterns + if (expressionText.startsWith("lazy")) { + return "kotlin.Lazy"; + } else if (expressionText.contains("observable")) { + return "kotlin.properties.ObservableProperty"; + } else if (expressionText.contains("vetoable")) { + return "kotlin.properties.VetoableProperty"; + } else if (expressionText.contains("notNull")) { + return "kotlin.properties.NotNullVar"; + } else if (expressionText.contains("Delegates.")) { + // Extract delegate type from Delegates.xxx() calls + if (expressionText.contains("Delegates.observable")) { + return "kotlin.properties.ObservableProperty"; + } else if (expressionText.contains("Delegates.vetoable")) { + return "kotlin.properties.VetoableProperty"; + } else if (expressionText.contains("Delegates.notNull")) { + return "kotlin.properties.NotNullVar"; + } + } + + // Try to resolve the delegate expression type using simple heuristics + if (delegateExpression instanceof KtCallExpression) { + KtCallExpression callExpr = (KtCallExpression) delegateExpression; + KtExpression calleeExpr = callExpr.getCalleeExpression(); + if (calleeExpr instanceof KtSimpleNameExpression) { + KtSimpleNameExpression simpleExpr = (KtSimpleNameExpression) calleeExpr; + String calleeName = simpleExpr.getReferencedName(); + + // Try to resolve from imports + if (fqImportByNameOrAlias.containsKey(calleeName)) { + return fqImportByNameOrAlias.get(calleeName).toString(); + } + + // Return the simple name as a fallback + return calleeName; + } + } + + // Fallback: return the expression text as-is + return expressionText; + } + + private void pushState(KtElement element) { + if (element instanceof KtModifierListOwner) { + KtModifierListOwner modifiedThing = (KtModifierListOwner) element; + pushVisibility(modifiedThing); + } + } + + private void popState(KtElement element) { + if (element instanceof KtModifierListOwner) { + popVisibility(); + } + } + + private void pushVisibility(KtModifierListOwner modifiedThing) { + if (modifiedThing.hasModifier(KtTokens.PROTECTED_KEYWORD)) { + visibilityStack.push(Visibility.PROTECTED); + } else if (modifiedThing.hasModifier(KtTokens.INTERNAL_KEYWORD)) { + visibilityStack.push(Visibility.INTERNAL); + } else if (modifiedThing.hasModifier(KtTokens.PRIVATE_KEYWORD)) { + visibilityStack.push(Visibility.PRIVATE); + } else { + visibilityStack.push(Visibility.PUBLIC); + } + } + + private void popVisibility() { + visibilityStack.pop(); + } + } + + private enum Visibility { + PUBLIC, + PROTECTED, + INTERNAL, + PRIVATE, + } +} diff --git a/java/src/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/ParsedPackageData.java b/java/src/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/ParsedPackageData.java new file mode 100644 index 00000000..a38a423e --- /dev/null +++ b/java/src/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/ParsedPackageData.java @@ -0,0 +1,47 @@ +package com.github.bazel_contrib.contrib_rules_jvm.javaparser.generators; + +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; + +class ParsedPackageData { + /** Packages defined. */ + final Set packages = new TreeSet<>(); + + /** The fully qualified name of types that are imported. */ + final Set usedTypes = new TreeSet<>(); + + /** The name of passages that are imported for wildcards or (in Kotlin) direct function access. */ + final Set usedPackagesWithoutSpecificTypes = new TreeSet<>(); + + /** The fully qualified names of types that should be exported by this build rule. */ + final Set exportedTypes = new TreeSet<>(); + + /** The short name (no package) of any classes that provide a public static main function. */ + final Set mainClasses = new TreeSet<>(); + + /** + * Maps from fully-qualified class-name to class-names of annotations on that class. Annotations + * will be fully-qualified where that's known, and not where not known. + */ + final Map perClassData = new TreeMap<>(); + + ParsedPackageData() {} + + void merge(ParsedPackageData other) { + packages.addAll(other.packages); + usedTypes.addAll(other.usedTypes); + usedPackagesWithoutSpecificTypes.addAll(other.usedPackagesWithoutSpecificTypes); + exportedTypes.addAll(other.exportedTypes); + mainClasses.addAll(other.mainClasses); + for (Map.Entry classData : other.perClassData.entrySet()) { + PerClassData existing = perClassData.get(classData.getKey()); + if (existing == null) { + existing = new PerClassData(); + perClassData.put(classData.getKey(), existing); + } + existing.merge(classData.getValue()); + } + } +} diff --git a/java/src/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/PerClassData.java b/java/src/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/PerClassData.java new file mode 100644 index 00000000..cd9408ff --- /dev/null +++ b/java/src/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/PerClassData.java @@ -0,0 +1,77 @@ +package com.github.bazel_contrib.contrib_rules_jvm.javaparser.generators; + +import java.util.Map; +import java.util.Objects; +import java.util.SortedMap; +import java.util.SortedSet; +import java.util.TreeMap; +import java.util.TreeSet; + +class PerClassData { + PerClassData() { + this(new TreeSet<>(), new TreeMap<>(), new TreeMap<>()); + } + + @Override + public String toString() { + return "PerClassData{" + + "annotations=" + + annotations + + ", perMethodAnnotations=" + + perMethodAnnotations + + ", perFieldAnnotations=" + + perFieldAnnotations + + '}'; + } + + PerClassData( + SortedSet annotations, + SortedMap> perMethodAnnotations, + SortedMap> perFieldAnnotations) { + this.annotations = annotations; + this.perMethodAnnotations = perMethodAnnotations; + this.perFieldAnnotations = perFieldAnnotations; + } + + final SortedSet annotations; + + final SortedMap> perMethodAnnotations; + final SortedMap> perFieldAnnotations; + + public void merge(PerClassData other) { + annotations.addAll(other.annotations); + for (Map.Entry> methodAndAnnotations : + other.perMethodAnnotations.entrySet()) { + SortedSet existing = perMethodAnnotations.get(methodAndAnnotations.getKey()); + if (existing == null) { + existing = new TreeSet<>(); + perMethodAnnotations.put(methodAndAnnotations.getKey(), existing); + } + existing.addAll(methodAndAnnotations.getValue()); + } + for (Map.Entry> fieldAndAnnotations : + other.perFieldAnnotations.entrySet()) { + SortedSet existing = perFieldAnnotations.get(fieldAndAnnotations.getKey()); + if (existing == null) { + existing = new TreeSet<>(); + perFieldAnnotations.put(fieldAndAnnotations.getKey(), existing); + } + existing.addAll(fieldAndAnnotations.getValue()); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PerClassData that = (PerClassData) o; + return Objects.equals(annotations, that.annotations) + && Objects.equals(perMethodAnnotations, that.perMethodAnnotations) + && Objects.equals(perFieldAnnotations, that.perFieldAnnotations); + } + + @Override + public int hashCode() { + return Objects.hash(annotations, perMethodAnnotations, perFieldAnnotations); + } +} diff --git a/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/BUILD.bazel b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/BUILD.bazel index 486ff7f3..9890556f 100644 --- a/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/BUILD.bazel +++ b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/BUILD.bazel @@ -8,6 +8,7 @@ java_test_suite( srcs = [ "ClasspathParserTest.java", "JavaIdentifierTest.java", + "KtParserTest.java", ], jvm_flags = [ "-Dorg.slf4j.simpleLogger.defaultLogLevel=DEBUG", @@ -15,6 +16,7 @@ java_test_suite( resource_strip_prefix = "java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators", resources = [ ":java-test-workspace", + ":kotlin-test-workspace", ], runner = "junit5", runtime_deps = [ @@ -37,3 +39,9 @@ filegroup( testonly = 1, srcs = glob(["workspace/**/*.java"]), ) + +filegroup( + name = "kotlin-test-workspace", + testonly = 1, + srcs = glob(["workspace/**/*.kt"]), +) diff --git a/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/ClasspathParserTest.java b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/ClasspathParserTest.java index 063cbf27..82fe92ab 100644 --- a/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/ClasspathParserTest.java +++ b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/ClasspathParserTest.java @@ -202,9 +202,8 @@ public void testAnnotationAfterImport() throws IOException { assertEquals( Map.of( "workspace.com.gazelle.java.javaparser.generators.AnnotationAfterImport", - new ClasspathParser.PerClassData( - treeSet("com.example.FlakyTest"), new TreeMap<>(), new TreeMap<>())), - parser.perClassData); + new PerClassData(treeSet("com.example.FlakyTest"), new TreeMap<>(), new TreeMap<>())), + parser.getParsedPackageData().perClassData); } @Test @@ -218,9 +217,8 @@ public void testAnnotationAfterImportOnNestedClass() throws IOException { assertEquals( Map.of( "workspace.com.gazelle.java.javaparser.generators.NestedClassAnnotations.Inner", - new ClasspathParser.PerClassData( - treeSet("com.example.FlakyTest"), new TreeMap<>(), new TreeMap<>())), - parser.perClassData); + new PerClassData(treeSet("com.example.FlakyTest"), new TreeMap<>(), new TreeMap<>())), + parser.getParsedPackageData().perClassData); } @Test @@ -240,21 +238,18 @@ public void testAnnotationOnField() throws IOException { TreeMap> expectedInnerEnumFieldAnnotations = new TreeMap<>(); expectedInnerEnumFieldAnnotations.put("size", treeSet("lombok.Getter")); - TreeMap expected = new TreeMap<>(); + TreeMap expected = new TreeMap<>(); expected.put( "workspace.com.gazelle.java.javaparser.generators.AnnotationOnField", - new ClasspathParser.PerClassData( - new TreeSet<>(), new TreeMap<>(), expectedOuterClassFieldAnnotations)); + new PerClassData(new TreeSet<>(), new TreeMap<>(), expectedOuterClassFieldAnnotations)); expected.put( "workspace.com.gazelle.java.javaparser.generators.AnnotationOnField.InnerClass", - new ClasspathParser.PerClassData( - new TreeSet<>(), new TreeMap<>(), expectedInnerClassFieldAnnotations)); + new PerClassData(new TreeSet<>(), new TreeMap<>(), expectedInnerClassFieldAnnotations)); expected.put( "workspace.com.gazelle.java.javaparser.generators.AnnotationOnField.InnerEnum", - new ClasspathParser.PerClassData( - new TreeSet<>(), new TreeMap<>(), expectedInnerEnumFieldAnnotations)); + new PerClassData(new TreeSet<>(), new TreeMap<>(), expectedInnerEnumFieldAnnotations)); - assertEquals(expected, parser.perClassData); + assertEquals(expected, parser.getParsedPackageData().perClassData); } @Test @@ -271,9 +266,8 @@ public void testAnnotationAfterImportOnMethod() throws IOException { assertEquals( Map.of( "workspace.com.gazelle.java.javaparser.generators.AnnotationAfterImportOnMethod", - new ClasspathParser.PerClassData( - new TreeSet<>(), expectedPerMethodAnnotations, new TreeMap<>())), - parser.perClassData); + new PerClassData(new TreeSet<>(), expectedPerMethodAnnotations, new TreeMap<>())), + parser.getParsedPackageData().perClassData); } @Test @@ -289,9 +283,8 @@ public void testAnnotationFromJavaStandardLibrary() throws IOException { assertEquals( Map.of( "workspace.com.gazelle.java.javaparser.generators.AnnotationFromJavaStandardLibrary", - new ClasspathParser.PerClassData( - treeSet("Deprecated"), new TreeMap<>(), new TreeMap<>())), - parser.perClassData); + new PerClassData(treeSet("Deprecated"), new TreeMap<>(), new TreeMap<>())), + parser.getParsedPackageData().perClassData); } @Test @@ -307,9 +300,8 @@ public void testAnnotationWithoutImport() throws IOException { assertEquals( Map.of( "workspace.com.gazelle.java.javaparser.generators.AnnotationWithoutImport", - new ClasspathParser.PerClassData( - treeSet("WhoKnowsWhereIAmFrom"), new TreeMap<>(), new TreeMap<>())), - parser.perClassData); + new PerClassData(treeSet("WhoKnowsWhereIAmFrom"), new TreeMap<>(), new TreeMap<>())), + parser.getParsedPackageData().perClassData); } @Test @@ -343,7 +335,7 @@ public void testAnonymousInnerClass() throws IOException { "java.util.HashMap", "javax.annotation.Nullable", "org.jetbrains.annotations.Nullable"); assertEquals(expectedTypes, parser.getUsedTypes()); - Map expectedPerClassMetadata = new TreeMap<>(); + Map expectedPerClassMetadata = new TreeMap<>(); TreeMap> expectedPerMethodAnnotations = new TreeMap<>(); expectedPerMethodAnnotations.put( "containsValue", treeSet("Override", "javax.annotation.Nullable")); @@ -351,8 +343,8 @@ public void testAnonymousInnerClass() throws IOException { // end up getting given, so we just use the empty string for anonymous inner classes. expectedPerClassMetadata.put( "workspace.com.gazelle.java.javaparser.generators.AnonymousInnerClass.", - new ClasspathParser.PerClassData(treeSet(), expectedPerMethodAnnotations, new TreeMap<>())); - assertEquals(expectedPerClassMetadata, parser.perClassData); + new PerClassData(treeSet(), expectedPerMethodAnnotations, new TreeMap<>())); + assertEquals(expectedPerClassMetadata, parser.getParsedPackageData().perClassData); } @Test diff --git a/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/KtParserTest.java b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/KtParserTest.java new file mode 100644 index 00000000..7395145f --- /dev/null +++ b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/KtParserTest.java @@ -0,0 +1,340 @@ +package com.github.bazel_contrib.contrib_rules_jvm.javaparser.generators; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class KtParserTest { + private static final Logger logger = LoggerFactory.getLogger(KtParserTest.class); + + private static Path workspace; + private static Path directory; + + private KtParser parser; + + @BeforeAll + public static void setup() throws IOException, URISyntaxException { + URI workspaceUri = KtParserTest.class.getClassLoader().getResource("workspace").toURI(); + try (@SuppressWarnings("unused") + FileSystem fileSystem = FileSystems.newFileSystem(workspaceUri, new HashMap<>())) { + // The IntelliJ file manager doesn't support reading resources from a jar, so we need to + // extract them into a temporary directory before accessing them from the KtParser. + Path workspaceResourcePath = Paths.get(workspaceUri); + Path directoryResourcePath = + workspaceResourcePath.resolve("com/gazelle/kotlin/javaparser/generators"); + + workspace = Files.createTempDirectory("workspace"); + directory = workspace.resolve("com/gazelle/kotlin/javaparser/generators"); + directory = Files.createDirectories(directory); + + Files.walk(directoryResourcePath) + .forEach( + file -> { + if (Files.isDirectory(file)) { + return; + } + try { + byte[] bytes = Files.readAllBytes(file); + Files.write(directory.resolve(file.getFileName().toString()), bytes); + } catch (Exception e) { + logger.error("Error copying file " + file.toString(), e); + } + }); + } + } + + @BeforeEach + public void setupPerTest() { + parser = new KtParser(); + } + + @Test + public void topLevelMainFunction() throws IOException { + ParsedPackageData data = parser.parseClasses(getPathsWithNames("Main.kt")); + + assertEquals(Set.of("workspace.com.gazelle.kotlin.javaparser.generators"), data.packages); + assertEquals(Set.of("MainKt"), data.mainClasses); + assertEquals( + Set.of("workspace.com.gazelle.kotlin.javaparser.generators.MainKt"), + data.perClassData.keySet()); + } + + @Test + public void mainInClass() throws IOException { + ParsedPackageData data = parser.parseClasses(getPathsWithNames("MainInClass.kt")); + + assertEquals(Set.of("workspace.com.gazelle.kotlin.javaparser.generators"), data.packages); + assertEquals(Set.of("MainInClass"), data.mainClasses); + assertEquals( + Set.of( + "workspace.com.gazelle.kotlin.javaparser.generators.MainInClass", + "workspace.com.gazelle.kotlin.javaparser.generators.MainInClass.Companion"), + data.perClassData.keySet()); + } + + @Test + public void mainOnCompanion() throws IOException { + ParsedPackageData data = parser.parseClasses(getPathsWithNames("MainOnCompanion.kt")); + + assertEquals(Set.of("workspace.com.gazelle.kotlin.javaparser.generators"), data.packages); + assertEquals(Set.of("MainOnCompanion.Companion"), data.mainClasses); + } + + @Test + public void exportingClassTest() throws IOException { + ParsedPackageData data = parser.parseClasses(getPathsWithNames("ExportingClass.kt")); + + assertEquals( + Set.of( + "example.external.FinalProperty", + "example.external.VarProperty", + "example.external.InternalReturn", + "example.external.ProtectedReturn", + "example.external.PublicReturn", + "example.external.ParameterizedReturn"), + data.exportedTypes); + } + + @Test + public void privateExportingClassTest() throws IOException { + ParsedPackageData data = parser.parseClasses(getPathsWithNames("PrivateExportingClass.kt")); + + assertEquals(Set.of(), data.exportedTypes); + } + + @Test + public void helloTest() throws IOException { + ParsedPackageData data = parser.parseClasses(getPathsWithNames("Hello.kt")); + + assertEquals(Set.of("workspace.com.gazelle.kotlin.javaparser.generators"), data.packages); + assertEquals( + Set.of( + "com.gazelle.java.javaparser.generators.DeleteBookRequest", + "com.gazelle.java.javaparser.generators.HelloProto", + "com.google.common.primitives.Ints"), + data.usedTypes); + assertEquals(Set.of(), data.usedPackagesWithoutSpecificTypes); + assertEquals(Set.of(), data.exportedTypes); + assertEquals(Set.of(), data.mainClasses); + assertEquals( + Set.of("workspace.com.gazelle.kotlin.javaparser.generators.Hello"), + data.perClassData.keySet()); + } + + @Test + public void constantTest() throws IOException { + ParsedPackageData data = parser.parseClasses(getPathsWithNames("Constant.kt")); + + assertEquals( + Set.of("workspace.com.gazelle.kotlin.javaparser.generators.ConstantKt"), + data.perClassData.keySet()); + } + + // @Test + // public void fullyQualifiedClassAndFunctionUse() throws IOException { + // ParsedPackageData data = parser.parseClasses(getPathsWithNames("FullyQualifieds.kt")); + // assertEquals( + // Set.of("com.example"), + // data.usedPackagesWithoutSpecificTypes); + // assertEquals( + // Set.of( + // "workspace.com.gazelle.java.javaparser.generators.DeleteBookRequest", + // "workspace.com.gazelle.java.javaparser.generators.DeleteBookResponse", + // "workspace.com.gazelle.java.javaparser.utils.Printer", + // "workspace.com.gazelle.java.javaparser.factories.Factory", + // "java.util.ArrayList", + // "com.example.PrivateArg"), + // data.usedTypes); + // } + + @Test + public void staticImportsTest() throws IOException { + ParsedPackageData data = parser.parseClasses(getPathsWithNames("StaticImports.kt")); + + assertEquals(Set.of("com.gazelle.java.javaparser.ClasspathParser"), data.usedTypes); + assertEquals( + Set.of( + "com.gazelle.kotlin.constantpackage", + "com.gazelle.kotlin.constantpackage2", + "com.gazelle.kotlin.functionpackage"), + data.usedPackagesWithoutSpecificTypes); + } + + @Test + public void wildcardsTest() throws IOException { + ParsedPackageData data = parser.parseClasses(getPathsWithNames("Wildcards.kt")); + + assertEquals(Set.of("org.junit.jupiter.api"), data.usedPackagesWithoutSpecificTypes); + assertEquals(Set.of("org.junit.jupiter.api.Assertions"), data.usedTypes); + } + + @Test + public void detectsInlineFunction() throws IOException { + ParsedPackageData data = parser.parseClasses(getPathsWithNames("InlineFunction.kt")); + + // Verify inline function dependencies are in exported types + assertNotNull(data.exportedTypes, "exportedTypes should not be null"); + assertTrue( + data.exportedTypes.contains("com.example.Helper"), + "Should detect Helper dependency from inline function: " + data.exportedTypes); + assertTrue( + data.exportedTypes.contains("com.google.gson.Gson"), + "Should detect Gson dependency from inline function: " + data.exportedTypes); + } + + @Test + public void detectsMultipleInlineFunctions() throws IOException { + ParsedPackageData data = parser.parseClasses(getPathsWithNames("MultipleInlines.kt")); + + assertNotNull(data.exportedTypes, "exportedTypes should not be null"); + + // Should detect all three inline functions' dependencies + assertTrue( + data.exportedTypes.contains("com.example.utils.StringUtils"), + "Should detect StringUtils from inline functions: " + data.exportedTypes); + assertTrue( + data.exportedTypes.contains("java.util.ArrayList"), + "Should detect ArrayList from inline functions: " + data.exportedTypes); + } + + @Test + public void detectsGsonAndArrayListInInlineFunction() throws IOException { + ParsedPackageData data = parser.parseClasses(getPathsWithNames("InlineWithGson.kt")); + + assertNotNull(data.exportedTypes, "exportedTypes should not be null"); + + // Should detect the processData inline function's dependencies + assertTrue( + data.exportedTypes.contains("com.google.gson.Gson"), + "Should detect Gson from inline function: " + data.exportedTypes); + assertTrue( + data.exportedTypes.contains("java.util.ArrayList"), + "Should detect ArrayList from inline function: " + data.exportedTypes); + } + + private List getPathsWithNames(String... names) throws IOException { + Set namesSet = Set.of(names); + return Files.walk(directory) + .filter(file -> namesSet.contains(file.getFileName().toString())) + .collect(Collectors.toUnmodifiableList()); + } + + @Test + public void detectsSimpleExtensionFunctions() throws IOException { + ParsedPackageData data = parser.parseClasses(getPathsWithNames("SimpleExtensions.kt")); + + // Extension function dependencies should be in exported types + assertTrue( + data.exportedTypes.contains("com.example.Helper"), + "Should detect Helper from extension function: " + data.exportedTypes); + assertTrue( + data.exportedTypes.contains("com.google.gson.Gson"), + "Should detect Gson from extension function: " + data.exportedTypes); + assertTrue( + data.exportedTypes.contains("com.google.gson.JsonArray"), + "Should detect JsonArray from extension function: " + data.exportedTypes); + } + + @Test + public void detectsExtensionOperators() throws IOException { + ParsedPackageData data = parser.parseClasses(getPathsWithNames("ExtensionOperators.kt")); + + // Extension operator dependencies should be in exported types + assertTrue( + data.exportedTypes.contains("com.example.MathUtils"), + "Should detect MathUtils from extension operator: " + data.exportedTypes); + assertTrue( + data.exportedTypes.contains("com.google.gson.JsonArray"), + "Should detect JsonArray from extension operator: " + data.exportedTypes); + assertTrue( + data.exportedTypes.contains("com.google.gson.JsonObject"), + "Should detect JsonObject from extension operator: " + data.exportedTypes); + } + + @Test + public void testAstEnhancementsAreActive() throws IOException { + // This test verifies that our AST-based enhancements are active and working + // by checking that the enhanced visitor methods are being called + + // Test with inline functions - dependencies should be in exportedTypes + ParsedPackageData inlineData = parser.parseClasses(getPathsWithNames("InlineWithGson.kt")); + assertTrue( + inlineData.exportedTypes.contains("com.google.gson.Gson"), + "Should detect Gson in exportedTypes: " + inlineData.exportedTypes); + assertTrue( + inlineData.exportedTypes.contains("java.util.ArrayList"), + "Should detect ArrayList in exportedTypes: " + inlineData.exportedTypes); + + // Test with extension functions - dependencies should be in exportedTypes + ParsedPackageData extensionData = parser.parseClasses(getPathsWithNames("SimpleExtensions.kt")); + assertTrue( + extensionData.exportedTypes.contains("com.example.Helper"), + "Should detect Helper in exportedTypes: " + extensionData.exportedTypes); + assertTrue( + extensionData.exportedTypes.contains("com.google.gson.Gson"), + "Should detect Gson in exportedTypes: " + extensionData.exportedTypes); + assertTrue( + extensionData.exportedTypes.contains("com.google.gson.JsonArray"), + "Should detect JsonArray in exportedTypes: " + extensionData.exportedTypes); + + // Test with extension operators - dependencies should be in exportedTypes + ParsedPackageData operatorData = + parser.parseClasses(getPathsWithNames("ExtensionOperators.kt")); + assertTrue( + operatorData.exportedTypes.contains("com.example.MathUtils"), + "Should detect MathUtils in exportedTypes: " + operatorData.exportedTypes); + assertTrue( + operatorData.exportedTypes.contains("com.google.gson.JsonArray"), + "Should detect JsonArray in exportedTypes: " + operatorData.exportedTypes); + assertTrue( + operatorData.exportedTypes.contains("com.google.gson.JsonObject"), + "Should detect JsonObject in exportedTypes: " + operatorData.exportedTypes); + } + + @Test + public void detectsDestructuringWithCustomComponentFunctions() throws IOException { + ParsedPackageData data = parser.parseClasses(getPathsWithNames("DestructuringWithDeps.kt")); + + assertNotNull(data.exportedTypes, "exportedTypes should not be null"); + + // Log what we found for debugging + logger.info("=== Destructuring Detection Test ==="); + logger.info("Exported types found: " + data.exportedTypes); + + // Should detect dependencies from componentN() functions in exportedTypes + assertTrue( + data.exportedTypes.contains("com.google.gson.Gson") + || data.exportedTypes.contains("com.google.code.gson.Gson"), + "Should detect Gson dependency from component1() function. Found: " + data.exportedTypes); + + assertTrue( + data.exportedTypes.contains("com.google.common.base.Strings"), + "Should detect Guava Strings dependency from component2() function. Found: " + + data.exportedTypes); + + // Verify that we have at least some exported types from componentN() functions + assertTrue( + data.exportedTypes.size() > 0, + "Should have detected some exported types from componentN() functions"); + + logger.info("Destructuring detection working correctly!"); + } +} diff --git a/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/AstComplexCalls.kt b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/AstComplexCalls.kt new file mode 100644 index 00000000..3fd5bc11 --- /dev/null +++ b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/AstComplexCalls.kt @@ -0,0 +1,34 @@ +package workspace.com.gazelle.kotlin.javaparser.generators + +import com.example.Helper +import com.google.gson.Gson + +// Inline function +inline fun processData(data: String): String { + val gson = Gson() + return gson.toJson(data) +} + +// Extension function +fun String.transform(): String { + val helper = Helper() + return helper.process(this) +} + +// Complex usage patterns +fun testComplexPatterns() { + val data = "test" + + // Nested calls + val result1 = processData(data.transform()) + + // Chained calls + val result2 = data.transform().transform() + + // Mixed inline and extension + val result3 = processData(data).transform() + + // Safe chained calls + val nullable: String? = "test" + val result4 = nullable?.transform()?.transform() +} diff --git a/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/AstExtensionReceivers.kt b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/AstExtensionReceivers.kt new file mode 100644 index 00000000..d2f72d35 --- /dev/null +++ b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/AstExtensionReceivers.kt @@ -0,0 +1,30 @@ +package workspace.com.gazelle.kotlin.javaparser.generators + +import com.example.StringProcessor +import com.google.gson.Gson + +// Extension functions on different types +fun String.processAsString(): String { + val gson = Gson() + return gson.toJson(this) +} + +fun Int.processAsInt(): String { + val processor = StringProcessor() + return processor.format(this.toString()) +} + +fun List.processAsList(): String { + return this.joinToString(",") +} + +// Usage with different receiver types +fun testReceivers() { + val str = "hello" + val num = 42 + val list = listOf("a", "b", "c") + + val result1 = str.processAsString() // String receiver + val result2 = num.processAsInt() // Int receiver + val result3 = list.processAsList() // List receiver +} diff --git a/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/AstInlineCalls.kt b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/AstInlineCalls.kt new file mode 100644 index 00000000..2a4914a0 --- /dev/null +++ b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/AstInlineCalls.kt @@ -0,0 +1,24 @@ +package workspace.com.gazelle.kotlin.javaparser.generators + +import com.example.Helper +import com.google.gson.Gson + +// Define inline functions +inline fun processJson(data: String): String { + val gson = Gson() + return gson.toJson(data) +} + +inline fun processHelper(input: String): String { + val helper = Helper() + return helper.process(input) +} + +inline fun unusedInlineFunction(x: Int): Int = x * 2 + +// Functions that use inline functions +fun actualUsage() { + val result1 = processJson("test") // This should be detected + val result2 = processHelper("data") // This should be detected + // Note: unusedInlineFunction is NOT called +} diff --git a/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/AstOperators.kt b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/AstOperators.kt new file mode 100644 index 00000000..01956df5 --- /dev/null +++ b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/AstOperators.kt @@ -0,0 +1,47 @@ +package workspace.com.gazelle.kotlin.javaparser.generators + +import com.example.MathUtils +import com.google.gson.JsonObject + +class Vector(val x: Int, val y: Int) + +// Arithmetic operators +operator fun Vector.plus(other: Vector): Vector { + val utils = MathUtils() + return utils.addVectors(this, other) +} + +operator fun Vector.minus(other: Vector): Vector { + return Vector(this.x - other.x, this.y - other.y) +} + +operator fun Vector.times(scalar: Int): Vector { + val json = JsonObject() + json.addProperty("operation", "scale") + return Vector(this.x * scalar, this.y * scalar) +} + +// Unary operators +operator fun Vector.unaryMinus(): Vector = Vector(-x, -y) + +operator fun Vector.not(): Boolean = x == 0 && y == 0 + +// Comparison operators +operator fun Vector.compareTo(other: Vector): Int { + val thisLength = x * x + y * y + val otherLength = other.x * other.x + other.y * other.y + return thisLength.compareTo(otherLength) +} + +// Usage of operators +fun testOperators() { + val v1 = Vector(1, 2) + val v2 = Vector(3, 4) + + val sum = v1 + v2 // plus operator + val diff = v1 - v2 // minus operator + val scaled = v1 * 3 // times operator + val negated = -v1 // unaryMinus operator + val isEmpty = !v1 // not operator + val comparison = v1 > v2 // compareTo operator +} diff --git a/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/AstSafeCalls.kt b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/AstSafeCalls.kt new file mode 100644 index 00000000..744d3bc1 --- /dev/null +++ b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/AstSafeCalls.kt @@ -0,0 +1,24 @@ +package workspace.com.gazelle.kotlin.javaparser.generators + +import com.google.gson.Gson + +// Extension functions on nullable types +fun String?.safeProcess(): String? { + val gson = Gson() + return this?.let { gson.toJson(it) } +} + +fun String?.safeLength(): Int? { + return this?.length +} + +// Usage with safe calls +fun testSafeCalls() { + val nullable: String? = "test" + val alsoNullable: String? = null + + val result1 = nullable?.safeProcess() // Safe call on extension + val result2 = alsoNullable?.safeProcess() // Safe call on null + val length1 = nullable?.safeLength() // Another safe call + val length2 = alsoNullable?.safeLength() // Safe call on null +} diff --git a/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/Constant.kt b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/Constant.kt new file mode 100644 index 00000000..29e567e3 --- /dev/null +++ b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/Constant.kt @@ -0,0 +1,3 @@ +package workspace.com.gazelle.kotlin.javaparser.generators + +const val SOME_CONSTANT = 1 diff --git a/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/DestructuringWithDeps.kt b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/DestructuringWithDeps.kt new file mode 100644 index 00000000..840c63d7 --- /dev/null +++ b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/DestructuringWithDeps.kt @@ -0,0 +1,68 @@ +package workspace.com.gazelle.kotlin.javaparser.generators + +import com.google.common.base.Strings +import com.google.gson.Gson + +/** + * Test file for destructuring declarations with custom componentN() functions that have external + * dependencies. + */ + +/** + * Custom class with componentN() functions that use external dependencies. Any code that + * destructures this class should transitively depend on: + * - com.google.gson (for Gson) + * - com.google.guava (for Strings) + */ +class CustomContainer(private val data: Map) { + + /** component1() function that uses Gson. Destructuring should add Gson to exports. */ + operator fun component1(): String { + val gson = Gson() + return gson.toJson(data["first"] ?: "") + } + + /** component2() function that uses Guava Strings. Destructuring should add Guava to exports. */ + operator fun component2(): String { + val value = data["second"]?.toString() ?: "" + return Strings.padEnd(value, 20, '-') + } + + /** component3() function that uses both dependencies. */ + operator fun component3(): String { + val gson = Gson() + val rawValue = data["third"]?.toString() ?: "" + val paddedValue = Strings.padStart(rawValue, 15, '*') + return gson.toJson(mapOf("padded" to paddedValue)) + } + + /** Regular method for comparison - should not affect destructuring dependencies. */ + fun getSize(): Int = data.size +} + +/** + * Consumer that uses destructuring on CustomContainer. This should automatically get transitive + * dependencies from the provider's componentN() functions through the exports mechanism. + */ +class Consumer { + + fun useDestructuring(): String { + val container = + CustomContainer(mapOf("first" to "hello", "second" to "world", "third" to "test")) + + // Destructure the container - this should work because + // the provider exports its componentN() dependencies + val (first, second, third) = container + + return "First: $first, Second: $second, Third: $third" + } + + fun usePartialDestructuring(): String { + val container = CustomContainer(mapOf("first" to mapOf("key" to "value"), "second" to "simple")) + + // Partial destructuring - only uses component1() and component2() + val (jsonData, paddedData) = container + + return "JSON: $jsonData, Padded: $paddedData" + } +} diff --git a/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/ExportingClass.kt b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/ExportingClass.kt new file mode 100644 index 00000000..c897826e --- /dev/null +++ b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/ExportingClass.kt @@ -0,0 +1,40 @@ +package workspace.com.gazelle.kotlin.javaparser.generators + +import example.external.FinalProperty +import example.external.InternalReturn +import example.external.ParameterizedReturn +import example.external.PrivateReturn +import example.external.ProtectedReturn +import example.external.PublicReturn +import example.external.VarProperty + +class ExportingClass { + val finalProperty: FinalProperty? = null + val varProperty: VarProperty? = null + + internal fun getInternal(): InternalReturn? { + return null + } + + private fun getPrivate(): PrivateReturn? { + return null + } + + protected fun getProtected(): ProtectedReturn? { + return null + } + + fun getPublic(): PublicReturn? { + return null + } + + fun getPrimitive(): Int { + return 0 + } + + fun getVoid(): Unit {} + + fun getParameterizedType(): ParameterizedReturn? { + return null + } +} diff --git a/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/ExtensionOperators.kt b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/ExtensionOperators.kt new file mode 100644 index 00000000..4b62020e --- /dev/null +++ b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/ExtensionOperators.kt @@ -0,0 +1,36 @@ +package workspace.com.gazelle.kotlin.javaparser.generators + +import com.example.MathUtils +import com.google.gson.JsonArray +import com.google.gson.JsonObject + +/** Test file with extension operators that use external dependencies. */ +data class Matrix(val data: Array>) + +// Extension operator that uses MathUtils +operator fun Matrix.plus(other: Matrix): Matrix { + val mathUtils = MathUtils() + return mathUtils.addMatrices(this, other) +} + +// Extension operator that uses Gson classes +operator fun Matrix.times(scalar: Double): Matrix { + val jsonObj = JsonObject() + val jsonArray = JsonArray() + // Use Gson classes in the computation + jsonObj.addProperty("scalar", scalar) + jsonArray.add(jsonObj) + + // Process with Gson classes + return Matrix(this.data.map { row -> row.map { it * scalar }.toTypedArray() }.toTypedArray()) +} + +// Extension operator with no external dependencies +operator fun Matrix.unaryMinus(): Matrix { + return Matrix(this.data.map { row -> row.map { -it }.toTypedArray() }.toTypedArray()) +} + +// Regular function for comparison (not an operator extension) +fun multiplyMatrices(a: Matrix, b: Matrix): Matrix { + return Matrix(emptyArray()) +} diff --git a/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/FullyQualifieds.kt b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/FullyQualifieds.kt new file mode 100644 index 00000000..90385ca9 --- /dev/null +++ b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/FullyQualifieds.kt @@ -0,0 +1,28 @@ +package workspace.com.gazelle.kotlin.javaparser.generators + +class FullyQualifieds { + fun fn() { + workspace.com.gazelle.java.javaparser.generators.DeleteBookRequest() + workspace.com.gazelle.java.javaparser.utils.Printer.print() + + val response: workspace.com.gazelle.java.javaparser.generators.DeleteBookResponse = + workspace.com.gazelle.java.javaparser.factories.Factory.create() + + // instance methods shouldn't get detected as classes (e.g. this.foo). + this.foo.bar() + + // Anonymous variables in lambdas shouldn't be picked up as variable names - visitVariable sees + // them as variables with null types. + someList.map { x -> x.toString() } + + java.util.ArrayList().map { y -> y.toString() } + + this.BLAH = "beep" + this.BEEP_BOOP = "baz" + } + + private fun privateFn(privateArg: com.example.PrivateArg): String { + // Top level functions should result in adding the package to the used packages list. + return com.example.externalPrivateFn(privateArg) + } +} diff --git a/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/Hello.kt b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/Hello.kt new file mode 100644 index 00000000..69799611 --- /dev/null +++ b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/Hello.kt @@ -0,0 +1,17 @@ +package workspace.com.gazelle.kotlin.javaparser.generators + +import com.gazelle.java.javaparser.generators.DeleteBookRequest +import com.gazelle.java.javaparser.generators.HelloProto +import com.google.common.primitives.Ints + +class Hello { + fun useImports() { + val req: DeleteBookRequest? = null + val proto: HelloProto? = null + val bytes = Ints.BYTES + // Touch the values so tools consider them used + if (bytes >= 0) { + println("$req $proto $bytes") + } + } +} diff --git a/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/InlineFunction.kt b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/InlineFunction.kt new file mode 100644 index 00000000..17f0a5b4 --- /dev/null +++ b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/InlineFunction.kt @@ -0,0 +1,19 @@ +package workspace.com.gazelle.kotlin.javaparser.generators + +import com.example.Helper +import com.google.gson.Gson + +/** Test file with a simple inline function that uses external dependencies. */ +class InlineFunctionExample { + + inline fun processData(data: String): String { + val helper = Helper() + val gson = Gson() + return gson.toJson(helper.transform(data)) + } + + // Regular function for comparison + fun regularFunction(data: String): String { + return data.uppercase() + } +} diff --git a/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/InlineWithGson.kt b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/InlineWithGson.kt new file mode 100644 index 00000000..4474b677 --- /dev/null +++ b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/InlineWithGson.kt @@ -0,0 +1,32 @@ +package workspace.com.gazelle.kotlin.javaparser.generators + +import com.google.gson.Gson +import java.util.ArrayList + +/** + * Test file that matches the failing test case scenario. This should detect Gson and ArrayList as + * dependencies of the inline function. + */ + +/** + * Inline function that uses external dependencies. Any code that calls this function should + * transitively depend on: + * - java.util (for ArrayList) + * - com.google.gson (for Gson) + */ +inline fun processData(data: String): String { + val list = ArrayList() + list.add(data) + + val gson = Gson() + return gson.toJson(list) +} + +/** + * Regular function that also uses external dependencies. This should NOT cause transitive + * dependencies for callers. + */ +fun regularProcessData(data: String): String { + val gson = Gson() + return gson.toJson(data) +} diff --git a/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/Main.kt b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/Main.kt new file mode 100644 index 00000000..39ce85b1 --- /dev/null +++ b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/Main.kt @@ -0,0 +1,3 @@ +package workspace.com.gazelle.kotlin.javaparser.generators + +fun main(vararg args: String) {} diff --git a/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/MainInClass.kt b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/MainInClass.kt new file mode 100644 index 00000000..ecfd20ea --- /dev/null +++ b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/MainInClass.kt @@ -0,0 +1,7 @@ +package workspace.com.gazelle.kotlin.javaparser.generators + +class MainInClass { + companion object { + @JvmStatic fun main(args: Array) {} + } +} diff --git a/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/MainOnCompanion.kt b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/MainOnCompanion.kt new file mode 100644 index 00000000..9ea70d05 --- /dev/null +++ b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/MainOnCompanion.kt @@ -0,0 +1,7 @@ +package workspace.com.gazelle.kotlin.javaparser.generators + +class MainOnCompanion { + companion object { + fun main(args: Array) {} + } +} diff --git a/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/MultipleInlines.kt b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/MultipleInlines.kt new file mode 100644 index 00000000..ee19130a --- /dev/null +++ b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/MultipleInlines.kt @@ -0,0 +1,30 @@ +package workspace.com.gazelle.kotlin.javaparser.generators + +import com.example.utils.StringUtils +import java.util.ArrayList + +/** Test file with multiple inline functions to verify we can detect all of them. */ +object MultipleInlines { + + // Inline function using Java stdlib + inline fun withList(block: (ArrayList) -> Unit) { + val list = ArrayList() + block(list) + } + + // Inline function using external dependency + inline fun formatString(input: String): String { + val utils = StringUtils() + return utils.format(input) + } + + // Inline function with no external dependencies + inline fun simpleInline(x: Int, y: Int): Int { + return x + y + } + + // Regular function (should not be detected as inline) + fun notInline(data: String): String { + return data.lowercase() + } +} diff --git a/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/PrivateExportingClass.kt b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/PrivateExportingClass.kt new file mode 100644 index 00000000..e71938ca --- /dev/null +++ b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/PrivateExportingClass.kt @@ -0,0 +1,24 @@ +package workspace.com.gazelle.kotlin.javaparser.generators + +import example.external.FinalProperty +import example.external.InternalReturn +import example.external.ProtectedReturn +import example.external.PublicReturn +import example.external.VarProperty + +private class PrivateExportingClass { + val finalProperty: FinalProperty? = null + val varProperty: VarProperty? = null + + internal fun getInternal(): InternalReturn? { + return null + } + + protected fun getProtected(): ProtectedReturn? { + return null + } + + fun getPublic(): PublicReturn? { + return null + } +} diff --git a/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/SimpleExtensions.kt b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/SimpleExtensions.kt new file mode 100644 index 00000000..8a3d0486 --- /dev/null +++ b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/SimpleExtensions.kt @@ -0,0 +1,35 @@ +package workspace.com.gazelle.kotlin.javaparser.generators + +import com.example.Helper +import com.google.gson.Gson +import com.google.gson.JsonArray + +/** Test file with simple extension functions that use external dependencies. */ + +// Extension function on String that uses Gson +fun String.parseAsJson(): com.google.gson.JsonObject { + val gson = Gson() + return gson.fromJson(this, com.google.gson.JsonObject::class.java) +} + +// Extension function on String that uses JsonArray (another Gson class) +fun String.parseAsJsonArray(): JsonArray { + val gson = Gson() + return gson.fromJson(this, JsonArray::class.java) +} + +// Extension function on String that uses Helper +fun String.processWithHelper(): String { + val helper = Helper() + return helper.transform(this) +} + +// Extension function with no external dependencies +fun String.reverseString(): String { + return this.reversed() +} + +// Regular function for comparison (not an extension) +fun regularFunction(data: String): String { + return data.uppercase() +} diff --git a/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/StaticImports.kt b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/StaticImports.kt new file mode 100644 index 00000000..9f5f0780 --- /dev/null +++ b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/StaticImports.kt @@ -0,0 +1,13 @@ +package workspace.com.gazelle.kotlin.javaparser.generators + +import com.gazelle.java.javaparser.ClasspathParser.MAIN_CLASS +import com.gazelle.kotlin.constantpackage.CONSTANT +import com.gazelle.kotlin.constantpackage2.FOO +import com.gazelle.kotlin.functionpackage.someFunction + +fun myFunction() { + // Use the imported constants so ktfmt or optimize imports doesn't drop them + val s = "${CONSTANT}_${FOO}_$MAIN_CLASS" + println(s) + someFunction() +} diff --git a/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/Wildcards.kt b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/Wildcards.kt new file mode 100644 index 00000000..d8573b95 --- /dev/null +++ b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/Wildcards.kt @@ -0,0 +1,4 @@ +package workspace.com.gazelle.kotlin.javaparser.generators + +import org.junit.jupiter.api.* +import org.junit.jupiter.api.Assertions.*