From 6abb4bf236bdd2be2389b87e530a7a29de2cccd7 Mon Sep 17 00:00:00 2001 From: Jason Bedard Date: Wed, 28 Jun 2023 11:47:08 -0700 Subject: [PATCH] fix(gazelle): support deps between local projects (#2696) GitOrigin-RevId: 7c2ab434235c42a4bd32bdd0aba3ecdf2bd0b87a --- gazelle/kotlin/kotlin.go | 2 +- gazelle/kotlin/parser/parser.go | 25 +++++++--- gazelle/kotlin/parser/parser_test.go | 49 ++++++++++++------- gazelle/kotlin/resolver.go | 29 +++++++---- gazelle/kotlin/tests/local_deps/BUILD.out | 4 ++ .../local_deps/{sub2 => impt-star}/BUILD.in | 0 .../tests/local_deps/impt-star/BUILD.out | 7 +++ .../kotlin/tests/local_deps/impt-star/S.kt | 7 +++ gazelle/kotlin/tests/local_deps/impt/B.kt | 7 +++ gazelle/kotlin/tests/local_deps/impt/BUILD.in | 0 .../tests/local_deps/{sub2 => impt}/BUILD.out | 3 +- gazelle/kotlin/tests/local_deps/main.kt | 6 +-- gazelle/kotlin/tests/local_deps/sub1/A.kt | 4 +- gazelle/kotlin/tests/local_deps/sub2/B.kt | 1 - gazelle/kotlin/tests/unknown_imports/BUILD.in | 0 .../kotlin/tests/unknown_imports/BUILD.out | 6 +++ .../kotlin/tests/unknown_imports/WORKSPACE | 2 + .../tests/unknown_imports/expectedStdout.txt | 4 ++ gazelle/kotlin/tests/unknown_imports/main.kt | 5 ++ 19 files changed, 119 insertions(+), 42 deletions(-) rename gazelle/kotlin/tests/local_deps/{sub2 => impt-star}/BUILD.in (100%) create mode 100644 gazelle/kotlin/tests/local_deps/impt-star/BUILD.out create mode 100644 gazelle/kotlin/tests/local_deps/impt-star/S.kt create mode 100644 gazelle/kotlin/tests/local_deps/impt/B.kt create mode 100644 gazelle/kotlin/tests/local_deps/impt/BUILD.in rename gazelle/kotlin/tests/local_deps/{sub2 => impt}/BUILD.out (71%) delete mode 100644 gazelle/kotlin/tests/local_deps/sub2/B.kt create mode 100644 gazelle/kotlin/tests/unknown_imports/BUILD.in create mode 100644 gazelle/kotlin/tests/unknown_imports/BUILD.out create mode 100644 gazelle/kotlin/tests/unknown_imports/WORKSPACE create mode 100644 gazelle/kotlin/tests/unknown_imports/expectedStdout.txt create mode 100644 gazelle/kotlin/tests/unknown_imports/main.kt diff --git a/gazelle/kotlin/kotlin.go b/gazelle/kotlin/kotlin.go index 281b892b3..20dece5f4 100644 --- a/gazelle/kotlin/kotlin.go +++ b/gazelle/kotlin/kotlin.go @@ -3,5 +3,5 @@ package gazelle import "strings" func IsNativeImport(impt string) bool { - return strings.HasPrefix(impt, "kotlin.") || strings.HasPrefix(impt, "java.") || strings.HasPrefix(impt, "javax.") + return strings.HasPrefix(impt, "kotlin.") || strings.HasPrefix(impt, "kotlinx.") || strings.HasPrefix(impt, "java.") || strings.HasPrefix(impt, "javax.") } diff --git a/gazelle/kotlin/parser/parser.go b/gazelle/kotlin/parser/parser.go index ca9a0a136..e3ca08ca7 100644 --- a/gazelle/kotlin/parser/parser.go +++ b/gazelle/kotlin/parser/parser.go @@ -63,7 +63,7 @@ func (p *treeSitterParser) ParsePackage(filePath, source string) (string, []erro os.Exit(1) } - pkg = readIdentifier(getLoneChild(nodeI, "identifier"), sourceCode) + pkg = readIdentifier(getLoneChild(nodeI, "identifier"), sourceCode, false) } } @@ -104,10 +104,18 @@ func (p *treeSitterParser) ParseImports(filePath, source string) ([]string, []er for j := 0; j < int(nodeI.NamedChildCount()); j++ { nodeJ := nodeI.NamedChild(j) if nodeJ.Type() == "import_header" { - for k := 0; k < int(nodeJ.NamedChildCount()); k++ { - nodeK := nodeJ.NamedChild(k) + for k := 0; k < int(nodeJ.ChildCount()); k++ { + nodeK := nodeJ.Child(k) if nodeK.Type() == "identifier" { - imports.Add(readIdentifier(nodeK, sourceCode)) + isStar := false + for l := k + 1; l < int(nodeJ.ChildCount()); l++ { + if nodeJ.Child(l).Type() == ".*" { + isStar = true + break + } + } + + imports.Add(readIdentifier(nodeK, sourceCode, !isStar)) } } } @@ -140,7 +148,7 @@ func getLoneChild(node *sitter.Node, name string) *sitter.Node { return nil } -func readIdentifier(node *sitter.Node, sourceCode []byte) string { +func readIdentifier(node *sitter.Node, sourceCode []byte, ignoreLast bool) string { if node.Type() != "identifier" { fmt.Printf("Must be type 'identifier': %v - %s", node.Type(), node.Content(sourceCode)) os.Exit(1) @@ -148,7 +156,12 @@ func readIdentifier(node *sitter.Node, sourceCode []byte) string { var s strings.Builder - for c := 0; c < int(node.NamedChildCount()); c++ { + total := int(node.NamedChildCount()) + if ignoreLast { + total = total - 1 + } + + for c := 0; c < total; c++ { nodeC := node.NamedChild(c) // TODO: are there any other node types under an "identifier" diff --git a/gazelle/kotlin/parser/parser_test.go b/gazelle/kotlin/parser/parser_test.go index 922197d75..cb7971698 100644 --- a/gazelle/kotlin/parser/parser_test.go +++ b/gazelle/kotlin/parser/parser_test.go @@ -19,34 +19,39 @@ var testCases = []struct { imports: []string{}, }, { - desc: "simple", - kt: "import a.b", + desc: "simple", + kt: ` +import a.B +import c.D as E + `, filename: "simple.kt", pkg: "", - imports: []string{"a.b"}, + imports: []string{"a", "c"}, + }, + { + desc: "stars", + kt: `package a.b.c + +import d.y.* + `, + filename: "stars.kt", + pkg: "a.b.c", + imports: []string{"d.y"}, }, { desc: "comments", kt: ` -/*dlfkj*/ package /*dlfkj*/ x // x +/*dlfkj*/package /*dlfkj*/ x // x //z -import a.b // y +import a.B // y //z -/* asdf */ import /* asdf */ c./* asdf */d // w - `, - filename: "empty.kt", +/* asdf */ import /* asdf */ c./* asdf */D // w +import /* fdsa */ d/* asdf */.* // w + `, + filename: "comments.kt", pkg: "x", - imports: []string{"a.b", "c.d"}, - }, - { - desc: "stars", - kt: `package a.b.c - - import d.y.*`, - filename: "stars.kt", - pkg: "a.b.c", - imports: []string{"d.y"}, + imports: []string{"a", "c", "d"}, }, } @@ -57,7 +62,13 @@ func TestTreesitterParser(t *testing.T) { actualImports, _ := NewParser().ParseImports(tc.filename, tc.kt) if !equal(actualImports, tc.imports) { - t.Errorf("Inequality.\nactual: %#v;\nimports: %#v\nkotlin code:\n%v", actualImports, tc.imports, tc.kt) + t.Errorf("Imports...\nactual: %#v;\nexpected: %#v\nkotlin code:\n%v", actualImports, tc.imports, tc.kt) + } + + actualPackage, _ := NewParser().ParsePackage(tc.filename, tc.kt) + + if actualPackage != tc.pkg { + t.Errorf("Package....\nactual: %#v;\nexpected: %#v\nkotlin code:\n%v", actualPackage, tc.pkg, tc.kt) } }) } diff --git a/gazelle/kotlin/resolver.go b/gazelle/kotlin/resolver.go index 771c6019a..12db7a504 100644 --- a/gazelle/kotlin/resolver.go +++ b/gazelle/kotlin/resolver.go @@ -26,11 +26,11 @@ type Resolver struct { } const ( - Resolution_Error = -1 - Resolution_None = 0 - Resolution_NotFound = 1 - Resolution_Label = 2 - Resolution_NativeNode = 3 + Resolution_Error = -1 + Resolution_None = 0 + Resolution_NotFound = 1 + Resolution_Label = 2 + Resolution_NativeKotlin = 3 ) type ResolutionType = int @@ -83,7 +83,7 @@ func (kt *Resolver) Resolve(c *config.Config, ix *resolve.RuleIndex, rc *repo.Re start := time.Now() BazelLog.Infof("Resolve '%s' dependencies", from.String()) - if r.Kind() != KtJvmLibrary { + if r.Kind() == KtJvmLibrary { deps, err := kt.resolveImports(c, ix, importData.(*KotlinImports).imports, from) if err != nil { log.Fatal("Resolution Error: ", err) @@ -115,12 +115,21 @@ func (kt *Resolver) resolveImports( return nil, err } - if resolutionType == Resolution_NativeNode || resolutionType == Resolution_None { + if resolutionType == Resolution_NotFound { + BazelLog.Debugf("import '%s' for target '%s' not found", mod.Imp, from.String()) + + notFound := fmt.Errorf( + "Import %[1]q from %[2]q is an unknown dependency. Possible solutions:\n"+ + "\t1. Instruct Gazelle to resolve to a known dependency using a directive:\n"+ + "\t\t# gazelle:resolve [src-lang] kotlin import-string label\n", + mod.Imp, mod.SourcePath, + ) + + fmt.Printf("Resolution error %v\n", notFound) continue } - if resolutionType == Resolution_NotFound { - // TODO: collect errors + if resolutionType == Resolution_NativeKotlin || resolutionType == Resolution_None { continue } @@ -176,7 +185,7 @@ func (kt *Resolver) resolveImport( // Native kotlin imports if IsNativeImport(impt.Imp) { - return Resolution_NativeNode, nil, nil + return Resolution_NativeKotlin, nil, nil } // TODO: maven imports diff --git a/gazelle/kotlin/tests/local_deps/BUILD.out b/gazelle/kotlin/tests/local_deps/BUILD.out index e309d78af..2771a1b97 100644 --- a/gazelle/kotlin/tests/local_deps/BUILD.out +++ b/gazelle/kotlin/tests/local_deps/BUILD.out @@ -3,4 +3,8 @@ load("@io_bazel_rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") kt_jvm_library( name = "local_deps", srcs = ["main.kt"], + deps = [ + "//impt", + "//impt-star", + ], ) diff --git a/gazelle/kotlin/tests/local_deps/sub2/BUILD.in b/gazelle/kotlin/tests/local_deps/impt-star/BUILD.in similarity index 100% rename from gazelle/kotlin/tests/local_deps/sub2/BUILD.in rename to gazelle/kotlin/tests/local_deps/impt-star/BUILD.in diff --git a/gazelle/kotlin/tests/local_deps/impt-star/BUILD.out b/gazelle/kotlin/tests/local_deps/impt-star/BUILD.out new file mode 100644 index 000000000..eeb5be2ba --- /dev/null +++ b/gazelle/kotlin/tests/local_deps/impt-star/BUILD.out @@ -0,0 +1,7 @@ +load("@io_bazel_rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") + +kt_jvm_library( + name = "impt-star", + srcs = ["S.kt"], + deps = ["//sub1"], +) diff --git a/gazelle/kotlin/tests/local_deps/impt-star/S.kt b/gazelle/kotlin/tests/local_deps/impt-star/S.kt new file mode 100644 index 000000000..3d17ffffa --- /dev/null +++ b/gazelle/kotlin/tests/local_deps/impt-star/S.kt @@ -0,0 +1,7 @@ +package test.imptstar + +import test.a.* + +class Rectangle2(var height: Double, var length: Double): Shape() { + var perimeter = (height + length) * 2 +} \ No newline at end of file diff --git a/gazelle/kotlin/tests/local_deps/impt/B.kt b/gazelle/kotlin/tests/local_deps/impt/B.kt new file mode 100644 index 000000000..f1ad4a5e7 --- /dev/null +++ b/gazelle/kotlin/tests/local_deps/impt/B.kt @@ -0,0 +1,7 @@ +package test.impt + +import test.a.Shape + +class Rectangle(var height: Double, var length: Double): Shape() { + var perimeter = (height + length) * 2 +} \ No newline at end of file diff --git a/gazelle/kotlin/tests/local_deps/impt/BUILD.in b/gazelle/kotlin/tests/local_deps/impt/BUILD.in new file mode 100644 index 000000000..e69de29bb diff --git a/gazelle/kotlin/tests/local_deps/sub2/BUILD.out b/gazelle/kotlin/tests/local_deps/impt/BUILD.out similarity index 71% rename from gazelle/kotlin/tests/local_deps/sub2/BUILD.out rename to gazelle/kotlin/tests/local_deps/impt/BUILD.out index a5840913b..0c56fb6f0 100644 --- a/gazelle/kotlin/tests/local_deps/sub2/BUILD.out +++ b/gazelle/kotlin/tests/local_deps/impt/BUILD.out @@ -1,6 +1,7 @@ load("@io_bazel_rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") kt_jvm_library( - name = "sub2", + name = "impt", srcs = ["B.kt"], + deps = ["//sub1"], ) diff --git a/gazelle/kotlin/tests/local_deps/main.kt b/gazelle/kotlin/tests/local_deps/main.kt index f120f1332..31d282317 100644 --- a/gazelle/kotlin/tests/local_deps/main.kt +++ b/gazelle/kotlin/tests/local_deps/main.kt @@ -1,10 +1,10 @@ package test.root // Basic -import test.A -import test.B +import test.impt.Rectangle +import test.imptstar.* fun main() { A.f() - B.f() + Rectangle.f() } \ No newline at end of file diff --git a/gazelle/kotlin/tests/local_deps/sub1/A.kt b/gazelle/kotlin/tests/local_deps/sub1/A.kt index a2450f2a8..c227b881e 100644 --- a/gazelle/kotlin/tests/local_deps/sub1/A.kt +++ b/gazelle/kotlin/tests/local_deps/sub1/A.kt @@ -1 +1,3 @@ -package test.a \ No newline at end of file +package test.a + +open class Shape diff --git a/gazelle/kotlin/tests/local_deps/sub2/B.kt b/gazelle/kotlin/tests/local_deps/sub2/B.kt deleted file mode 100644 index b2df088d5..000000000 --- a/gazelle/kotlin/tests/local_deps/sub2/B.kt +++ /dev/null @@ -1 +0,0 @@ -package test.b \ No newline at end of file diff --git a/gazelle/kotlin/tests/unknown_imports/BUILD.in b/gazelle/kotlin/tests/unknown_imports/BUILD.in new file mode 100644 index 000000000..e69de29bb diff --git a/gazelle/kotlin/tests/unknown_imports/BUILD.out b/gazelle/kotlin/tests/unknown_imports/BUILD.out new file mode 100644 index 000000000..2d2af8416 --- /dev/null +++ b/gazelle/kotlin/tests/unknown_imports/BUILD.out @@ -0,0 +1,6 @@ +load("@io_bazel_rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") + +kt_jvm_library( + name = "simple_file", + srcs = ["main.kt"], +) diff --git a/gazelle/kotlin/tests/unknown_imports/WORKSPACE b/gazelle/kotlin/tests/unknown_imports/WORKSPACE new file mode 100644 index 000000000..105b24c73 --- /dev/null +++ b/gazelle/kotlin/tests/unknown_imports/WORKSPACE @@ -0,0 +1,2 @@ +# This is a Bazel workspace for the Gazelle test data. +workspace(name = "simple_file") diff --git a/gazelle/kotlin/tests/unknown_imports/expectedStdout.txt b/gazelle/kotlin/tests/unknown_imports/expectedStdout.txt new file mode 100644 index 000000000..7d8ea5bea --- /dev/null +++ b/gazelle/kotlin/tests/unknown_imports/expectedStdout.txt @@ -0,0 +1,4 @@ +Resolution error Import "foo" from "main.kt" is an unknown dependency. Possible solutions: + 1. Instruct Gazelle to resolve to a known dependency using a directive: + # gazelle:resolve [src-lang] kotlin import-string label + diff --git a/gazelle/kotlin/tests/unknown_imports/main.kt b/gazelle/kotlin/tests/unknown_imports/main.kt new file mode 100644 index 000000000..d8e503190 --- /dev/null +++ b/gazelle/kotlin/tests/unknown_imports/main.kt @@ -0,0 +1,5 @@ +import foo.bar + +fun main() { + println("Hello world!") +} \ No newline at end of file