From 4c088fd885617dd47291c2af165d71322c0cc0fd Mon Sep 17 00:00:00 2001 From: MorganR Date: Tue, 22 Oct 2024 22:38:35 +0100 Subject: [PATCH 01/19] Add basic failing kotlin test --- java/gazelle/testdata/kotlin_lib/WORKSPACE | 16 ++++++++++++++++ .../testdata/kotlin_lib/maven_install.json | 1 + .../src/main/java/com/example/hello/BUILD.in | 0 .../src/main/java/com/example/hello/BUILD.out | 7 +++++++ .../src/main/java/com/example/hello/Hello.kt | 8 ++++++++ 5 files changed, 32 insertions(+) create mode 100644 java/gazelle/testdata/kotlin_lib/WORKSPACE create mode 100644 java/gazelle/testdata/kotlin_lib/maven_install.json create mode 100644 java/gazelle/testdata/kotlin_lib/src/main/java/com/example/hello/BUILD.in create mode 100644 java/gazelle/testdata/kotlin_lib/src/main/java/com/example/hello/BUILD.out create mode 100644 java/gazelle/testdata/kotlin_lib/src/main/java/com/example/hello/Hello.kt diff --git a/java/gazelle/testdata/kotlin_lib/WORKSPACE b/java/gazelle/testdata/kotlin_lib/WORKSPACE new file mode 100644 index 00000000..4ae715f0 --- /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() # if you want the default. Otherwise see custom kotlinc distribution below + +load("@rules_kotlin//kotlin:core.bzl", "kt_register_toolchains") +kt_register_toolchains() # to use the default toolchain, otherwise see toolchains below 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..e69de29b 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..81e09432 --- /dev/null +++ b/java/gazelle/testdata/kotlin_lib/src/main/java/com/example/hello/BUILD.out @@ -0,0 +1,7 @@ +load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") + +kt_jvm_library( + name = "hello", + srcs = ["Hello.kt"], + visibility = ["//:__subpackages__"], +) diff --git a/java/gazelle/testdata/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..67be9f00 --- /dev/null +++ b/java/gazelle/testdata/kotlin_lib/src/main/java/com/example/hello/Hello.kt @@ -0,0 +1,8 @@ +package com.example.hello + +import kotlin.io.println + +@JvmStatic +fun sayHi() { + println("Hi!") +} From 15a071b13d985bdc6ad07c55e3ca81b2bd8945e5 Mon Sep 17 00:00:00 2001 From: MorganR Date: Tue, 22 Oct 2024 22:40:17 +0100 Subject: [PATCH 02/19] Remove comments from Kotlin test WORKSPACE --- java/gazelle/testdata/kotlin_lib/WORKSPACE | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/gazelle/testdata/kotlin_lib/WORKSPACE b/java/gazelle/testdata/kotlin_lib/WORKSPACE index 4ae715f0..f5f6402b 100644 --- a/java/gazelle/testdata/kotlin_lib/WORKSPACE +++ b/java/gazelle/testdata/kotlin_lib/WORKSPACE @@ -10,7 +10,7 @@ http_archive( ) load("@rules_kotlin//kotlin:repositories.bzl", "kotlin_repositories") -kotlin_repositories() # if you want the default. Otherwise see custom kotlinc distribution below +kotlin_repositories() load("@rules_kotlin//kotlin:core.bzl", "kt_register_toolchains") -kt_register_toolchains() # to use the default toolchain, otherwise see toolchains below +kt_register_toolchains() From 9c167a25d5ebd30a623edfab021d4b4782b020f1 Mon Sep 17 00:00:00 2001 From: MorganR Date: Tue, 22 Oct 2024 23:35:13 +0100 Subject: [PATCH 03/19] Add a directive for enabling Kotlin --- java/gazelle/configure.go | 11 +++++++++++ java/gazelle/javaconfig/config.go | 16 ++++++++++++++++ .../src/main/java/com/example/hello/BUILD.in | 1 + 3 files changed, 28 insertions(+) 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/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/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 index e69de29b..c9ac7dee 100644 --- 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 @@ -0,0 +1 @@ +# gazelle:jvm_kotlin_enabled true \ No newline at end of file From a40708bbeee2096c30a80964c825d307f21e9e8c Mon Sep 17 00:00:00 2001 From: MorganR Date: Wed, 30 Oct 2024 20:04:01 +0000 Subject: [PATCH 04/19] Passing Kotlin lib test with faked KtParser --- java/gazelle/generate.go | 41 +++++++++----- java/gazelle/lang.go | 21 ++++++++ .../java/javaparser/v0/javaparser.proto | 6 +-- java/gazelle/testdata/kotlin_lib/WORKSPACE | 2 +- .../src/main/java/com/example/hello/BUILD.out | 2 + .../javaparser/generators/BUILD.bazel | 3 ++ .../generators/ClasspathParser.java | 47 ---------------- .../javaparser/generators/GrpcServer.java | 25 ++++++++- .../javaparser/generators/JavaIdentifier.java | 12 +++-- .../javaparser/generators/KtParser.java | 18 +++++++ .../generators/ParsedPackageData.java | 21 ++++++++ .../javaparser/generators/PerClassData.java | 54 +++++++++++++++++++ .../generators/ClasspathParserTest.java | 22 ++++---- 13 files changed, 192 insertions(+), 82 deletions(-) create mode 100644 java/src/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/KtParser.java create mode 100644 java/src/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/ParsedPackageData.java create mode 100644 java/src/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/PerClassData.java diff --git a/java/gazelle/generate.go b/java/gazelle/generate.go index 98d83358..5f967d3a 100644 --- a/java/gazelle/generate.go +++ b/java/gazelle/generate.go @@ -55,25 +55,38 @@ 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") @@ -86,12 +99,12 @@ func (l javaLang) GenerateRules(args language.GenerateArgs) language.GenerateRes // (e.g. inner classes, annotation processor generated classes, etc). // But it will do for now. javaClassNamesFromFileNames := sorted_set.NewSortedSet([]string{}) - for _, filename := range javaFilenamesRelativeToPackage { + for _, filename := range srcFilenamesRelativeToPackage { javaClassNamesFromFileNames.Add(strings.TrimSuffix(filename, ".java")) } if isModule { - if len(javaFilenamesRelativeToPackage) > 0 { + if len(srcFilenamesRelativeToPackage) > 0 { l.javaPackageCache[args.Rel] = javaPkg } @@ -167,7 +180,7 @@ func (l javaLang) GenerateRules(args language.GenerateArgs) language.GenerateRes addNonLocalImportsAndExports(productionJavaImports, nonLocalJavaExports, javaPkg.ImportedClasses, javaPkg.ImportedPackagesWithoutSpecificClasses, javaPkg.ExportedClasses, javaPkg.Name, javaClassNamesFromFileNames) } 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 +208,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 +488,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/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/testdata/kotlin_lib/WORKSPACE b/java/gazelle/testdata/kotlin_lib/WORKSPACE index f5f6402b..fb636112 100644 --- a/java/gazelle/testdata/kotlin_lib/WORKSPACE +++ b/java/gazelle/testdata/kotlin_lib/WORKSPACE @@ -1,7 +1,7 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") rules_kotlin_version = "1.9.6" -rules_kotlin_sha = "3b772976fec7bdcda1d84b9d39b176589424c047eb2175bed09aac630e50af43", +rules_kotlin_sha = "3b772976fec7bdcda1d84b9d39b176589424c047eb2175bed09aac630e50af43" http_archive( name = "rules_kotlin", 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 index 81e09432..f5ac86d2 100644 --- 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 @@ -1,5 +1,7 @@ load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") +# gazelle:jvm_kotlin_enabled true + kt_jvm_library( name = "hello", srcs = ["Hello.kt"], 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..d8b29121 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", + "PerClassData.java", + "ParsedPackageData.java", "TimeoutHandler.java", ], visibility = ["//java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser:__subpackages__"], 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..bc995e7a 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 @@ -73,53 +73,6 @@ 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 ImmutableSet getUsedTypes() { return ImmutableSet.copyOf(usedTypes); } 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..6648e82e 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 @@ -120,8 +120,29 @@ 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. + if (files.stream().anyMatch(file -> file.endsWith(".kt"))) { + KtParser parser = new KtParser(); + ParsedPackageData data = parser.parseClasses(directory, files); + + Package.Builder packageBuilder = Package.newBuilder(); + if (data.packages.size() > 1) { + throw new StatusRuntimeException( + Status.INVALID_ARGUMENT.withDescription( + String.format( + "Expected exactly one java package, but saw %d: %s", + data.packages.size(), Joiner.on(", ").join(data.packages)))); + } else if (data.packages.isEmpty()) { + logger.info( + "Set of classes in {} has no package", Paths.get(request.getRel()).toAbsolutePath()); + data.packages.add(""); + } + packageBuilder.setName(Iterables.getOnlyElement(data.packages)); + return packageBuilder.build(); + } + + ClasspathParser parser = new ClasspathParser(); try { parser.parseClasses(directory, files); @@ -156,7 +177,7 @@ private Package getImports(ParsePackageRequest request) { .addAllImportedPackagesWithoutSpecificClasses( parser.getUsedPackagesWithoutSpecificTypes()) .addAllMains(parser.getMainClasses()); - for (Map.Entry classEntry : + for (Map.Entry classEntry : parser.perClassData.entrySet()) { PerClassMetadata.Builder perClassMetadata = PerClassMetadata.newBuilder() 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..e730b3b4 --- /dev/null +++ b/java/src/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/KtParser.java @@ -0,0 +1,18 @@ +package com.github.bazel_contrib.contrib_rules_jvm.javaparser.generators; + +import org.jetbrains.kotlin.parsing.KotlinParser; +import java.nio.file.Path; +import java.util.List; + +import org.jetbrains.kotlin.parsing.KotlinParser; + +public class KtParser { + private final KotlinParser parser = new KotlinParser(null); + + public ParsedPackageData parseClasses(Path directory, List files) { + ParsedPackageData result = new ParsedPackageData(); + result.packages.add("com.example"); + // TODO: Implement something more robust. + return result; + } +} \ 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..86ba5b7e --- /dev/null +++ b/java/src/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/ParsedPackageData.java @@ -0,0 +1,21 @@ +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 { + final Set packages = new TreeSet<>(); + final Set usedTypes = new TreeSet<>(); + final Set usedPackagesWithoutSpecificTypes = new TreeSet<>(); + + final Set exportedTypes = new TreeSet<>(); + 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<>(); + + ParsedPackageData() {} +} 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..76873441 --- /dev/null +++ b/java/src/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/PerClassData.java @@ -0,0 +1,54 @@ +package com.github.bazel_contrib.contrib_rules_jvm.javaparser.generators; + +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; + + @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/ClasspathParserTest.java b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/ClasspathParserTest.java index 10e2f606..5f9d15f7 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,7 +201,7 @@ 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); } @@ -217,7 +217,7 @@ 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); } @@ -239,18 +239,18 @@ public void testAnnotationOnField() throws IOException { TreeMap> expectedInnerEnumFieldAnnotations = new TreeMap<>(); expectedInnerEnumFieldAnnotations.put("size", treeSet("lombok.Getter")); - TreeMap expected = new TreeMap<>(); + TreeMap expected = new TreeMap<>(); expected.put( "workspace.com.gazelle.java.javaparser.generators.AnnotationOnField", - new ClasspathParser.PerClassData( + new 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); @@ -270,7 +270,7 @@ 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); } @@ -288,7 +288,7 @@ 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); } @@ -306,7 +306,7 @@ 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); } @@ -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,7 +350,7 @@ 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<>())); + new PerClassData(treeSet(), expectedPerMethodAnnotations, new TreeMap<>())); assertEquals(expectedPerClassMetadata, parser.perClassData); } From cf88e542f373784a48f21a4f8d602aca35de4d2b Mon Sep 17 00:00:00 2001 From: MorganR Date: Wed, 30 Oct 2024 20:14:30 +0000 Subject: [PATCH 05/19] Add kotlin_bin test --- java/gazelle/testdata/kotlin_bin/WORKSPACE | 16 ++++++++++++++++ .../testdata/kotlin_bin/maven_install.json | 1 + .../src/main/java/com/example/hello/BUILD.in | 1 + .../src/main/java/com/example/hello/BUILD.out | 18 ++++++++++++++++++ .../src/main/java/com/example/hello/Hello.kt | 7 +++++++ 5 files changed, 43 insertions(+) create mode 100644 java/gazelle/testdata/kotlin_bin/WORKSPACE create mode 100644 java/gazelle/testdata/kotlin_bin/maven_install.json create mode 100644 java/gazelle/testdata/kotlin_bin/src/main/java/com/example/hello/BUILD.in create mode 100644 java/gazelle/testdata/kotlin_bin/src/main/java/com/example/hello/BUILD.out create mode 100644 java/gazelle/testdata/kotlin_bin/src/main/java/com/example/hello/Hello.kt 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!") +} From 14855d139466a5cf777ae77e001e37c618becc17 Mon Sep 17 00:00:00 2001 From: MorganR Date: Wed, 30 Oct 2024 20:14:51 +0000 Subject: [PATCH 06/19] Add kotlin_lib_with_java test --- .../testdata/kotlin_lib_with_java/WORKSPACE | 16 ++++++++++++++++ .../kotlin_lib_with_java/maven_install.json | 1 + .../src/main/java/com/example/hello/BUILD.in | 1 + .../src/main/java/com/example/hello/BUILD.out | 12 ++++++++++++ .../src/main/java/com/example/hello/Goodbye.java | 7 +++++++ .../src/main/java/com/example/hello/Hello.kt | 7 +++++++ 6 files changed, 44 insertions(+) create mode 100644 java/gazelle/testdata/kotlin_lib_with_java/WORKSPACE create mode 100644 java/gazelle/testdata/kotlin_lib_with_java/maven_install.json create mode 100644 java/gazelle/testdata/kotlin_lib_with_java/src/main/java/com/example/hello/BUILD.in create mode 100644 java/gazelle/testdata/kotlin_lib_with_java/src/main/java/com/example/hello/BUILD.out create mode 100644 java/gazelle/testdata/kotlin_lib_with_java/src/main/java/com/example/hello/Goodbye.java create mode 100644 java/gazelle/testdata/kotlin_lib_with_java/src/main/java/com/example/hello/Hello.kt diff --git a/java/gazelle/testdata/kotlin_lib_with_java/WORKSPACE b/java/gazelle/testdata/kotlin_lib_with_java/WORKSPACE new file mode 100644 index 00000000..fb636112 --- /dev/null +++ b/java/gazelle/testdata/kotlin_lib_with_java/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/maven_install.json b/java/gazelle/testdata/kotlin_lib_with_java/maven_install.json new file mode 100644 index 00000000..6c01c298 --- /dev/null +++ b/java/gazelle/testdata/kotlin_lib_with_java/maven_install.json @@ -0,0 +1 @@ +{"version": "2"} diff --git a/java/gazelle/testdata/kotlin_lib_with_java/src/main/java/com/example/hello/BUILD.in b/java/gazelle/testdata/kotlin_lib_with_java/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/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/main/java/com/example/hello/BUILD.out b/java/gazelle/testdata/kotlin_lib_with_java/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/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/main/java/com/example/hello/Goodbye.java b/java/gazelle/testdata/kotlin_lib_with_java/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/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/main/java/com/example/hello/Hello.kt b/java/gazelle/testdata/kotlin_lib_with_java/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/main/java/com/example/hello/Hello.kt @@ -0,0 +1,7 @@ +package com.example.hello + +import kotlin.io.println + +fun sayHi() { + println("Hi!") +} From e00965b3e29b71cacf7276bac934c19eea69bb52 Mon Sep 17 00:00:00 2001 From: MorganR Date: Wed, 30 Oct 2024 20:15:52 +0000 Subject: [PATCH 07/19] Fix kotlin_lib compile --- .../testdata/kotlin_lib/src/main/java/com/example/hello/Hello.kt | 1 - 1 file changed, 1 deletion(-) 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 index 67be9f00..119d4742 100644 --- 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 @@ -2,7 +2,6 @@ package com.example.hello import kotlin.io.println -@JvmStatic fun sayHi() { println("Hi!") } From c487350691b477f08070cb23d9f10699adf44482 Mon Sep 17 00:00:00 2001 From: MorganR Date: Wed, 30 Oct 2024 23:11:57 +0000 Subject: [PATCH 08/19] Basic functional Kotlin parser --- MODULE.bazel | 3 + contrib_rules_jvm_deps_install.json | 1150 ++++++++++++++++- .../private/servermanager/servermanager.go | 4 +- .../javaparser/generators/BUILD.bazel | 3 +- .../javaparser/generators/KtParser.java | 47 +- 5 files changed, 1195 insertions(+), 12 deletions(-) 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/private/servermanager/servermanager.go b/java/gazelle/private/servermanager/servermanager.go index 342903ca..96cd5e60 100644 --- a/java/gazelle/private/servermanager/servermanager.go +++ b/java/gazelle/private/servermanager/servermanager.go @@ -63,8 +63,8 @@ func (m *ServerManager) Connect() (*grpc.ClientConn, error) { // 1. We don't want to pollute our own stdout // 2. Java does some output buffer sniffing where it will block its own progress until the // stdout buffer is read from, whereas stderr is unbuffered so doesn't hit this issue. - cmd.Stdout = os.Stderr - cmd.Stderr = os.Stderr + // cmd.Stdout = os.Stderr + // cmd.Stderr = os.Stderr if err := cmd.Start(); err != nil { return nil, fmt.Errorf("failed to start javaparser sever: %w", err) } 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 d8b29121..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 @@ -9,8 +9,8 @@ java_library( "KtParser.java", "LifecycleService.java", "Main.java", - "PerClassData.java", "ParsedPackageData.java", + "PerClassData.java", "TimeoutHandler.java", ], visibility = ["//java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser:__subpackages__"], @@ -27,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/KtParser.java b/java/src/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/KtParser.java index e730b3b4..6180299c 100644 --- 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 @@ -1,18 +1,51 @@ package com.github.bazel_contrib.contrib_rules_jvm.javaparser.generators; -import org.jetbrains.kotlin.parsing.KotlinParser; import java.nio.file.Path; import java.util.List; -import org.jetbrains.kotlin.parsing.KotlinParser; +import com.intellij.openapi.Disposable; +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.vfs.VirtualFileManager; +import com.intellij.openapi.vfs.VirtualFile; + +import com.intellij.psi.PsiManager; +import io.grpc.Metadata; +import io.grpc.StatusRuntimeException; +import io.grpc.Status; +import org.jetbrains.kotlin.config.CompilerConfiguration; +import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles; +import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment; +import org.jetbrains.kotlin.psi.KtFile; +import org.jetbrains.kotlin.psi.KtPackageDirective; +import org.jetbrains.kotlin.psi.KtTreeVisitorVoid; +import java.util.List; public class KtParser { - private final KotlinParser parser = new KotlinParser(null); + private final KotlinCoreEnvironment env = KotlinCoreEnvironment.createForProduction( + Disposer.newDisposable(), + CompilerConfiguration.EMPTY, + EnvironmentConfigFiles.JVM_CONFIG_FILES); + private final VirtualFileManager vfm = VirtualFileManager.getInstance(); + private final PsiManager psiManager = PsiManager.getInstance(env.getProject()); public ParsedPackageData parseClasses(Path directory, List files) { - ParsedPackageData result = new ParsedPackageData(); - result.packages.add("com.example"); - // TODO: Implement something more robust. - return result; + VirtualFile file = vfm.findFileByNioPath(directory.resolve(files.get(0))); + + KtFile ktFile = (KtFile) psiManager.findFile(file); + + KtFileVisitor visitor = new KtFileVisitor(); + ktFile.accept(visitor); + + return visitor.packageData; + } + + public static class KtFileVisitor extends KtTreeVisitorVoid { + final ParsedPackageData packageData = new ParsedPackageData(); + + @Override + public void visitPackageDirective(KtPackageDirective packageDirective) { + packageData.packages.add(packageDirective.getQualifiedName()); + super.visitPackageDirective(packageDirective); + } } } \ No newline at end of file From 86c13dce5f6478e0b507eb2ac2d658b2e2cca703 Mon Sep 17 00:00:00 2001 From: MorganR Date: Thu, 7 Nov 2024 23:15:07 +0000 Subject: [PATCH 09/19] Start writing KtParserTest with some examples --- .../javaparser/generators/GrpcServer.java | 6 +- .../javaparser/generators/KtParser.java | 126 +++++++++++++++++- .../javaparser/generators/BUILD.bazel | 8 ++ .../javaparser/generators/KtParserTest.java | 110 +++++++++++++++ .../kotlin/javaparser/generators/Hello.kt | 8 ++ .../kotlin/javaparser/generators/Main.kt | 3 + .../javaparser/generators/StaticImports.kt | 10 ++ .../kotlin/javaparser/generators/Wildcards.kt | 5 + 8 files changed, 272 insertions(+), 4 deletions(-) create mode 100644 java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/KtParserTest.java create mode 100644 java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/Hello.kt create mode 100644 java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/Main.kt create mode 100644 java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/StaticImports.kt create mode 100644 java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/Wildcards.kt 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 6648e82e..da260502 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 @@ -28,6 +28,7 @@ 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; @@ -122,9 +123,10 @@ private Package getImports(ParsePackageRequest request) { Path directory = workspace.resolve(request.getRel()); // TODO: Make this tidier. - if (files.stream().anyMatch(file -> file.endsWith(".kt"))) { + List kotlinFiles = files.stream().filter(file -> file.endsWith(".kt")).map(file -> directory.resolve(file)).collect(Collectors.toList()); + if (!kotlinFiles.isEmpty()) { KtParser parser = new KtParser(); - ParsedPackageData data = parser.parseClasses(directory, files); + ParsedPackageData data = parser.parseClasses(kotlinFiles); Package.Builder packageBuilder = Package.newBuilder(); if (data.packages.size() > 1) { 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 index 6180299c..c21f65ef 100644 --- 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 @@ -15,10 +15,21 @@ import org.jetbrains.kotlin.config.CompilerConfiguration; import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles; import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment; +import org.jetbrains.kotlin.name.FqName; +import org.jetbrains.kotlin.name.Name; +import org.jetbrains.kotlin.psi.KtClass; +import org.jetbrains.kotlin.psi.KtClassBody; +import org.jetbrains.kotlin.psi.KtClassOrObject; +import org.jetbrains.kotlin.psi.KtObjectDeclaration; import org.jetbrains.kotlin.psi.KtFile; +import org.jetbrains.kotlin.psi.KtImportDirective; +import org.jetbrains.kotlin.psi.KtNamedFunction; import org.jetbrains.kotlin.psi.KtPackageDirective; +import org.jetbrains.kotlin.psi.KtParameter; import org.jetbrains.kotlin.psi.KtTreeVisitorVoid; +import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; public class KtParser { private final KotlinCoreEnvironment env = KotlinCoreEnvironment.createForProduction( @@ -28,11 +39,22 @@ public class KtParser { private final VirtualFileManager vfm = VirtualFileManager.getInstance(); private final PsiManager psiManager = PsiManager.getInstance(env.getProject()); - public ParsedPackageData parseClasses(Path directory, List files) { - VirtualFile file = vfm.findFileByNioPath(directory.resolve(files.get(0))); + public ParsedPackageData parseClasses(List files) { + VirtualFile file = vfm.findFileByNioPath(files.get(0)); + if (file == null) { + throw new IllegalArgumentException("File not found: " + files.get(0)); + } KtFile ktFile = (KtFile) psiManager.findFile(file); + System.out.println("ktFile: " + ktFile); + System.out.println("name: " + ktFile.getName()); + System.out.println("script: " + ktFile.getScript()); + System.out.println("declarations: " + ktFile.getDeclarations()); + System.out.println("classes: " + Arrays.stream(ktFile.getClasses()).map(Object::toString).collect(Collectors.joining(", "))); + System.out.println("import directives: " + ktFile.getImportDirectives()); + System.out.println("import list: " + ktFile.getImportList()); + KtFileVisitor visitor = new KtFileVisitor(); ktFile.accept(visitor); @@ -47,5 +69,105 @@ public void visitPackageDirective(KtPackageDirective packageDirective) { packageData.packages.add(packageDirective.getQualifiedName()); super.visitPackageDirective(packageDirective); } + + @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; + packageData.usedTypes.add(pathSegments.subList(0, segmentCount) + .stream().map(Name::toString).collect(Collectors.joining("."))); + break; + } + } + if (!foundClass) { + if (importDirective.isAllUnder()) { + // If it's a wildcard import with no PascalCase component, assume it's a package. + packageData.usedPackagesWithoutSpecificTypes.add(importName.asString()); + } else { + // If it's not a wildcard import and lacks a PascalCase component, assume it's a function in a package. + packageData.usedPackagesWithoutSpecificTypes.add(importName.parent().asString()); + } + } + super.visitImportDirective(importDirective); + } + + @Override + public void visitClassOrObject(KtClassOrObject classOrObject) { + // TODO: Check for public visibility. + packageData.exportedTypes.add(classOrObject.getFqName().shortName().asString()); + + if (classOrObject instanceof KtObjectDeclaration && hasMainFunction((KtObjectDeclaration) classOrObject)) { + packageData.mainClasses.add(classOrObject.getFqName().shortName().asString()); + } + + super.visitClassOrObject(classOrObject); + } + + @Override + public void visitNamedFunction(KtNamedFunction function) { + if (function.isTopLevel()) { + String fileName = function.getContainingKtFile().getName(); + String outerClassName = fileName.substring(0, fileName.length() - 3) + "Kt"; + packageData.exportedTypes.add(outerClassName); + if (hasMainFunctionShape(function)) { + packageData.mainClasses.add(outerClassName); + } + } + super.visitNamedFunction(function); + } + + /** Returns true if this simple name is PascalCase. */ + private boolean isLikelyClassName(String name) { + if (name.isEmpty() || !Character.isUpperCase(name.charAt(0))) { + 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 hasMainFunction(KtObjectDeclaration object) { + // TODO: maybe check for @JvmStatic? + KtClassBody body = object.getBody(); + for (KtNamedFunction function : body.getFunctions()) { + if (hasMainFunctionShape(function)) { + return true; + } + } + return false; + } + + private boolean hasMainFunctionShape(KtNamedFunction function) { + // TODO: Use KtLexer to check for public modifier. + if (!function.getName().equals("main")) { + 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; + } } } \ No newline at end of file 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/KtParserTest.java b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/KtParserTest.java new file mode 100644 index 00000000..2c62072c --- /dev/null +++ b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/KtParserTest.java @@ -0,0 +1,110 @@ +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 mainTest() throws IOException { + ParsedPackageData data = parser.parseClasses(Files.walk(directory).filter(file -> file.getFileName().equals(Path.of("Main.kt"))).toList()); + + assertEquals(Set.of("workspace.com.gazelle.kotlin.javaparser.generators"), data.packages); + assertEquals(Set.of("MainKt"), data.mainClasses); + assertEquals(Set.of("MainKt"), 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("Hello"), data.exportedTypes); + assertEquals(Set.of(), data.mainClasses); + } + + @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/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/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.* From c1f1f136eb71db7bd7093e6ecc577d0ac17ef9b7 Mon Sep 17 00:00:00 2001 From: MorganR Date: Thu, 21 Nov 2024 18:34:18 +0000 Subject: [PATCH 10/19] Support parameterized types and basic JvmStatic main --- .../javaparser/generators/KtParser.java | 132 +++++++++++++++--- .../generators/ParsedPackageData.java | 11 +- .../javaparser/generators/KtParserTest.java | 33 ++++- .../javaparser/generators/ExportingClass.kt | 37 +++++ .../javaparser/generators/MainInClass.kt | 9 ++ .../javaparser/generators/MainOnCompanion.kt | 9 ++ 6 files changed, 208 insertions(+), 23 deletions(-) create mode 100644 java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/ExportingClass.kt create mode 100644 java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/MainInClass.kt create mode 100644 java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/MainOnCompanion.kt 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 index c21f65ef..87499ca4 100644 --- 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 @@ -2,6 +2,7 @@ import java.nio.file.Path; import java.util.List; +import java.util.Optional; import com.intellij.openapi.Disposable; import com.intellij.openapi.util.Disposer; @@ -16,7 +17,11 @@ import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles; import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment; import org.jetbrains.kotlin.name.FqName; +import org.jetbrains.kotlin.name.FqNamesUtilKt; import org.jetbrains.kotlin.name.Name; +import org.jetbrains.kotlin.name.NameUtils; +import org.jetbrains.kotlin.lexer.KtTokens; +import org.jetbrains.kotlin.psi.KtAnnotated; import org.jetbrains.kotlin.psi.KtClass; import org.jetbrains.kotlin.psi.KtClassBody; import org.jetbrains.kotlin.psi.KtClassOrObject; @@ -24,11 +29,17 @@ import org.jetbrains.kotlin.psi.KtFile; import org.jetbrains.kotlin.psi.KtImportDirective; import org.jetbrains.kotlin.psi.KtNamedFunction; +import org.jetbrains.kotlin.psi.KtNullableType; import org.jetbrains.kotlin.psi.KtPackageDirective; import org.jetbrains.kotlin.psi.KtParameter; 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 java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Optional; import java.util.stream.Collectors; public class KtParser { @@ -64,6 +75,8 @@ public ParsedPackageData parseClasses(List files) { public static class KtFileVisitor extends KtTreeVisitorVoid { final ParsedPackageData packageData = new ParsedPackageData(); + private HashMap fqImportByNameOrAlias = new HashMap<>(); + @Override public void visitPackageDirective(KtPackageDirective packageDirective) { packageData.packages.add(packageDirective.getQualifiedName()); @@ -81,12 +94,22 @@ public void visitImportDirective(KtImportDirective importDirective) { // If there is a PascalCase component, assume it's a class. if (isLikelyClassName(importPart.asString())) { foundClass = true; - packageData.usedTypes.add(pathSegments.subList(0, segmentCount) - .stream().map(Name::toString).collect(Collectors.joining("."))); break; } } - if (!foundClass) { + if (foundClass) { + FqName className = importName; + if (!isLikelyClassName(className.shortName().toString())) { + // 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 PascalCase component, assume it's a package. packageData.usedPackagesWithoutSpecificTypes.add(importName.asString()); @@ -99,30 +122,75 @@ public void visitImportDirective(KtImportDirective importDirective) { } @Override - public void visitClassOrObject(KtClassOrObject classOrObject) { - // TODO: Check for public visibility. - packageData.exportedTypes.add(classOrObject.getFqName().shortName().asString()); + public void visitClass(KtClass clazz) { + if (clazz.isLocal() || clazz.hasModifier(KtTokens.PRIVATE_KEYWORD)) { + super.visitClass(clazz); + return; + } + packageData.perClassData.put(clazz.getFqName().toString(), new PerClassData()); + super.visitClass(clazz); + } - if (classOrObject instanceof KtObjectDeclaration && hasMainFunction((KtObjectDeclaration) classOrObject)) { - packageData.mainClasses.add(classOrObject.getFqName().shortName().asString()); + @Override + public void visitObjectDeclaration(KtObjectDeclaration object) { + if (object.isLocal() || object.hasModifier(KtTokens.PRIVATE_KEYWORD)) { + super.visitObjectDeclaration(object); + return; } - super.visitClassOrObject(classOrObject); + packageData.perClassData.put(object.getFqName().toString(), new PerClassData()); + + Optional maybeMainFunction = retrievePossibleMainFunction(object); + maybeMainFunction.ifPresent( + mainFunction -> { + FqName relativeFqName = packageRelativeName(object.getFqName(), object.getContainingKtFile()); + if (isStatic(mainFunction)) { + packageData.mainClasses.add(relativeFqName.parent().toString()); + } else { + packageData.mainClasses.add(relativeFqName.asString()); + } + } + ); + super.visitObjectDeclaration(object); } + // TODO: Check for public top-level constants. + @Override public void visitNamedFunction(KtNamedFunction function) { + if (function.isLocal() || function.hasModifier(KtTokens.PRIVATE_KEYWORD)) { + super.visitNamedFunction(function); + return; + } + if (function.isTopLevel()) { - String fileName = function.getContainingKtFile().getName(); - String outerClassName = fileName.substring(0, fileName.length() - 3) + "Kt"; - packageData.exportedTypes.add(outerClassName); + FqName filePackage = function.getContainingKtFile().getPackageFqName(); + String outerClassName = NameUtils.getScriptNameForFile(function.getContainingKtFile().getName()) + "Kt"; + FqName outerClassFqName = filePackage.child(Name.identifier(outerClassName)); + packageData.perClassData.put(outerClassFqName.toString(), new PerClassData()); if (hasMainFunctionShape(function)) { packageData.mainClasses.add(outerClassName); } } + + KtTypeReference returnType = function.getTypeReference(); + if (returnType != null) { + KtTypeElement typeElement = getRootType(returnType); + Optional maybeQualifiedType = tryGetFullyQualifiedName(typeElement); + maybeQualifiedType.ifPresent( + qualifiedType -> { + // TODO: Check for java and Kotlin standard library types. + packageData.exportedTypes.add(qualifiedType); + } + ); + } super.visitNamedFunction(function); } + 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() || !Character.isUpperCase(name.charAt(0))) { @@ -140,19 +208,18 @@ private boolean isLikelyClassName(String name) { return false; } - private boolean hasMainFunction(KtObjectDeclaration object) { - // TODO: maybe check for @JvmStatic? + private Optional retrievePossibleMainFunction(KtObjectDeclaration object) { KtClassBody body = object.getBody(); for (KtNamedFunction function : body.getFunctions()) { if (hasMainFunctionShape(function)) { - return true; + return Optional.of(function); } } - return false; + return Optional.empty(); } private boolean hasMainFunctionShape(KtNamedFunction function) { - // TODO: Use KtLexer to check for public modifier. + // TODO: Use lexer.KtTokens to check for public modifier. if (!function.getName().equals("main")) { return false; } @@ -169,5 +236,36 @@ private boolean hasMainFunctionShape(KtNamedFunction function) { } return true; } + + private boolean isStatic(KtAnnotated annotatedThing) { + return annotatedThing.getAnnotationEntries().stream().anyMatch( + entry -> entry.getTypeReference().getTypeText().equals("JvmStatic")); + } + + 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 { + // TODO: Search the import declarations for this type. + return Optional.empty(); + } + } + } else { + return Optional.empty(); + } + } } } \ 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 index 86ba5b7e..3f375020 100644 --- 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 @@ -6,15 +6,22 @@ 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<>(); - // 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. + /** + * 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() {} 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 index 2c62072c..446d8caa 100644 --- 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 @@ -68,12 +68,36 @@ public void setupPerTest() { } @Test - public void mainTest() throws IOException { - ParsedPackageData data = parser.parseClasses(Files.walk(directory).filter(file -> file.getFileName().equals(Path.of("Main.kt"))).toList()); + 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("MainKt"), data.exportedTypes); + 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.InternalReturn", "example.external.ProtectedReturn", "example.external.PublicReturn", "example.external.ParameterizedReturn"), data.exportedTypes); } @Test @@ -83,8 +107,9 @@ public void helloTest() throws IOException { 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("Hello"), data.exportedTypes); + assertEquals(Set.of(), data.exportedTypes); assertEquals(Set.of(), data.mainClasses); + assertEquals(Set.of("workspace.com.gazelle.kotlin.javaparser.generators.Hello"), data.perClassData.keySet()); } @Test 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..3426ab33 --- /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,37 @@ +package workspace.com.gazelle.kotlin.javaparser.generators + +import example.external.ParameterizedReturn +import example.external.InternalReturn +import example.external.PrivateReturn +import example.external.ProtectedReturn +import example.external.PublicReturn + +class ExportingClass { + 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/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) {} + } +} + + From f095fa61a2166b9bf48a3f3b12740454d9b0c2a8 Mon Sep 17 00:00:00 2001 From: MorganR Date: Wed, 27 Nov 2024 18:04:47 +0000 Subject: [PATCH 11/19] Simplify main function parsing and add properties --- .../javaparser/generators/KtParser.java | 143 +++++++++++------- .../javaparser/generators/KtParserTest.java | 16 +- .../kotlin/javaparser/generators/Constant.kt | 3 + .../javaparser/generators/ExportingClass.kt | 5 + 4 files changed, 111 insertions(+), 56 deletions(-) create mode 100644 java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/Constant.kt 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 index 87499ca4..579f6ea7 100644 --- 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 @@ -1,46 +1,40 @@ 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.stream.Collectors; -import com.intellij.openapi.Disposable; -import com.intellij.openapi.util.Disposer; -import com.intellij.openapi.vfs.VirtualFileManager; -import com.intellij.openapi.vfs.VirtualFile; - -import com.intellij.psi.PsiManager; -import io.grpc.Metadata; -import io.grpc.StatusRuntimeException; -import io.grpc.Status; -import org.jetbrains.kotlin.config.CompilerConfiguration; 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.Name; import org.jetbrains.kotlin.name.NameUtils; -import org.jetbrains.kotlin.lexer.KtTokens; import org.jetbrains.kotlin.psi.KtAnnotated; import org.jetbrains.kotlin.psi.KtClass; import org.jetbrains.kotlin.psi.KtClassBody; -import org.jetbrains.kotlin.psi.KtClassOrObject; -import org.jetbrains.kotlin.psi.KtObjectDeclaration; import org.jetbrains.kotlin.psi.KtFile; import org.jetbrains.kotlin.psi.KtImportDirective; 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.KtTreeVisitorVoid; import org.jetbrains.kotlin.psi.KtTypeElement; import org.jetbrains.kotlin.psi.KtTypeReference; import org.jetbrains.kotlin.psi.KtUserType; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; + +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.VirtualFileManager; +import com.intellij.psi.PsiManager; public class KtParser { private final KotlinCoreEnvironment env = KotlinCoreEnvironment.createForProduction( @@ -83,6 +77,17 @@ public void visitPackageDirective(KtPackageDirective packageDirective) { 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(); @@ -99,7 +104,7 @@ public void visitImportDirective(KtImportDirective importDirective) { } if (foundClass) { FqName className = importName; - if (!isLikelyClassName(className.shortName().toString())) { + if (!isLikelyClassName(importName.shortName().asString())) { // If we're directly importing a function from a parent class or object, use the parent. className = importName.parent(); } @@ -111,10 +116,10 @@ public void visitImportDirective(KtImportDirective importDirective) { fqImportByNameOrAlias.put(localName, className); } else { if (importDirective.isAllUnder()) { - // If it's a wildcard import with no PascalCase component, assume it's a package. + // 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 a PascalCase component, assume it's a function in a package. + // 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()); } } @@ -124,7 +129,7 @@ public void visitImportDirective(KtImportDirective importDirective) { @Override public void visitClass(KtClass clazz) { if (clazz.isLocal() || clazz.hasModifier(KtTokens.PRIVATE_KEYWORD)) { - super.visitClass(clazz); + // Don't go further down this branch. return; } packageData.perClassData.put(clazz.getFqName().toString(), new PerClassData()); @@ -134,55 +139,57 @@ public void visitClass(KtClass clazz) { @Override public void visitObjectDeclaration(KtObjectDeclaration object) { if (object.isLocal() || object.hasModifier(KtTokens.PRIVATE_KEYWORD)) { - super.visitObjectDeclaration(object); + // Don't go further down this branch. return; } packageData.perClassData.put(object.getFqName().toString(), new PerClassData()); - Optional maybeMainFunction = retrievePossibleMainFunction(object); - maybeMainFunction.ifPresent( - mainFunction -> { - FqName relativeFqName = packageRelativeName(object.getFqName(), object.getContainingKtFile()); - if (isStatic(mainFunction)) { - packageData.mainClasses.add(relativeFqName.parent().toString()); - } else { - packageData.mainClasses.add(relativeFqName.asString()); - } - } - ); super.visitObjectDeclaration(object); } - // TODO: Check for public top-level constants. + @Override + public void visitProperty(KtProperty property) { + if (property.isLocal() || property.hasModifier(KtTokens.PRIVATE_KEYWORD)) { + // Don't go further down this branch. + return; + } + + KtTypeReference typeReference = property.getTypeReference(); + if (typeReference != null) { + addExportedTypeIfNeeded(typeReference); + } + + super.visitProperty(property); + } @Override public void visitNamedFunction(KtNamedFunction function) { if (function.isLocal() || function.hasModifier(KtTokens.PRIVATE_KEYWORD)) { - super.visitNamedFunction(function); + // Don't go further down this branch. return; } - if (function.isTopLevel()) { - FqName filePackage = function.getContainingKtFile().getPackageFqName(); - String outerClassName = NameUtils.getScriptNameForFile(function.getContainingKtFile().getName()) + "Kt"; - FqName outerClassFqName = filePackage.child(Name.identifier(outerClassName)); - packageData.perClassData.put(outerClassFqName.toString(), new PerClassData()); - if (hasMainFunctionShape(function)) { + 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) { - KtTypeElement typeElement = getRootType(returnType); - Optional maybeQualifiedType = tryGetFullyQualifiedName(typeElement); - maybeQualifiedType.ifPresent( - qualifiedType -> { - // TODO: Check for java and Kotlin standard library types. - packageData.exportedTypes.add(qualifiedType); - } - ); + addExportedTypeIfNeeded(returnType); } super.visitNamedFunction(function); } @@ -193,7 +200,7 @@ private FqName packageRelativeName(FqName name, KtFile file) { /** Returns true if this simple name is PascalCase. */ private boolean isLikelyClassName(String name) { - if (name.isEmpty() || !Character.isUpperCase(name.charAt(0))) { + if (name.isEmpty() || !firstLetterIsUppercase(name)) { return false; } // If the name is all uppercase, assume it's a constant. At worst, we'll still @@ -207,6 +214,16 @@ private boolean isLikelyClassName(String name) { } 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(); @@ -219,8 +236,8 @@ private Optional retrievePossibleMainFunction(KtObjectDeclarati } private boolean hasMainFunctionShape(KtNamedFunction function) { - // TODO: Use lexer.KtTokens to check for public modifier. - if (!function.getName().equals("main")) { + // 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(); @@ -237,11 +254,22 @@ private boolean hasMainFunctionShape(KtNamedFunction function) { return true; } - private boolean isStatic(KtAnnotated annotatedThing) { + 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) { @@ -259,7 +287,6 @@ private Optional tryGetFullyQualifiedName(KtTypeElement typeElement) { if (fqImportByNameOrAlias.containsKey(identifier)) { return Optional.of(fqImportByNameOrAlias.get(identifier).toString()); } else { - // TODO: Search the import declarations for this type. return Optional.empty(); } } @@ -267,5 +294,11 @@ private Optional tryGetFullyQualifiedName(KtTypeElement typeElement) { 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)); + } } } \ No newline at end of file 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 index 446d8caa..c299b56b 100644 --- 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 @@ -97,7 +97,14 @@ public void mainOnCompanion() throws IOException { public void exportingClassTest() throws IOException { ParsedPackageData data = parser.parseClasses(getPathsWithNames("ExportingClass.kt")); - assertEquals(Set.of("example.external.InternalReturn", "example.external.ProtectedReturn", "example.external.PublicReturn", "example.external.ParameterizedReturn"), data.exportedTypes); + assertEquals(Set.of( + "example.external.FinalProperty", + "example.external.VarProperty", + "example.external.InternalReturn", + "example.external.ProtectedReturn", + "example.external.PublicReturn", + "example.external.ParameterizedReturn" + ), data.exportedTypes); } @Test @@ -112,6 +119,13 @@ public void helloTest() throws IOException { 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 staticImportsTest() throws IOException { ParsedPackageData data = parser.parseClasses(getPathsWithNames("StaticImports.kt")); 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 index 3426ab33..33bbeef4 100644 --- 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 @@ -1,5 +1,7 @@ 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 @@ -7,6 +9,9 @@ import example.external.ProtectedReturn import example.external.PublicReturn class ExportingClass { + val finalProperty: FinalProperty? = null + val varProperty: VarProperty? = null + internal fun getInternal(): InternalReturn? { return null } From cabdd4897fab6de76514f6e4239173ff7a986764 Mon Sep 17 00:00:00 2001 From: MorganR Date: Sun, 1 Dec 2024 22:17:13 +0000 Subject: [PATCH 12/19] Fully functioning bin and lib This has a lot of in-progress stuff in the KtParser for trying figure out how to properly handle fully-qualified types all over Kotlin code --- java/gazelle/BUILD.bazel | 1 + java/gazelle/generate.go | 5 + java/gazelle/private/kotlin/BUILD.bazel | 23 ++ java/gazelle/private/kotlin/kotlin.go | 11 + java/gazelle/private/kotlin/kotlin_test.go | 26 +++ .../private/servermanager/servermanager.go | 4 +- java/gazelle/resolve.go | 4 + java/gazelle/testdata/BUILD.bazel | 1 + .../generators/ClasspathParser.java | 71 +++--- .../javaparser/generators/GrpcServer.java | 60 ++--- .../javaparser/generators/KtParser.java | 205 +++++++++++++++--- .../generators/ParsedPackageData.java | 16 ++ .../javaparser/generators/PerClassData.java | 21 ++ .../generators/ClasspathParserTest.java | 14 +- .../javaparser/generators/KtParserTest.java | 24 ++ .../javaparser/generators/FullyQualifieds.kt | 28 +++ .../generators/PrivateExportingClass.kt | 25 +++ 17 files changed, 436 insertions(+), 103 deletions(-) create mode 100644 java/gazelle/private/kotlin/BUILD.bazel create mode 100644 java/gazelle/private/kotlin/kotlin.go create mode 100644 java/gazelle/private/kotlin/kotlin_test.go create mode 100644 java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/FullyQualifieds.kt create mode 100644 java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/kotlin/javaparser/generators/PrivateExportingClass.kt 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/generate.go b/java/gazelle/generate.go index 5f967d3a..6c1dc1e5 100644 --- a/java/gazelle/generate.go +++ b/java/gazelle/generate.go @@ -92,6 +92,11 @@ func (l javaLang) GenerateRules(args language.GenerateArgs) language.GenerateRes 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. 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/private/servermanager/servermanager.go b/java/gazelle/private/servermanager/servermanager.go index 96cd5e60..342903ca 100644 --- a/java/gazelle/private/servermanager/servermanager.go +++ b/java/gazelle/private/servermanager/servermanager.go @@ -63,8 +63,8 @@ func (m *ServerManager) Connect() (*grpc.ClientConn, error) { // 1. We don't want to pollute our own stdout // 2. Java does some output buffer sniffing where it will block its own progress until the // stdout buffer is read from, whereas stderr is unbuffered so doesn't hit this issue. - // cmd.Stdout = os.Stderr - // cmd.Stderr = os.Stderr + cmd.Stdout = os.Stderr + cmd.Stderr = os.Stderr if err := cmd.Start(); err != nil { return nil, fmt.Errorf("failed to start javaparser sever: %w", err) } diff --git a/java/gazelle/resolve.go b/java/gazelle/resolve.go index 7d0e6c82..75748368 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" @@ -226,6 +227,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" { diff --git a/java/gazelle/testdata/BUILD.bazel b/java/gazelle/testdata/BUILD.bazel index 8b04da55..c9103cfc 100644 --- a/java/gazelle/testdata/BUILD.bazel +++ b/java/gazelle/testdata/BUILD.bazel @@ -42,6 +42,7 @@ SKIPPED_ON_WINDOWS = [ "GAZELLE_JAVA_LOG_CALLER": "false", "GAZELLE_JAVA_LOG_FORMAT": "json", "GAZELLE_JAVA_LOG_TIMESTAMP": "false", + # "GAZELLE_LANGUAGES_JAVA_LOG_LEVEL": "debug", }, gazelle_binary = ":gazelle", gazelle_timeout_seconds = 30, 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 bc995e7a..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,24 +64,28 @@ public ClasspathParser() { // Doesn't need to do anything currently } + 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 { @@ -167,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); } @@ -188,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); } @@ -234,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); } } @@ -246,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 @@ -352,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) { @@ -419,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); @@ -432,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 da260502..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,6 +24,7 @@ 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; @@ -124,36 +125,35 @@ private Package getImports(ParsePackageRequest request) { 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()) { - KtParser parser = new KtParser(); - ParsedPackageData data = parser.parseClasses(kotlinFiles); - - Package.Builder packageBuilder = Package.newBuilder(); - if (data.packages.size() > 1) { - throw new StatusRuntimeException( - Status.INVALID_ARGUMENT.withDescription( - String.format( - "Expected exactly one java package, but saw %d: %s", - data.packages.size(), Joiner.on(", ").join(data.packages)))); - } else if (data.packages.isEmpty()) { - logger.info( - "Set of classes in {} has no package", Paths.get(request.getRel()).toAbsolutePath()); - data.packages.add(""); + try { + KtParser parser = new KtParser(); + data.merge(parser.parseClasses(kotlinFiles)); + } catch (Exception ex) { + logger.error("Error parsing Kotlin files", ex); + throw ex; } - packageBuilder.setName(Iterables.getOnlyElement(data.packages)); - return packageBuilder.build(); } ClasspathParser parser = new ClasspathParser(); - 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(); + 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( @@ -166,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()); + data.usedPackagesWithoutSpecificTypes) + .addAllMains(data.mainClasses); for (Map.Entry classEntry : - parser.perClassData.entrySet()) { + 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/KtParser.java b/java/src/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/KtParser.java index 579f6ea7..d0d90a7b 100644 --- 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 @@ -5,70 +5,116 @@ 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.Name; 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(), - CompilerConfiguration.EMPTY, + compilerConf, EnvironmentConfigFiles.JVM_CONFIG_FILES); private final VirtualFileManager vfm = VirtualFileManager.getInstance(); private final PsiManager psiManager = PsiManager.getInstance(env.getProject()); public ParsedPackageData parseClasses(List files) { - VirtualFile file = vfm.findFileByNioPath(files.get(0)); - if (file == null) { - throw new IllegalArgumentException("File not found: " + files.get(0)); - } - - KtFile ktFile = (KtFile) psiManager.findFile(file); - - System.out.println("ktFile: " + ktFile); - System.out.println("name: " + ktFile.getName()); - System.out.println("script: " + ktFile.getScript()); - System.out.println("declarations: " + ktFile.getDeclarations()); - System.out.println("classes: " + Arrays.stream(ktFile.getClasses()).map(Object::toString).collect(Collectors.joining(", "))); - System.out.println("import directives: " + ktFile.getImportDirectives()); - System.out.println("import list: " + ktFile.getImportList()); - KtFileVisitor visitor = new KtFileVisitor(); - ktFile.accept(visitor); + 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 @@ -128,30 +174,38 @@ public void visitImportDirective(KtImportDirective importDirective) { @Override public void visitClass(KtClass clazz) { - if (clazz.isLocal() || clazz.hasModifier(KtTokens.PRIVATE_KEYWORD)) { - // Don't go further down this branch. + 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) { - if (object.isLocal() || object.hasModifier(KtTokens.PRIVATE_KEYWORD)) { - // Don't go further down this branch. + 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) { - if (property.isLocal() || property.hasModifier(KtTokens.PRIVATE_KEYWORD)) { - // Don't go further down this branch. + pushState(property); + if (property.isLocal() || !isVisible()) { + super.visitProperty(property); + popState(property); return; } @@ -161,12 +215,15 @@ public void visitProperty(KtProperty property) { } super.visitProperty(property); + popState(property); } @Override public void visitNamedFunction(KtNamedFunction function) { - if (function.isLocal() || function.hasModifier(KtTokens.PRIVATE_KEYWORD)) { - // Don't go further down this branch. + pushState(function); + if (function.isLocal() || !isVisible()) { + super.visitNamedFunction(function); + popState(function); return; } @@ -192,8 +249,65 @@ public void visitNamedFunction(KtNamedFunction function) { 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()); } @@ -254,6 +368,10 @@ private boolean hasMainFunctionShape(KtNamedFunction function) { 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")); @@ -300,5 +418,40 @@ private FqName javaClassNameForKtFile(KtFile file) { 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 index 3f375020..ab681243 100644 --- 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 @@ -25,4 +25,20 @@ class ParsedPackageData { 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 index 76873441..b3a6257b 100644 --- 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 @@ -1,5 +1,6 @@ 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; @@ -37,6 +38,26 @@ public String toString() { 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; 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 5f9d15f7..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 @@ -203,7 +203,7 @@ public void testAnnotationAfterImport() throws IOException { "workspace.com.gazelle.java.javaparser.generators.AnnotationAfterImport", new PerClassData( treeSet("com.example.FlakyTest"), new TreeMap<>(), new TreeMap<>())), - parser.perClassData); + parser.getParsedPackageData().perClassData); } @Test @@ -219,7 +219,7 @@ public void testAnnotationAfterImportOnNestedClass() throws IOException { "workspace.com.gazelle.java.javaparser.generators.NestedClassAnnotations.Inner", new PerClassData( treeSet("com.example.FlakyTest"), new TreeMap<>(), new TreeMap<>())), - parser.perClassData); + parser.getParsedPackageData().perClassData); } @Test @@ -253,7 +253,7 @@ public void testAnnotationOnField() throws IOException { new PerClassData( new TreeSet<>(), new TreeMap<>(), expectedInnerEnumFieldAnnotations)); - assertEquals(expected, parser.perClassData); + assertEquals(expected, parser.getParsedPackageData().perClassData); } @Test @@ -272,7 +272,7 @@ public void testAnnotationAfterImportOnMethod() throws IOException { "workspace.com.gazelle.java.javaparser.generators.AnnotationAfterImportOnMethod", new PerClassData( new TreeSet<>(), expectedPerMethodAnnotations, new TreeMap<>())), - parser.perClassData); + parser.getParsedPackageData().perClassData); } @Test @@ -290,7 +290,7 @@ public void testAnnotationFromJavaStandardLibrary() throws IOException { "workspace.com.gazelle.java.javaparser.generators.AnnotationFromJavaStandardLibrary", new PerClassData( treeSet("Deprecated"), new TreeMap<>(), new TreeMap<>())), - parser.perClassData); + parser.getParsedPackageData().perClassData); } @Test @@ -308,7 +308,7 @@ public void testAnnotationWithoutImport() throws IOException { "workspace.com.gazelle.java.javaparser.generators.AnnotationWithoutImport", new PerClassData( treeSet("WhoKnowsWhereIAmFrom"), new TreeMap<>(), new TreeMap<>())), - parser.perClassData); + parser.getParsedPackageData().perClassData); } @Test @@ -351,7 +351,7 @@ public void testAnonymousInnerClass() throws IOException { expectedPerClassMetadata.put( "workspace.com.gazelle.java.javaparser.generators.AnonymousInnerClass.", new PerClassData(treeSet(), expectedPerMethodAnnotations, new TreeMap<>())); - assertEquals(expectedPerClassMetadata, parser.perClassData); + 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 index c299b56b..a6ffab66 100644 --- 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 @@ -107,6 +107,13 @@ public void exportingClassTest() throws IOException { ), 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")); @@ -126,6 +133,23 @@ public void constantTest() throws IOException { 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")); 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/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 + } +} + From c26f54d0d9a7cdad86062aa3aa8589e6e7552571 Mon Sep 17 00:00:00 2001 From: MorganR Date: Fri, 13 Dec 2024 14:20:19 +0000 Subject: [PATCH 13/19] Freeze new Java deps --- java/private/contrib_rules_jvm_deps.zip | Bin 234450 -> 255231 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/java/private/contrib_rules_jvm_deps.zip b/java/private/contrib_rules_jvm_deps.zip index 060352001dc19d2059ea205962c2f1526aa4ace0..e5e6d8dcfdafe64c1264a06a88b7f1a2225e766c 100644 GIT binary patch delta 12403 zcmcIqU5p)79lvv@5Ox7;rR)}J%kEMNw7YlD%$d)v?RHy)wuH*!gNcw_&zv~}ciFwW zy?cv&F@?kgi661t=t&e!*BCTDh|&Owf`P^tjV9C=A8AuHga>4OFdo4Y|;cHh%nqxzi%Gl6_$0aE5Uv52FTOe(xg&NJ( z+SAMJ#YS_+|Cz$eODl~<(wb3fc1_hUd~?gTn}@&lm2c3kA66<`KfLhMt=7x?!nO5A zvrQHk8w+)ptgP1Ct>tHkwoq5LJ zq1z8cQfCw|LxXvIDq5e>dKI@CEA4vgbhF)9BC+3ZKWy6&64x~h({v1kux7Z5qZ7|_ zk%C-8Y+yjoae#(Rjj3%p8jBg*QgjPB3;||m{B7%4j^;ksseo(7Y3Y|~0Gx>Q$k-vh zs078{U|4Otiw!3<$CV$B9I~;CU6uwE26|%Y&;p3)s$rWZF)V1R238T$RF)WsYY>Cz z?7d|Z4hdV2!@$b3h+vqNTRco#U?$fBMHGa_xuG9u%rd~za+3wX`L#O^V@1QnFjbby zER*?Og_fr{07HNu1m0jplMjD$02aEr@}mEGk#2Oy%t4) zFG}!=X|H!qO-%9RceJ>zan*XvBsD zdJFzNj`0VcLXAYfI{@mT)DIx40Mw6`azp^aJqhr?a zOqK-{)mESjUCqI)JpdNcfubnDay3)akf}O`3ZY@BglMkjx;k)?t?R(CqRMz8V~jI_ zQO+T4;*#F0c~& zB5A3|BCm%u!{1HQ4lOnsN?k|I=5iag8_Uhrx&k}rc8<;>OjM)*7h6PiS&s=dTh$3h z7_g*jTb83@;90t2=m6WAV`1WHw!>`ZGIX)x$`Z=`vFHf~(+XsnH1S}HaSZZjA1jo- zHI^lZ=|>0T5&GVYJk&Y;=Is~mc>M)%HdLz?cCt0z$<73wENi~wPIh8)c#Qp-=%Z^d zTynP2oC>-g#*24Og-h6qbbk;`2&EQ^l$2O-wn(8=`C8Uil^=bw~frs`f(Qy zJpnhKGJ7ryqBU5yA$t_dc_vtTe8XBEbv@S4B)~Ys(HUBUZaXNCU3lwnwmkgw`{y6n zeXLU1eeBf_wp+VHh;<**=-Qpq^6m)znabtQFcj>UmUx|yX$5_L@Do5t(8`?&eAEQp zF&!V5PVI_BOOx~6&|9+Lo8R0dwYX#xJxSF$5Cpxu^ECD8@eeKW;hyY=-6|Mu-YTtd z_WLsIyM64Jy9Ih~hi}ZA!Zn>sfB$lnAKsXrklq~{{?~!O{PF5b)ymbEo*$Z&*#Ffx zh9>0?hL^%c-Mz`sFtmVLV+Tc=n2vPV+WW{ zrpph-m@6i_?SQn4jv4()vUa(82Yq{=Jor8dJy(^^_B&d7?lo!TEGw1um}^%vai&8PGwCnW@&(zg#FwcQM<$*qrkzMTJ?M8!Csa9K z-7ms29z%QY;ZbQ-h6C>U(D@%rkIW9XjpfH=N^}8J2+86rzYssB0B3&NID(cA>X+-vRF@b>D-$^V%QrMmNfVy zP5fOxto5O5*BwSnOF%(vDS-` z62)tfZ9=;CYW3uy!L~dpnwECc%ggeXl0x&MHIUFzz1q{;J1kV#wyvmDxt*hLPkLUL zkCmeqm8f!`N5yF%jd@+lRl(2Zbp4;z(c@7#Wgwp(g)${6{W300iIytfOU~diy*8Le zLvWjn+%*MRP^tmI5 z*vhBh`?z{?GV*;1s&PV{%!#4yvhcKeJX|p-#{3jZLULKL#SmVy+4B37ESCJ8MRA%W z8m(JXrHlUIJxLXvNp9lu$tF=fz*anc&SgExU}p+`Pf|`^j$AteMb=ruHoXkSks34V z>YQXRl8aEmNK68E_w=++ECyy?ehpgDM4h)oy8Y_t12Ed$>aHW}EZAv9UpVBwfAI+i z+cRUm!^9*#eo?BG!C^p_e(NIZp7;Ut^*CptmmZW(4t(k(M%TD^%@zqi5Phr6DD>O^ zu3Cw)HQg-v{w+2k-S%7QK{2-G^@IUZe739+n7vjV$*-b5P9L-oM|uZnu^FZm3jR}l>TE=(J8!sO5K85NI7S1G@c(2Rxm!E3jfwC;TQt+fF~RoMOT|P$Hf~xX2j)g+BnfY&i({hD0^NIW?;z z-kWxF_ckeC3Qwy&CEiO`A+Uldwg zq&U3(5BD$pFdOiKh$Bi(d}LTgt02@q7XiH})bbse^j{oueO{CilSm(d#2v#65-1(n zY(;uB>3?Yyg7eqeY{A4b6p^KKSQ?#TF%Ww-d6}i#?G`$BQ8L6Fu_GdrxpP3*{wyhC zOpc06AyrWa2E3-@&jE>N8qI1`(+Y<%_&s9%~DRwlnPWnO; zoqt8TOU$gfBqRUF^`XFibo~_xQ*evCg@Om<8~uAk?4H1lZ)`W^haXSe>!*zu5mzS5C*-FWn>%%1-bdetdR delta 323 zcmex=lK;|jz72tlT;2KJ9$z>Z7`||9j%GAwo_>ETqsHb(EHAjYD&PA|GP=dUV03Hq z6@H6l(-+t=vQCe;VU(QC-^0u{+2ow)<{QU54JP0DCB6CBFH2^w+0GK-4;>j89y)I3 z{3l`o*03F@;Tb1a>)Jcl{-iQ7{7G&1cVh%%rtSW2%&)k%D|9gDL?bz3`n1Q)0Zi#! zlh<>JPyhdzS&L~W*JMU+aqR$aMkWzv5e5iQWI(u%fdS}zIA~y;Fg^DPvvdf;H4F?~ yaA^jHZHzBoGB70P=N2TE=p|L Date: Thu, 2 Jan 2025 20:53:53 +0000 Subject: [PATCH 14/19] Add Kotlin support to resolution --- java/gazelle/generate.go | 19 +++++++++++++------ java/gazelle/resolve.go | 16 +++++++++++++++- java/gazelle/resolve_test.go | 27 +++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 7 deletions(-) diff --git a/java/gazelle/generate.go b/java/gazelle/generate.go index 6c1dc1e5..a88912f1 100644 --- a/java/gazelle/generate.go +++ b/java/gazelle/generate.go @@ -103,9 +103,16 @@ func (l javaLang) GenerateRules(args language.GenerateArgs) language.GenerateRes // 2. "What input files did you have" isn't a great heuristic for "What classes are generated" // (e.g. inner classes, annotation processor generated classes, etc). // But it will do for now. - javaClassNamesFromFileNames := sorted_set.NewSortedSet([]string{}) + likelyLocalClassNames := sorted_set.NewSortedSet([]string{}) for _, filename := range srcFilenamesRelativeToPackage { - javaClassNamesFromFileNames.Add(strings.TrimSuffix(filename, ".java")) + 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 { @@ -155,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{ @@ -180,9 +187,9 @@ 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 srcFilenamesRelativeToPackage { diff --git a/java/gazelle/resolve.go b/java/gazelle/resolve.go index 75748368..0894e98b 100644 --- a/java/gazelle/resolve.go +++ b/java/gazelle/resolve.go @@ -51,7 +51,13 @@ 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") + } + if cfg.KotlinEnabled() && !isJvmLibrary(r.Kind()) && r.Kind() != "java_test_suite" { + return nil + } else if !isJavaLibrary(r.Kind()) && r.Kind() != "java_test_suite" { return nil } @@ -299,10 +305,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..cca0a623 100644 --- a/java/gazelle/resolve_test.go +++ b/java/gazelle/resolve_test.go @@ -83,6 +83,33 @@ java_library( visibility = ["//:__subpackages__"], deps = ["@maven//:com_google_guava_guava"], ) +`, + }, + "kotlin": { + old: buildFile{ + rel: "", + content: `load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") + +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") + +kt_jvm_library( + name = "myproject", + srcs = ["App.kt"], + visibility = ["//:__subpackages__"], + deps = ["@maven//:com_google_guava_guava"], +) `, }, } { From 6f746b4b371bd15818eebecf72e0c5a6315abf43 Mon Sep 17 00:00:00 2001 From: Morgan Roff Date: Tue, 4 Feb 2025 09:43:20 +0000 Subject: [PATCH 15/19] Update resolve support for Kotlin --- java/gazelle/resolve.go | 9 ++- java/gazelle/resolve_test.go | 116 +++++++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+), 3 deletions(-) diff --git a/java/gazelle/resolve.go b/java/gazelle/resolve.go index 0894e98b..fd7d4325 100644 --- a/java/gazelle/resolve.go +++ b/java/gazelle/resolve.go @@ -55,9 +55,12 @@ func (jr *Resolver) Imports(c *config.Config, r *rule.Rule, f *rule.File) []reso if cfg == nil { jr.lang.logger.Fatal().Msg("failed retrieving package config") } - if cfg.KotlinEnabled() && !isJvmLibrary(r.Kind()) && r.Kind() != "java_test_suite" { - return nil - } else if !isJavaLibrary(r.Kind()) && r.Kind() != "java_test_suite" { + 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 } diff --git a/java/gazelle/resolve_test.go b/java/gazelle/resolve_test.go index cca0a623..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 @@ -90,6 +192,8 @@ java_library( rel: "", content: `load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") +# gazelle:jvm_kotlin_enabled true + kt_jvm_library( name = "myproject", srcs = ["App.kt"], @@ -104,6 +208,8 @@ kt_jvm_library( }, want: `load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") +# gazelle:jvm_kotlin_enabled true + kt_jvm_library( name = "myproject", srcs = ["App.kt"], @@ -204,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"), From d2461ed87169e5997e27a4f52483344a090c82aa Mon Sep 17 00:00:00 2001 From: Morgan Roff Date: Tue, 4 Feb 2025 15:33:15 +0000 Subject: [PATCH 16/19] Rename kotlin & java integration test --- java/gazelle/testdata/BUILD.bazel | 1 - .../{kotlin_lib_with_java => kotlin_lib_with_java_src}/WORKSPACE | 0 .../maven_install.json | 0 .../src/main/java/com/example/hello/BUILD.in | 0 .../src/main/java/com/example/hello/BUILD.out | 0 .../src/main/java/com/example/hello/Goodbye.java | 0 .../src/main/java/com/example/hello/Hello.kt | 0 7 files changed, 1 deletion(-) rename java/gazelle/testdata/{kotlin_lib_with_java => kotlin_lib_with_java_src}/WORKSPACE (100%) rename java/gazelle/testdata/{kotlin_lib_with_java => kotlin_lib_with_java_src}/maven_install.json (100%) rename java/gazelle/testdata/{kotlin_lib_with_java => kotlin_lib_with_java_src}/src/main/java/com/example/hello/BUILD.in (100%) rename java/gazelle/testdata/{kotlin_lib_with_java => kotlin_lib_with_java_src}/src/main/java/com/example/hello/BUILD.out (100%) rename java/gazelle/testdata/{kotlin_lib_with_java => kotlin_lib_with_java_src}/src/main/java/com/example/hello/Goodbye.java (100%) rename java/gazelle/testdata/{kotlin_lib_with_java => kotlin_lib_with_java_src}/src/main/java/com/example/hello/Hello.kt (100%) diff --git a/java/gazelle/testdata/BUILD.bazel b/java/gazelle/testdata/BUILD.bazel index c9103cfc..8b04da55 100644 --- a/java/gazelle/testdata/BUILD.bazel +++ b/java/gazelle/testdata/BUILD.bazel @@ -42,7 +42,6 @@ SKIPPED_ON_WINDOWS = [ "GAZELLE_JAVA_LOG_CALLER": "false", "GAZELLE_JAVA_LOG_FORMAT": "json", "GAZELLE_JAVA_LOG_TIMESTAMP": "false", - # "GAZELLE_LANGUAGES_JAVA_LOG_LEVEL": "debug", }, gazelle_binary = ":gazelle", gazelle_timeout_seconds = 30, diff --git a/java/gazelle/testdata/kotlin_lib_with_java/WORKSPACE b/java/gazelle/testdata/kotlin_lib_with_java_src/WORKSPACE similarity index 100% rename from java/gazelle/testdata/kotlin_lib_with_java/WORKSPACE rename to java/gazelle/testdata/kotlin_lib_with_java_src/WORKSPACE diff --git a/java/gazelle/testdata/kotlin_lib_with_java/maven_install.json b/java/gazelle/testdata/kotlin_lib_with_java_src/maven_install.json similarity index 100% rename from java/gazelle/testdata/kotlin_lib_with_java/maven_install.json rename to java/gazelle/testdata/kotlin_lib_with_java_src/maven_install.json diff --git a/java/gazelle/testdata/kotlin_lib_with_java/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 similarity index 100% rename from java/gazelle/testdata/kotlin_lib_with_java/src/main/java/com/example/hello/BUILD.in rename to java/gazelle/testdata/kotlin_lib_with_java_src/src/main/java/com/example/hello/BUILD.in diff --git a/java/gazelle/testdata/kotlin_lib_with_java/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 similarity index 100% rename from java/gazelle/testdata/kotlin_lib_with_java/src/main/java/com/example/hello/BUILD.out rename to java/gazelle/testdata/kotlin_lib_with_java_src/src/main/java/com/example/hello/BUILD.out diff --git a/java/gazelle/testdata/kotlin_lib_with_java/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 similarity index 100% rename from java/gazelle/testdata/kotlin_lib_with_java/src/main/java/com/example/hello/Goodbye.java rename to java/gazelle/testdata/kotlin_lib_with_java_src/src/main/java/com/example/hello/Goodbye.java diff --git a/java/gazelle/testdata/kotlin_lib_with_java/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 similarity index 100% rename from java/gazelle/testdata/kotlin_lib_with_java/src/main/java/com/example/hello/Hello.kt rename to java/gazelle/testdata/kotlin_lib_with_java_src/src/main/java/com/example/hello/Hello.kt From 17d87d83e3305505cd4380eb5c7e036ca545c3c7 Mon Sep 17 00:00:00 2001 From: Morgan Roff Date: Tue, 4 Feb 2025 15:33:23 +0000 Subject: [PATCH 17/19] Add kotlin with java dep test --- .../testdata/kotlin_lib_with_java_dep/WORKSPACE | 16 ++++++++++++++++ .../kotlin_lib_with_java_dep/maven_install.json | 1 + .../src/main/java/com/example/hello/BUILD.in | 1 + .../src/main/java/com/example/hello/BUILD.out | 14 ++++++++++++++ .../src/main/java/com/example/hello/Hello.kt | 8 ++++++++ .../main/java/com/example/hello/greeter/BUILD.in | 1 + .../java/com/example/hello/greeter/BUILD.out | 9 +++++++++ .../java/com/example/hello/greeter/Greeter.java | 7 +++++++ 8 files changed, 57 insertions(+) create mode 100644 java/gazelle/testdata/kotlin_lib_with_java_dep/WORKSPACE create mode 100644 java/gazelle/testdata/kotlin_lib_with_java_dep/maven_install.json create mode 100644 java/gazelle/testdata/kotlin_lib_with_java_dep/src/main/java/com/example/hello/BUILD.in create mode 100644 java/gazelle/testdata/kotlin_lib_with_java_dep/src/main/java/com/example/hello/BUILD.out create mode 100644 java/gazelle/testdata/kotlin_lib_with_java_dep/src/main/java/com/example/hello/Hello.kt create mode 100644 java/gazelle/testdata/kotlin_lib_with_java_dep/src/main/java/com/example/hello/greeter/BUILD.in create mode 100644 java/gazelle/testdata/kotlin_lib_with_java_dep/src/main/java/com/example/hello/greeter/BUILD.out create mode 100644 java/gazelle/testdata/kotlin_lib_with_java_dep/src/main/java/com/example/hello/greeter/Greeter.java 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..a900b750 --- /dev/null +++ b/java/gazelle/testdata/kotlin_lib_with_java_dep/src/main/java/com/example/hello/BUILD.out @@ -0,0 +1,14 @@ +load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") + +# gazelle:jvm_kotlin_enabled true + +kt_jvm_library( + name = "hello", + srcs = [ + "Hello.kt", + ], + deps = [ + "//src/main/java/com/example/hello/greeter", + ], + visibility = ["//:__subpackages__"], +) 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 From e0660ac4a94f1c2de84e5b9705d111fd978aa64e Mon Sep 17 00:00:00 2001 From: Morgan Roff Date: Tue, 4 Feb 2025 15:35:37 +0000 Subject: [PATCH 18/19] Fix kotlin_lib_with_java_dep test --- .../src/main/java/com/example/hello/BUILD.out | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) 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 index a900b750..d6b3f929 100644 --- 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 @@ -4,11 +4,7 @@ load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") kt_jvm_library( name = "hello", - srcs = [ - "Hello.kt", - ], - deps = [ - "//src/main/java/com/example/hello/greeter", - ], + srcs = ["Hello.kt"], visibility = ["//:__subpackages__"], + deps = ["//src/main/java/com/example/hello/greeter"], ) From 0955007ae00f8a498853eca87e958c7c72247686 Mon Sep 17 00:00:00 2001 From: Morgan Roff Date: Tue, 4 Feb 2025 15:38:46 +0000 Subject: [PATCH 19/19] Add java_lib_with_kotlin_dep test --- .../testdata/java_lib_with_kotlin_dep/WORKSPACE | 16 ++++++++++++++++ .../java_lib_with_kotlin_dep/maven_install.json | 1 + .../src/main/java/com/example/hello/BUILD.in | 1 + .../src/main/java/com/example/hello/BUILD.out | 10 ++++++++++ .../src/main/java/com/example/hello/Hello.java | 10 ++++++++++ .../main/java/com/example/hello/greeter/BUILD.in | 1 + .../java/com/example/hello/greeter/BUILD.out | 10 ++++++++++ .../java/com/example/hello/greeter/Greeter.kt | 7 +++++++ 8 files changed, 56 insertions(+) create mode 100644 java/gazelle/testdata/java_lib_with_kotlin_dep/WORKSPACE create mode 100644 java/gazelle/testdata/java_lib_with_kotlin_dep/maven_install.json create mode 100644 java/gazelle/testdata/java_lib_with_kotlin_dep/src/main/java/com/example/hello/BUILD.in create mode 100644 java/gazelle/testdata/java_lib_with_kotlin_dep/src/main/java/com/example/hello/BUILD.out create mode 100644 java/gazelle/testdata/java_lib_with_kotlin_dep/src/main/java/com/example/hello/Hello.java create mode 100644 java/gazelle/testdata/java_lib_with_kotlin_dep/src/main/java/com/example/hello/greeter/BUILD.in create mode 100644 java/gazelle/testdata/java_lib_with_kotlin_dep/src/main/java/com/example/hello/greeter/BUILD.out create mode 100644 java/gazelle/testdata/java_lib_with_kotlin_dep/src/main/java/com/example/hello/greeter/Greeter.kt 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