diff --git a/MODULE.bazel b/MODULE.bazel index 7b206cec..6276f5dc 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -169,6 +169,9 @@ maven.install( "org.slf4j:slf4j-api:1.7.32", "org.slf4j:slf4j-jdk14:1.7.32", + # For Kotlin parsing in the Gazelle plugin + "org.jetbrains.kotlin:kotlin-compiler:1.9.25", + # 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 3244070c..2b9f1c36 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": -661500966, - "__RESOLVED_ARTIFACTS_HASH": -1503133859, + "__INPUT_ARTIFACTS_HASH": 634721631, + "__RESOLVED_ARTIFACTS_HASH": 826825, "conflict_resolution": { "com.google.guava:guava:30.1.1-jre": "com.google.guava:guava:31.1-jre", "com.google.guava:guava:31.0.1-android": "com.google.guava:guava:31.1-jre", @@ -638,6 +638,72 @@ }, "version": "3.28.0-GA" }, + "org.jetbrains.intellij.deps:trove4j": { + "shasums": { + "jar": "c5fd725bffab51846bf3c77db1383c60aaaebfe1b7fe2f00d23fe1b7df0a439d" + }, + "version": "1.0.20200330" + }, + "org.jetbrains.kotlin:kotlin-compiler": { + "shasums": { + "jar": "cf43de80296b7c9e28a32fcaa157f5d09ce4c56c56042c43f02af4f588eb07fd" + }, + "version": "1.9.25" + }, + "org.jetbrains.kotlin:kotlin-reflect": { + "shasums": { + "jar": "3277ac102ae17aad10a55abec75ff5696c8d109790396434b496e75087854203" + }, + "version": "1.6.10" + }, + "org.jetbrains.kotlin:kotlin-script-runtime": { + "shasums": { + "jar": "9933edd6677b6aac46d1b5effda1adee9063fbb04c746e78b403398158abaf47" + }, + "version": "1.9.25" + }, + "org.jetbrains.kotlin:kotlin-stdlib": { + "shasums": { + "jar": "f9cdcdbff1f5de85380ae526977e683726c2aa42db1ed6e6e50ae89e496e95fd" + }, + "version": "1.9.25" + }, + "org.jetbrains.kotlin:kotlin-stdlib-common": { + "shasums": { + "jar": "c14ce6720a487b5e238f1b0c30ac3ad73e45b90a40731ca0b1cfddec1a37682f" + }, + "version": "1.5.0" + }, + "org.jetbrains.kotlin:kotlin-stdlib-jdk7": { + "shasums": { + "jar": "fb5373dd761b4e93e3f538c5e853bba38a71143a181536e8f193ed6e4eddb3b8" + }, + "version": "1.9.25" + }, + "org.jetbrains.kotlin:kotlin-stdlib-jdk8": { + "shasums": { + "jar": "f94fdf78390ce9be30383bf039c5a935caea33b11f037fc7f86bbcee19287e5a" + }, + "version": "1.9.25" + }, + "org.jetbrains.kotlinx:kotlinx-coroutines-core": { + "shasums": { + "jar": "6f738012913d3d4bc18408a5011108d4744a72b6233662ee4d4dd50da9550b8d" + }, + "version": "1.5.0" + }, + "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm": { + "shasums": { + "jar": "78d6cc7135f84d692ff3752fcfd1fa1bbe0940d7df70652e4f1eaeec0c78afbb" + }, + "version": "1.5.0" + }, + "org.jetbrains:annotations": { + "shasums": { + "jar": "ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478" + }, + "version": "13.0" + }, "org.junit.jupiter:junit-jupiter-api": { "shasums": { "jar": "1808ee87e0f718cd6e25f3b75afc17956ac8a3edc48c7e9bab9f19f9a79e3801" @@ -1136,6 +1202,30 @@ "commons-logging:commons-logging", "org.apache.httpcomponents:httpcore" ], + "org.jetbrains.kotlin:kotlin-compiler": [ + "org.jetbrains.intellij.deps:trove4j", + "org.jetbrains.kotlin:kotlin-reflect", + "org.jetbrains.kotlin:kotlin-script-runtime", + "org.jetbrains.kotlin:kotlin-stdlib-jdk8", + "org.jetbrains.kotlinx:kotlinx-coroutines-core" + ], + "org.jetbrains.kotlin:kotlin-stdlib": [ + "org.jetbrains:annotations" + ], + "org.jetbrains.kotlin:kotlin-stdlib-jdk7": [ + "org.jetbrains.kotlin:kotlin-stdlib" + ], + "org.jetbrains.kotlin:kotlin-stdlib-jdk8": [ + "org.jetbrains.kotlin:kotlin-stdlib", + "org.jetbrains.kotlin:kotlin-stdlib-jdk7" + ], + "org.jetbrains.kotlinx:kotlinx-coroutines-core": [ + "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm" + ], + "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm": [ + "org.jetbrains.kotlin:kotlin-stdlib-common", + "org.jetbrains.kotlin:kotlin-stdlib-jdk8" + ], "org.junit.jupiter:junit-jupiter-api": [ "org.apiguardian:apiguardian-api", "org.junit.platform:junit-platform-commons", @@ -2309,6 +2399,1016 @@ "javassist.util", "javassist.util.proxy" ], + "org.jetbrains.intellij.deps:trove4j": [ + "gnu.trove", + "gnu.trove.decorator" + ], + "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.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.icons", + "com.intellij.ide", + "com.intellij.ide.highlighter", + "com.intellij.ide.plugins", + "com.intellij.ide.plugins.cl", + "com.intellij.ide.util", + "com.intellij.injected.editor", + "com.intellij.jna", + "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.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.io.win32", + "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.ex", + "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.util.plugins", + "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.io", + "com.intellij.util.io.keyStorage", + "com.intellij.util.keyFMap", + "com.intellij.util.lang", + "com.intellij.util.lang.fastutil", + "com.intellij.util.loader", + "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.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", + "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", + "javaslang", + "javaslang.collection", + "javaslang.concurrent", + "javaslang.control", + "javaslang.match", + "javaslang.match.annotation", + "javaslang.match.generator", + "javaslang.match.model", + "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.hawtjni.runtime", + "org.fusesource.jansi", + "org.fusesource.jansi.internal", + "org.jdom", + "org.jdom.internal", + "org.jdom.output", + "org.jdom.output.support", + "org.jetbrains.annotations", + "org.jetbrains.concurrency", + "org.jetbrains.jps.model.java.impl", + "org.jetbrains.kotlin", + "org.jetbrains.kotlin.analysis.decompiled.light.classes", + "org.jetbrains.kotlin.analysis.decompiled.light.classes.fe10", + "org.jetbrains.kotlin.analysis.decompiled.light.classes.origin", + "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.analysis.project.structure", + "org.jetbrains.kotlin.analysis.project.structure.impl", + "org.jetbrains.kotlin.analysis.providers", + "org.jetbrains.kotlin.analysis.providers.impl", + "org.jetbrains.kotlin.analysis.providers.impl.declarationProviders", + "org.jetbrains.kotlin.analysis.providers.impl.util", + "org.jetbrains.kotlin.analysis.providers.topics", + "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.bridges", + "org.jetbrains.kotlin.backend.common.descriptors", + "org.jetbrains.kotlin.backend.common.extensions", + "org.jetbrains.kotlin.backend.common.ir", + "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.psi", + "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.metadata.impl", + "org.jetbrains.kotlin.backend.common.serialization.proto", + "org.jetbrains.kotlin.backend.common.serialization.signature", + "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.serialization", + "org.jetbrains.kotlin.backend.jvm.serialization.proto", + "org.jetbrains.kotlin.backend.wasm", + "org.jetbrains.kotlin.backend.wasm.dce", + "org.jetbrains.kotlin.backend.wasm.ir2wasm", + "org.jetbrains.kotlin.backend.wasm.lower", + "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.jvm", + "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.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.dce", + "org.jetbrains.kotlin.cli.js.internal", + "org.jetbrains.kotlin.cli.js.klib", + "org.jetbrains.kotlin.cli.jvm", + "org.jetbrains.kotlin.cli.jvm.compiler", + "org.jetbrains.kotlin.cli.jvm.compiler.builder", + "org.jetbrains.kotlin.cli.jvm.compiler.jarfs", + "org.jetbrains.kotlin.cli.jvm.compiler.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.plugins", + "org.jetbrains.kotlin.codegen", + "org.jetbrains.kotlin.codegen.binding", + "org.jetbrains.kotlin.codegen.context", + "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.range", + "org.jetbrains.kotlin.codegen.range.comparison", + "org.jetbrains.kotlin.codegen.range.forLoop", + "org.jetbrains.kotlin.codegen.range.inExpression", + "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.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.expression", + "org.jetbrains.kotlin.fir.analysis.checkers.extended", + "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.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.backend", + "org.jetbrains.kotlin.fir.backend.generators", + "org.jetbrains.kotlin.fir.backend.js", + "org.jetbrains.kotlin.fir.backend.jvm", + "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.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.jvm", + "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.signaturer", + "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.components", + "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.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.backend.wasm.lower", + "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.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.linkage", + "org.jetbrains.kotlin.ir.linkage.partial", + "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.coroutine", + "org.jetbrains.kotlin.js.dce", + "org.jetbrains.kotlin.js.descriptorUtils", + "org.jetbrains.kotlin.js.facade", + "org.jetbrains.kotlin.js.facade.exceptions", + "org.jetbrains.kotlin.js.inline", + "org.jetbrains.kotlin.js.inline.clean", + "org.jetbrains.kotlin.js.inline.context", + "org.jetbrains.kotlin.js.inline.util", + "org.jetbrains.kotlin.js.inline.util.collectors", + "org.jetbrains.kotlin.js.inline.util.rewriters", + "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.patterns.typePredicates", + "org.jetbrains.kotlin.js.resolve", + "org.jetbrains.kotlin.js.resolve.diagnostics", + "org.jetbrains.kotlin.js.sourceMap", + "org.jetbrains.kotlin.js.translate.callTranslator", + "org.jetbrains.kotlin.js.translate.context", + "org.jetbrains.kotlin.js.translate.context.generator", + "org.jetbrains.kotlin.js.translate.declaration", + "org.jetbrains.kotlin.js.translate.expression", + "org.jetbrains.kotlin.js.translate.extensions", + "org.jetbrains.kotlin.js.translate.general", + "org.jetbrains.kotlin.js.translate.initializer", + "org.jetbrains.kotlin.js.translate.intrinsic", + "org.jetbrains.kotlin.js.translate.intrinsic.functions", + "org.jetbrains.kotlin.js.translate.intrinsic.functions.basic", + "org.jetbrains.kotlin.js.translate.intrinsic.functions.factories", + "org.jetbrains.kotlin.js.translate.intrinsic.objects", + "org.jetbrains.kotlin.js.translate.intrinsic.operation", + "org.jetbrains.kotlin.js.translate.operation", + "org.jetbrains.kotlin.js.translate.reference", + "org.jetbrains.kotlin.js.translate.test", + "org.jetbrains.kotlin.js.translate.utils", + "org.jetbrains.kotlin.js.translate.utils.jsAstUtils", + "org.jetbrains.kotlin.js.translate.utils.mutator", + "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.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.metadata", + "org.jetbrains.kotlin.library.metadata.impl", + "org.jetbrains.kotlin.library.metadata.resolver", + "org.jetbrains.kotlin.library.metadata.resolver.impl", + "org.jetbrains.kotlin.library.resolver", + "org.jetbrains.kotlin.library.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.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.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.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.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.builtins", + "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.serialization.konan", + "org.jetbrains.kotlin.serialization.konan.impl", + "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.javaslang", + "org.jetbrains.kotlin.util.slicedMap", + "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.intellij", + "org.jetbrains.kotlin.utils.kapt", + "org.jetbrains.kotlin.utils.repl", + "org.jetbrains.kotlin.utils.strings", + "org.jetbrains.kotlin.wasm.analyze", + "org.jetbrains.kotlin.wasm.ir", + "org.jetbrains.kotlin.wasm.ir.convertors", + "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.keymap", + "org.jline.reader", + "org.jline.reader.impl", + "org.jline.reader.impl.history", + "org.jline.terminal", + "org.jline.terminal.impl", + "org.jline.terminal.impl.jansi", + "org.jline.terminal.impl.jansi.freebsd", + "org.jline.terminal.impl.jansi.linux", + "org.jline.terminal.impl.jansi.osx", + "org.jline.terminal.impl.jansi.win", + "org.jline.terminal.impl.jna", + "org.jline.terminal.impl.jna.freebsd", + "org.jline.terminal.impl.jna.linux", + "org.jline.terminal.impl.jna.osx", + "org.jline.terminal.impl.jna.solaris", + "org.jline.terminal.impl.jna.win", + "org.jline.terminal.spi", + "org.jline.utils", + "org.picocontainer", + "org.picocontainer.defaults" + ], + "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", + "kotlin.collections", + "kotlin.collections.builders", + "kotlin.collections.jdk8", + "kotlin.collections.unsigned", + "kotlin.comparisons", + "kotlin.concurrent", + "kotlin.contracts", + "kotlin.coroutines", + "kotlin.coroutines.cancellation", + "kotlin.coroutines.intrinsics", + "kotlin.coroutines.jvm.internal", + "kotlin.enums", + "kotlin.experimental", + "kotlin.internal", + "kotlin.internal.jdk7", + "kotlin.internal.jdk8", + "kotlin.io", + "kotlin.io.encoding", + "kotlin.io.path", + "kotlin.jdk7", + "kotlin.js", + "kotlin.jvm", + "kotlin.jvm.functions", + "kotlin.jvm.internal", + "kotlin.jvm.internal.markers", + "kotlin.jvm.internal.unsafe", + "kotlin.jvm.jdk8", + "kotlin.jvm.optionals", + "kotlin.math", + "kotlin.properties", + "kotlin.random", + "kotlin.random.jdk8", + "kotlin.ranges", + "kotlin.reflect", + "kotlin.sequences", + "kotlin.streams.jdk8", + "kotlin.system", + "kotlin.text", + "kotlin.text.jdk8", + "kotlin.time", + "kotlin.time.jdk8" + ], + "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm": [ + "kotlinx.coroutines", + "kotlinx.coroutines.channels", + "kotlinx.coroutines.debug", + "kotlinx.coroutines.debug.internal", + "kotlinx.coroutines.flow", + "kotlinx.coroutines.flow.internal", + "kotlinx.coroutines.internal", + "kotlinx.coroutines.intrinsics", + "kotlinx.coroutines.scheduling", + "kotlinx.coroutines.selects", + "kotlinx.coroutines.sync", + "kotlinx.coroutines.test" + ], + "org.jetbrains:annotations": [ + "org.intellij.lang.annotations", + "org.jetbrains.annotations" + ], "org.junit.jupiter:junit-jupiter-api": [ "org.junit.jupiter.api", "org.junit.jupiter.api.condition", @@ -2559,6 +3659,17 @@ "org.dom4j:dom4j", "org.hamcrest:hamcrest-core", "org.javassist:javassist", + "org.jetbrains.intellij.deps:trove4j", + "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-common", + "org.jetbrains.kotlin:kotlin-stdlib-jdk7", + "org.jetbrains.kotlin:kotlin-stdlib-jdk8", + "org.jetbrains.kotlinx:kotlinx-coroutines-core", + "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm", + "org.jetbrains:annotations", "org.junit.jupiter:junit-jupiter-api", "org.junit.jupiter:junit-jupiter-engine", "org.junit.platform:junit-platform-commons", @@ -2819,6 +3930,41 @@ "org.codehaus.groovy.ast.builder.AstBuilderTransformation" ] }, + "org.jetbrains.kotlin:kotlin-compiler": { + "javax.annotation.processing.Processor": [ + "javaslang.match.PatternsProcessor" + ], + "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.jvmSignature.KotlinToJvmSignatureMapper": [ + "org.jetbrains.kotlin.codegen.signature.KotlinToJvmSignatureMapperImpl" + ], + "org.jetbrains.kotlin.util.ModuleVisibilityHelper": [ + "org.jetbrains.kotlin.cli.common.ModuleVisibilityHelperImpl" + ], + "org.jline.terminal.spi.JansiSupport": [ + "org.jline.terminal.impl.jansi.JansiSupportImpl" + ], + "org.jline.terminal.spi.JnaSupport": [ + "org.jline.terminal.impl.jna.JnaSupportImpl" + ] + }, + "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.junit.jupiter:junit-jupiter-engine": { "org.junit.platform.engine.TestEngine": [ "org.junit.jupiter.engine.JupiterTestEngine" diff --git a/java/gazelle/BUILD.bazel b/java/gazelle/BUILD.bazel index 2ea896f5..ad2851ce 100644 --- a/java/gazelle/BUILD.bazel +++ b/java/gazelle/BUILD.bazel @@ -17,6 +17,7 @@ go_library( deps = [ "//java/gazelle/javaconfig", "//java/gazelle/private/java", + "//java/gazelle/private/kotlin", "//java/gazelle/private/javaparser", "//java/gazelle/private/logconfig", "//java/gazelle/private/maven", diff --git a/java/gazelle/configure.go b/java/gazelle/configure.go index e2b405b3..d0dedb11 100644 --- a/java/gazelle/configure.go +++ b/java/gazelle/configure.go @@ -66,6 +66,7 @@ func (jc *Configurer) KnownDirectives() []string { javaconfig.JavaGenerateProto, javaconfig.JavaMavenRepositoryName, javaconfig.JavaAnnotationProcessorPlugin, + javaconfig.JvmKotlinEnabled, } } @@ -146,6 +147,16 @@ func (jc *Configurer) Configure(c *config.Config, rel string, f *rule.File) { jc.lang.logger.Fatal().Msgf("invalid value for directive %q: %q: couldn't parse annotation processor class-name: %v", javaconfig.JavaAnnotationProcessorPlugin, parts[1], err) } cfg.AddAnnotationProcessorPlugin(*annotationClassName, *processorClassName) + 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 98d83358..a88912f1 100644 --- a/java/gazelle/generate.go +++ b/java/gazelle/generate.go @@ -55,43 +55,68 @@ 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" }) + } + + isModule := cfg.ModuleGranularity() == "module" - if len(javaFilenamesRelativeToPackage) == 0 { + if len(srcFilenamesRelativeToPackage) == 0 { if !isModule || !cfg.IsModuleRoot() { return res } } - sort.Strings(javaFilenamesRelativeToPackage) - + sort.Strings(srcFilenamesRelativeToPackage) 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") } + l.logger.Debug().Msgf("Parsed package: %v", javaPkg.Name.Name) + for _, m := range javaPkg.Mains.SortedSlice() { + l.logger.Debug().Msgf("Found main file: %v", m) + } + // We exclude intra-package imports to avoid self-dependencies. // This isn't a great heuristic for a few reasons: // 1. We may want to split targets with more granularity than per-package. // 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 } @@ -137,14 +162,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{ @@ -162,12 +187,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{ @@ -195,7 +220,10 @@ 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 } @@ -472,8 +500,7 @@ func accumulateJavaFile(cfg *javaconfig.Config, testJavaFiles, testHelperJavaFil } func (l javaLang) generateJavaLibrary(file *rule.File, pathToPackageRelativeToBazelWorkspace string, name string, srcsRelativeToBazelWorkspace []string, packages, imports *sorted_set.SortedSet[types.PackageName], exports *sorted_set.SortedSet[types.PackageName], annotationProcessorClasses *sorted_set.SortedSet[types.ClassName], testonly bool, javaLibraryRuleKind string, res *language.GenerateResult) { - const ruleKind = "java_library" - r := rule.NewRule(ruleKind, name) + r := rule.NewRule(javaLibraryRuleKind, name) srcs := make([]string, 0, len(srcsRelativeToBazelWorkspace)) for _, src := range srcsRelativeToBazelWorkspace { diff --git a/java/gazelle/javaconfig/config.go b/java/gazelle/javaconfig/config.go index a3cc492c..5ddd7184 100644 --- a/java/gazelle/javaconfig/config.go +++ b/java/gazelle/javaconfig/config.go @@ -53,6 +53,11 @@ const ( // JavaAnnotationProcessorPlugin tells the code generator about specific java_plugin targets needed to process // specific annotations. JavaAnnotationProcessorPlugin = "java_annotation_processor_plugin" + + // JvmKotlinEnabled tells the code generator whether to support `kt_jvm_library` rules for Kotlin sources. + // Can be either "true" or "false". Defaults to "false". + // This requires importing the `@rules_kotlin` repository into your workspace. + JvmKotlinEnabled = "jvm_kotlin_enabled" ) // Configs is an extension of map[string]*Config. It provides finding methods @@ -75,6 +80,7 @@ func (c *Config) NewChild() *Config { extensionEnabled: c.extensionEnabled, isModuleRoot: false, generateProto: true, + kotlinEnabled: c.kotlinEnabled, mavenInstallFile: c.mavenInstallFile, moduleGranularity: c.moduleGranularity, repoRoot: c.repoRoot, @@ -105,6 +111,7 @@ type Config struct { extensionEnabled bool isModuleRoot bool generateProto bool + kotlinEnabled bool mavenInstallFile string moduleGranularity string repoRoot string @@ -128,6 +135,7 @@ func New(repoRoot string) *Config { extensionEnabled: true, isModuleRoot: false, generateProto: true, + kotlinEnabled: false, mavenInstallFile: "maven_install.json", moduleGranularity: "package", repoRoot: repoRoot, @@ -163,6 +171,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 c293d8eb..4c3daae0 100644 --- a/java/gazelle/lang.go +++ b/java/gazelle/lang.go @@ -112,6 +112,20 @@ var javaLibraryKind = 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, @@ -121,6 +135,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) @@ -154,6 +169,12 @@ var baseJavaLoads = []rule.LoadInfo{ "java_test_suite", }, }, + { + Name: "@rules_kotlin//kotlin:jvm.bzl", + Symbols: []string{ + "kt_jvm_library", + }, + }, } func (l javaLang) Loads() []rule.LoadInfo { 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..e8b668b6 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 parser's. 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; } diff --git a/java/gazelle/private/kotlin/BUILD.bazel b/java/gazelle/private/kotlin/BUILD.bazel new file mode 100644 index 00000000..c93a0348 --- /dev/null +++ b/java/gazelle/private/kotlin/BUILD.bazel @@ -0,0 +1,23 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +# gazelle:exclude testdata + +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"], +) 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/resolve.go b/java/gazelle/resolve.go index 7d0e6c82..fd7d4325 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,16 @@ 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" { + cfg := c.Exts[languageName].(javaconfig.Configs)[f.Pkg] + if cfg == nil { + jr.lang.logger.Fatal().Msg("failed retrieving package config") + } + isLibraryFn := isJavaLibrary + if cfg.KotlinEnabled() { + isLibraryFn = isJvmLibrary + } + if !isLibraryFn(r.Kind()) && r.Kind() != "java_test_suite" { + log.Trace().Str("kind", r.Kind()).Str("label", label.New("", f.Pkg, r.Name()).String()).Msg("return not JVM/Java library or test suite") return nil } @@ -226,6 +236,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" { @@ -295,10 +308,18 @@ func (jr *Resolver) resolveSinglePackage(c *config.Config, pc *javaconfig.Config return label.NoLabel } +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 54761fd6..9c4537ea 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 @@ -83,6 +185,37 @@ java_library( 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"], +) `, }, } { @@ -177,6 +310,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..fb636112 --- /dev/null +++ b/java/gazelle/testdata/java_lib_with_kotlin_dep/WORKSPACE @@ -0,0 +1,16 @@ +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", + + urls = ["https://github.com/bazelbuild/rules_kotlin/releases/download/v%s/rules_kotlin-v%s.tar.gz" % (rules_kotlin_version, rules_kotlin_version)], + sha256 = rules_kotlin_sha, +) + +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..db49faba --- /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!" + } +} \ No newline at end of file diff --git a/java/gazelle/testdata/kotlin_bin/WORKSPACE b/java/gazelle/testdata/kotlin_bin/WORKSPACE new file mode 100644 index 00000000..fb636112 --- /dev/null +++ b/java/gazelle/testdata/kotlin_bin/WORKSPACE @@ -0,0 +1,16 @@ +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", + + urls = ["https://github.com/bazelbuild/rules_kotlin/releases/download/v%s/rules_kotlin-v%s.tar.gz" % (rules_kotlin_version, rules_kotlin_version)], + sha256 = rules_kotlin_sha, +) + +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/kotlin_bin/maven_install.json b/java/gazelle/testdata/kotlin_bin/maven_install.json new file mode 100644 index 00000000..6c01c298 --- /dev/null +++ b/java/gazelle/testdata/kotlin_bin/maven_install.json @@ -0,0 +1 @@ +{"version": "2"} diff --git a/java/gazelle/testdata/kotlin_bin/src/main/java/com/example/hello/BUILD.in b/java/gazelle/testdata/kotlin_bin/src/main/java/com/example/hello/BUILD.in new file mode 100644 index 00000000..c9ac7dee --- /dev/null +++ b/java/gazelle/testdata/kotlin_bin/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/kotlin_bin/src/main/java/com/example/hello/BUILD.out b/java/gazelle/testdata/kotlin_bin/src/main/java/com/example/hello/BUILD.out new file mode 100644 index 00000000..f4b94502 --- /dev/null +++ b/java/gazelle/testdata/kotlin_bin/src/main/java/com/example/hello/BUILD.out @@ -0,0 +1,18 @@ +load("@rules_java//java:defs.bzl", "java_binary") +load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") + +# gazelle:jvm_kotlin_enabled true + +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/kotlin_bin/src/main/java/com/example/hello/Hello.kt b/java/gazelle/testdata/kotlin_bin/src/main/java/com/example/hello/Hello.kt new file mode 100644 index 00000000..923e425a --- /dev/null +++ b/java/gazelle/testdata/kotlin_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/kotlin_lib/WORKSPACE b/java/gazelle/testdata/kotlin_lib/WORKSPACE new file mode 100644 index 00000000..fb636112 --- /dev/null +++ b/java/gazelle/testdata/kotlin_lib/WORKSPACE @@ -0,0 +1,16 @@ +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", + + urls = ["https://github.com/bazelbuild/rules_kotlin/releases/download/v%s/rules_kotlin-v%s.tar.gz" % (rules_kotlin_version, rules_kotlin_version)], + sha256 = rules_kotlin_sha, +) + +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/kotlin_lib/maven_install.json b/java/gazelle/testdata/kotlin_lib/maven_install.json new file mode 100644 index 00000000..6c01c298 --- /dev/null +++ b/java/gazelle/testdata/kotlin_lib/maven_install.json @@ -0,0 +1 @@ +{"version": "2"} diff --git a/java/gazelle/testdata/kotlin_lib/src/main/java/com/example/hello/BUILD.in b/java/gazelle/testdata/kotlin_lib/src/main/java/com/example/hello/BUILD.in new file mode 100644 index 00000000..c9ac7dee --- /dev/null +++ b/java/gazelle/testdata/kotlin_lib/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/kotlin_lib/src/main/java/com/example/hello/BUILD.out b/java/gazelle/testdata/kotlin_lib/src/main/java/com/example/hello/BUILD.out new file mode 100644 index 00000000..f5ac86d2 --- /dev/null +++ b/java/gazelle/testdata/kotlin_lib/src/main/java/com/example/hello/BUILD.out @@ -0,0 +1,9 @@ +load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") + +# gazelle:jvm_kotlin_enabled true + +kt_jvm_library( + name = "hello", + srcs = ["Hello.kt"], + visibility = ["//:__subpackages__"], +) diff --git a/java/gazelle/testdata/kotlin_lib/src/main/java/com/example/hello/Hello.kt b/java/gazelle/testdata/kotlin_lib/src/main/java/com/example/hello/Hello.kt new file mode 100644 index 00000000..119d4742 --- /dev/null +++ b/java/gazelle/testdata/kotlin_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/kotlin_lib_with_java_dep/WORKSPACE b/java/gazelle/testdata/kotlin_lib_with_java_dep/WORKSPACE new file mode 100644 index 00000000..fb636112 --- /dev/null +++ b/java/gazelle/testdata/kotlin_lib_with_java_dep/WORKSPACE @@ -0,0 +1,16 @@ +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", + + urls = ["https://github.com/bazelbuild/rules_kotlin/releases/download/v%s/rules_kotlin-v%s.tar.gz" % (rules_kotlin_version, rules_kotlin_version)], + sha256 = rules_kotlin_sha, +) + +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/kotlin_lib_with_java_dep/maven_install.json b/java/gazelle/testdata/kotlin_lib_with_java_dep/maven_install.json new file mode 100644 index 00000000..6c01c298 --- /dev/null +++ b/java/gazelle/testdata/kotlin_lib_with_java_dep/maven_install.json @@ -0,0 +1 @@ +{"version": "2"} diff --git a/java/gazelle/testdata/kotlin_lib_with_java_dep/src/main/java/com/example/hello/BUILD.in b/java/gazelle/testdata/kotlin_lib_with_java_dep/src/main/java/com/example/hello/BUILD.in new file mode 100644 index 00000000..c9ac7dee --- /dev/null +++ b/java/gazelle/testdata/kotlin_lib_with_java_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/kotlin_lib_with_java_dep/src/main/java/com/example/hello/BUILD.out b/java/gazelle/testdata/kotlin_lib_with_java_dep/src/main/java/com/example/hello/BUILD.out new file mode 100644 index 00000000..d6b3f929 --- /dev/null +++ b/java/gazelle/testdata/kotlin_lib_with_java_dep/src/main/java/com/example/hello/BUILD.out @@ -0,0 +1,10 @@ +load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") + +# gazelle:jvm_kotlin_enabled true + +kt_jvm_library( + name = "hello", + srcs = ["Hello.kt"], + visibility = ["//:__subpackages__"], + deps = ["//src/main/java/com/example/hello/greeter"], +) diff --git a/java/gazelle/testdata/kotlin_lib_with_java_dep/src/main/java/com/example/hello/Hello.kt b/java/gazelle/testdata/kotlin_lib_with_java_dep/src/main/java/com/example/hello/Hello.kt new file mode 100644 index 00000000..e85d1ee0 --- /dev/null +++ b/java/gazelle/testdata/kotlin_lib_with_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/kotlin_lib_with_java_dep/src/main/java/com/example/hello/greeter/BUILD.in b/java/gazelle/testdata/kotlin_lib_with_java_dep/src/main/java/com/example/hello/greeter/BUILD.in new file mode 100644 index 00000000..c9ac7dee --- /dev/null +++ b/java/gazelle/testdata/kotlin_lib_with_java_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/kotlin_lib_with_java_dep/src/main/java/com/example/hello/greeter/BUILD.out b/java/gazelle/testdata/kotlin_lib_with_java_dep/src/main/java/com/example/hello/greeter/BUILD.out new file mode 100644 index 00000000..2424a699 --- /dev/null +++ b/java/gazelle/testdata/kotlin_lib_with_java_dep/src/main/java/com/example/hello/greeter/BUILD.out @@ -0,0 +1,9 @@ +load("@rules_java//java:defs.bzl", "java_library") + +# gazelle:jvm_kotlin_enabled true + +java_library( + name = "greeter", + srcs = ["Greeter.java"], + visibility = ["//:__subpackages__"], +) diff --git a/java/gazelle/testdata/kotlin_lib_with_java_dep/src/main/java/com/example/hello/greeter/Greeter.java b/java/gazelle/testdata/kotlin_lib_with_java_dep/src/main/java/com/example/hello/greeter/Greeter.java new file mode 100644 index 00000000..3f0a9a38 --- /dev/null +++ b/java/gazelle/testdata/kotlin_lib_with_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/kotlin_lib_with_java_src/WORKSPACE b/java/gazelle/testdata/kotlin_lib_with_java_src/WORKSPACE new file mode 100644 index 00000000..fb636112 --- /dev/null +++ b/java/gazelle/testdata/kotlin_lib_with_java_src/WORKSPACE @@ -0,0 +1,16 @@ +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", + + urls = ["https://github.com/bazelbuild/rules_kotlin/releases/download/v%s/rules_kotlin-v%s.tar.gz" % (rules_kotlin_version, rules_kotlin_version)], + sha256 = rules_kotlin_sha, +) + +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/kotlin_lib_with_java_src/maven_install.json b/java/gazelle/testdata/kotlin_lib_with_java_src/maven_install.json new file mode 100644 index 00000000..6c01c298 --- /dev/null +++ b/java/gazelle/testdata/kotlin_lib_with_java_src/maven_install.json @@ -0,0 +1 @@ +{"version": "2"} diff --git a/java/gazelle/testdata/kotlin_lib_with_java_src/src/main/java/com/example/hello/BUILD.in b/java/gazelle/testdata/kotlin_lib_with_java_src/src/main/java/com/example/hello/BUILD.in new file mode 100644 index 00000000..c9ac7dee --- /dev/null +++ b/java/gazelle/testdata/kotlin_lib_with_java_src/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/kotlin_lib_with_java_src/src/main/java/com/example/hello/BUILD.out b/java/gazelle/testdata/kotlin_lib_with_java_src/src/main/java/com/example/hello/BUILD.out new file mode 100644 index 00000000..e323da9f --- /dev/null +++ b/java/gazelle/testdata/kotlin_lib_with_java_src/src/main/java/com/example/hello/BUILD.out @@ -0,0 +1,12 @@ +load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") + +# gazelle:jvm_kotlin_enabled true + +kt_jvm_library( + name = "hello", + srcs = [ + "Goodbye.java", + "Hello.kt", + ], + visibility = ["//:__subpackages__"], +) diff --git a/java/gazelle/testdata/kotlin_lib_with_java_src/src/main/java/com/example/hello/Goodbye.java b/java/gazelle/testdata/kotlin_lib_with_java_src/src/main/java/com/example/hello/Goodbye.java new file mode 100644 index 00000000..befcf703 --- /dev/null +++ b/java/gazelle/testdata/kotlin_lib_with_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/kotlin_lib_with_java_src/src/main/java/com/example/hello/Hello.kt b/java/gazelle/testdata/kotlin_lib_with_java_src/src/main/java/com/example/hello/Hello.kt new file mode 100644 index 00000000..119d4742 --- /dev/null +++ b/java/gazelle/testdata/kotlin_lib_with_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/private/contrib_rules_jvm_deps.zip b/java/private/contrib_rules_jvm_deps.zip index 06035200..e5e6d8dc 100644 Binary files a/java/private/contrib_rules_jvm_deps.zip and b/java/private/contrib_rules_jvm_deps.zip differ 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..2edadaf3 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 @@ -54,16 +54,7 @@ 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(); @@ -73,71 +64,28 @@ 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 +162,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 +183,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 +229,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 +241,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 +347,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 +414,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 +427,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..2fa58dfc 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 @@ -24,10 +24,12 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedSet; +import java.util.stream.Collectors; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -120,17 +122,38 @@ 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(file -> directory.resolve(file)).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(); + data.merge(parser.parseClasses(kotlinFiles)); + } 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. - return Package.newBuilder().setName("").build(); + ClasspathParser parser = new ClasspathParser(); + + if (!javaFiles.isEmpty()) { + try { + parser.parseClasses(directory, javaFiles); + data.merge(parser.getParsedPackageData()); + } 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. + return Package.newBuilder().setName("").build(); + } catch (Exception ex) { + logger.error("Error parsing Java files", ex); + throw ex; + } } - Set packages = parser.getPackages(); + Set packages = data.packages; if (packages.size() > 1) { throw new StatusRuntimeException( Status.INVALID_ARGUMENT.withDescription( @@ -143,21 +166,21 @@ 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()); + data.usedPackagesWithoutSpecificTypes); Builder packageBuilder = Package.newBuilder() .setName(Iterables.getOnlyElement(packages)) - .addAllImportedClasses(parser.getUsedTypes()) - .addAllExportedClasses(parser.getExportedTypes()) + .addAllImportedClasses(data.usedTypes) + .addAllExportedClasses(data.exportedTypes) .addAllImportedPackagesWithoutSpecificClasses( - parser.getUsedPackagesWithoutSpecificTypes()) - .addAllMains(parser.getMainClasses()); - for (Map.Entry classEntry : - parser.perClassData.entrySet()) { + data.usedPackagesWithoutSpecificTypes) + .addAllMains(data.mainClasses); + for (Map.Entry classEntry : + data.perClassData.entrySet()) { PerClassMetadata.Builder perClassMetadata = PerClassMetadata.newBuilder() .addAllAnnotationClassNames(classEntry.getValue().annotations); 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 3febeaba..036d537d 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 @@ -8,11 +8,13 @@ public class JavaIdentifier implements Comparable { private final String className; /** * 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..d0d90a7b --- /dev/null +++ b/java/src/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/KtParser.java @@ -0,0 +1,457 @@ +package com.github.bazel_contrib.contrib_rules_jvm.javaparser.generators; + +import java.nio.file.Path; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Optional; +import java.util.Stack; +import java.util.stream.Collectors; + +// import org.jetbrains.kotlin.resolve.lazy.JvmResolveUtil; +import org.jetbrains.kotlin.cli.common.messages.MessageCollector; +import org.jetbrains.kotlin.config.CommonConfigurationKeys; +import org.jetbrains.kotlin.analyzer.AnalysisResult; +import org.jetbrains.kotlin.cli.jvm.compiler.NoScopeRecordCliBindingTrace; +import org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM; +import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles; +import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment; +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.NameUtils; +import org.jetbrains.kotlin.name.Name; +import org.jetbrains.kotlin.psi.KotlinReferenceProvidersService; +import org.jetbrains.kotlin.psi.KtAnnotated; +import org.jetbrains.kotlin.psi.KtClass; +import org.jetbrains.kotlin.psi.KtClassBody; +import org.jetbrains.kotlin.psi.KtElement; +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.KtSimpleNameExpression; +import org.jetbrains.kotlin.psi.KtTreeVisitorVoid; +import org.jetbrains.kotlin.psi.KtTypeElement; +import org.jetbrains.kotlin.psi.KtTypeReference; +import org.jetbrains.kotlin.psi.KtUserType; +import org.jetbrains.kotlin.resolve.BindingContext; +import org.jetbrains.kotlin.types.KotlinType; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +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.PsiReference; +import com.intellij.psi.search.GlobalSearchScope; + +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)).toList(); + + for (VirtualFile virtualFile : virtualFiles) { + if (virtualFile == null) { + throw new IllegalArgumentException("File not found: " + files.get(0)); + } + KtFile ktFile = (KtFile) psiManager.findFile(virtualFile); + + // AnalysisResult analysis = TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration( + // env.getProject(), + // List.of(ktFile), + // new NoScopeRecordCliBindingTrace(), + // compilerConf, + // env::createPackagePartProvider + // // () -> env.createPackagePartProvider( + // // GlobalSearchScope.filesScope(env.getProject(), List.of(file))) + // ); + // // AnalysisResult analysis = JvmResolveUtil.analyze(ktFile, env); + // BindingContext context = analysis.getBindingContext(); + + 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"); + + // conf.put(CommonConfigurationKeys.MESSAGE_COLLECTOR_KEY, MessageCollector.Companion.getNONE()); + return conf; + } + + public static class KtFileVisitor extends KtTreeVisitorVoid { + final ParsedPackageData packageData = new ParsedPackageData(); + + private Stack visibilityStack = new Stack<>(); + private HashMap fqImportByNameOrAlias = new HashMap<>(); + + @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); + } + + @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); + } + + super.visitProperty(property); + popState(property); + } + + @Override + public void visitNamedFunction(KtNamedFunction function) { + pushState(function); + if (function.isLocal() || !isVisible()) { + super.visitNamedFunction(function); + popState(function); + return; + } + + 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); + } + super.visitNamedFunction(function); + 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()); + + // PsiReference[] references = referenceService.getReferences( + // reference + // ); + // for (PsiReference psiReference : references) { + // logger.info("\tResolved reference: " + psiReference.toString()); + // } + // var ktType = context.get(BindingContext.TYPE, reference); + // logger.info("\tResolved KotlinType: " + ktType.toString()); + + 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: " + expression.getText()); + logger.debug("\tReferenced name: " + expression.getReferencedName()); + + // PsiReference[] references = referenceService.getReferences( + // expression.getReferencedNameElement() + // ); + // for (PsiReference psiReference : references) { + // logger.info("\tResolved reference for named element: " + psiReference.toString()); + // } + + // references = referenceService.getReferences( + // expression.getIdentifier() + // ); + // for (PsiReference psiReference : references) { + // logger.info("\tResolved reference for identifier: " + psiReference.toString()); + // } + + // KotlinType ktType = context.getType(expression); + // logger.info("\tResolved KotlinType: " + ktType.toString()); + + super.visitSimpleNameExpression(expression); + } + + + 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 (!function.getName().equals("main") || 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); + maybeQualifiedType.ifPresent( + qualifiedType -> { + // TODO: Check for java and Kotlin standard library types. + packageData.exportedTypes.add(qualifiedType); + } + ); + } + + private KtTypeElement getRootType(KtTypeReference typeReference) { + KtTypeElement typeElement = typeReference.getTypeElement(); + if (typeElement instanceof KtNullableType nullableType) { + return nullableType.getInnerType(); + } + return typeElement; + } + + private Optional tryGetFullyQualifiedName(KtTypeElement typeElement) { + if (typeElement instanceof KtUserType userType) { + 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)); + } + + private void pushState(KtElement element) { + if (element instanceof KtModifierListOwner modifiedThing) { + pushVisibility(modifiedThing); + } + } + + private void popState(KtElement element) { + if (element instanceof KtModifierListOwner modifiedThing) { + 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 static enum Visibility { + PUBLIC, + PROTECTED, + INTERNAL, + PRIVATE, + } +} \ No newline at end of file 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..ab681243 --- /dev/null +++ b/java/src/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/ParsedPackageData.java @@ -0,0 +1,44 @@ +package com.github.bazel_contrib.contrib_rules_jvm.javaparser.generators; + +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import java.util.TreeMap; + +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..b3a6257b --- /dev/null +++ b/java/src/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/PerClassData.java @@ -0,0 +1,75 @@ +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 10e2f606..88fbdc52 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 @@ -201,9 +201,9 @@ public void testAnnotationAfterImport() throws IOException { assertEquals( Map.of( "workspace.com.gazelle.java.javaparser.generators.AnnotationAfterImport", - new ClasspathParser.PerClassData( + new PerClassData( treeSet("com.example.FlakyTest"), new TreeMap<>(), new TreeMap<>())), - parser.perClassData); + parser.getParsedPackageData().perClassData); } @Test @@ -217,9 +217,9 @@ public void testAnnotationAfterImportOnNestedClass() throws IOException { assertEquals( Map.of( "workspace.com.gazelle.java.javaparser.generators.NestedClassAnnotations.Inner", - new ClasspathParser.PerClassData( + new PerClassData( treeSet("com.example.FlakyTest"), new TreeMap<>(), new TreeMap<>())), - parser.perClassData); + parser.getParsedPackageData().perClassData); } @Test @@ -239,21 +239,21 @@ 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 PerClassData( new TreeSet<>(), new TreeMap<>(), expectedOuterClassFieldAnnotations)); expected.put( "workspace.com.gazelle.java.javaparser.generators.AnnotationOnField.InnerClass", - new ClasspathParser.PerClassData( + new PerClassData( new TreeSet<>(), new TreeMap<>(), expectedInnerClassFieldAnnotations)); expected.put( "workspace.com.gazelle.java.javaparser.generators.AnnotationOnField.InnerEnum", - new ClasspathParser.PerClassData( + new PerClassData( new TreeSet<>(), new TreeMap<>(), expectedInnerEnumFieldAnnotations)); - assertEquals(expected, parser.perClassData); + assertEquals(expected, parser.getParsedPackageData().perClassData); } @Test @@ -270,9 +270,9 @@ public void testAnnotationAfterImportOnMethod() throws IOException { assertEquals( Map.of( "workspace.com.gazelle.java.javaparser.generators.AnnotationAfterImportOnMethod", - new ClasspathParser.PerClassData( + new PerClassData( new TreeSet<>(), expectedPerMethodAnnotations, new TreeMap<>())), - parser.perClassData); + parser.getParsedPackageData().perClassData); } @Test @@ -288,9 +288,9 @@ public void testAnnotationFromJavaStandardLibrary() throws IOException { assertEquals( Map.of( "workspace.com.gazelle.java.javaparser.generators.AnnotationFromJavaStandardLibrary", - new ClasspathParser.PerClassData( + new PerClassData( treeSet("Deprecated"), new TreeMap<>(), new TreeMap<>())), - parser.perClassData); + parser.getParsedPackageData().perClassData); } @Test @@ -306,9 +306,9 @@ public void testAnnotationWithoutImport() throws IOException { assertEquals( Map.of( "workspace.com.gazelle.java.javaparser.generators.AnnotationWithoutImport", - new ClasspathParser.PerClassData( + new PerClassData( treeSet("WhoKnowsWhereIAmFrom"), new TreeMap<>(), new TreeMap<>())), - parser.perClassData); + parser.getParsedPackageData().perClassData); } @Test @@ -342,7 +342,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")); @@ -350,8 +350,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..a6ffab66 --- /dev/null +++ b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/KtParserTest.java @@ -0,0 +1,173 @@ +package com.github.bazel_contrib.contrib_rules_jvm.javaparser.generators; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.FileSystems; +import java.nio.file.FileVisitOption; +import java.nio.file.FileSystem; +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.Map; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.junit.jupiter.api.Assertions; +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 (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); + } + + private List getPathsWithNames(String... names) throws IOException { + Set namesSet = Set.of(names); + return Files.walk(directory).filter(file -> namesSet.contains(file.getFileName().toString())).toList(); + } +} 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..e640c666 --- /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/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..33bbeef4 --- /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,42 @@ +package workspace.com.gazelle.kotlin.javaparser.generators + +import example.external.FinalProperty +import example.external.VarProperty +import example.external.ParameterizedReturn +import example.external.InternalReturn +import example.external.PrivateReturn +import example.external.ProtectedReturn +import example.external.PublicReturn + +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/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..43b91aa1 --- /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..f0968eed --- /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,8 @@ +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 { +} 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..b887b952 --- /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,9 @@ +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..0347fa04 --- /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,9 @@ +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/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..88765b90 --- /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,25 @@ +package workspace.com.gazelle.kotlin.javaparser.generators + +import example.external.FinalProperty +import example.external.VarProperty +import example.external.InternalReturn +import example.external.ProtectedReturn +import example.external.PublicReturn + +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/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..61d21391 --- /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,10 @@ +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() { + someFunction() +} \ No newline at end of file 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..476f7eda --- /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,5 @@ +package workspace.com.gazelle.kotlin.javaparser.generators + +import org.junit.jupiter.api.Assertions.* + +import org.junit.jupiter.api.*