Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Kotlin parser failure to handle value classes #536

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions gazelle/kotlin/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ func (p *treeSitterParser) Parse(filePath, source string) (*ParseResult, []error
} else if nodeI.Type() == "package_header" {
if result.Package != "" {
fmt.Printf("Multiple package declarations found in %s\n", filePath)
// TODO: Don't exit here and return an error instead?
os.Exit(1)
}

Expand Down Expand Up @@ -131,13 +132,17 @@ func readIdentifier(node *sitter.Node, sourceCode []byte, ignoreLast bool) strin

// TODO: are there any other node types under an "identifier"

if nodeC.Type() == "simple_identifier" {
switch nodeC.Type() {
case "simple_identifier":
if s.Len() > 0 {
s.WriteString(".")
}
s.WriteString(nodeC.Content(sourceCode))
} else if nodeC.Type() != "comment" {
case "multiline_comment", "comment":
// ignore
default:
fmt.Printf("Unexpected node type '%v' within: %s", nodeC.Type(), node.Content(sourceCode))
// TODO: Return error instead of exiting.
os.Exit(1)
}
}
Expand Down
1 change: 1 addition & 0 deletions gazelle/kotlin/tests/gcsutil/BUILD.in
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# gazelle:resolve kotlin com.google.cloud.storage.contrib.nio @maven//:com_google_cloud_google_cloud_nio
9 changes: 9 additions & 0 deletions gazelle/kotlin/tests/gcsutil/BUILD.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
load("@io_bazel_rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library")

# gazelle:resolve kotlin com.google.cloud.storage.contrib.nio @maven//:com_google_cloud_google_cloud_nio

kt_jvm_library(
name = "simple_file",
srcs = ["gcsutil.kt"],
deps = ["@maven//:com_google_cloud_google_cloud_nio"],
)
2 changes: 2 additions & 0 deletions gazelle/kotlin/tests/gcsutil/WORKSPACE
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# This is a Bazel workspace for the Gazelle test data.
workspace(name = "simple_file")
132 changes: 132 additions & 0 deletions gazelle/kotlin/tests/gcsutil/gcsutil.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package gazelle.kotlin.tests.gcsutil

import com.google.cloud.storage.contrib.nio.CloudStorageFileSystem
import com.google.cloud.storage.contrib.nio.CloudStoragePath
import java.net.URI

private fun quote(value: String): String = "\"$value\""

/**
* Checks that the uri is formatted like "gs://bucket/file/path.txt", the form accepted by
* gsutil, and returns a [CloudStorageUri].
*
* @throws IllegalArgumentException if the scheme is not a valid GCS path.
* @return a wrapper around [URI] that ensures the URI passed
*/
fun URI.toCloudStorageUri(): CloudStorageUri = CloudStorageUri(this)

/**
* Returns true if the URI is a valid GCS URI as accepted by [CloudStorageUri].
*/
fun URI.isCloudStorageUri(): Boolean {
return try {
this.validateGoogleCloudStorageUri()
true
} catch (e: java.lang.IllegalArgumentException) {
false
}
}

private fun URI.validateGoogleCloudStorageUri() {
if (this.scheme != "gs") {
throw IllegalArgumentException(
"URI doesn't start with gs://, got scheme ${this.scheme}",
)
}
val fragment = this.rawFragment ?: ""
if (fragment.isNotEmpty()) {
throw IllegalArgumentException(
"GCS uris must not have a fragment, got ${quote(fragment)}",
)
}
val query = this.rawQuery ?: ""
if (query.isNotEmpty()) {
throw IllegalArgumentException(
"GCS uris must not have a query part, got ${quote(query)}",
)
}
val path = this.rawPath
if (path.contains("//")) {
throw IllegalArgumentException(
"GCS URIs should not contain two adjacent slashes: ${quote(this)}",
)
}
}

/**
* A class for keeping a validated (per [validateGoogleCloudStorageUri]) URI.
*
* @throws IllegalArgumentException if the passed uri isn't a GCS URI.
*/
// TODO(reddaly): Add more checks to ensure the bucket characters comply with GCS requirements.
@JvmInline
value class CloudStorageUri(val uri: URI) {
constructor(gsUri: String) : this(URI.create(gsUri))

init {
uri.validateGoogleCloudStorageUri()
}

/**
* Returns a [CloudStoragePath] for this "gs://"-style URI.
*/
val path: CloudStoragePath get() = CloudStorageFileSystemSet.DEFAULT.pathFromGsUri(this)

/**
* Returns the GCS path without a leading gs://. The result is formatted like
* <bucket name>/<path to file or directory>
*/
val pathStringWithBucketName: String get() = this.uri.toString().removePrefix("gs://")

/**
* Returns the GCS bucket name for this path.
*/
val bucket: String get() = this.uri.authority

/**
* The path of the file within the bucket. For "gs://bucket-x/foo/bar", returns "foo/bar".
* For "gs://bucket-x", returns "".
*/
val bucketRelativePath: String get() = this.uri.path.removePrefix("/")

/**
* Returns a string like "gs://foo/bar".
*/
override fun toString(): String = this.uri.toString()

/**
* Resolves a GCS path using [URI.resolve] on the URI version of this object.
*
* CloudStorageUri("gs://foo/bar", "baz") returns
* CloudStorageUri("gs://foo/bar/baz").
*/
fun child(relativePath: String): CloudStorageUri =
CloudStorageUri(URI("$this/$relativePath"))
}

// TODO: Workaround for https://github.com/googleapis/java-storage-nio/issues/1153 -
private class CloudStorageFileSystemSet {
companion object {
/**
* A singleton instance of CloudStorageFileSystemSet.
*/
val DEFAULT = CloudStorageFileSystemSet()
}

private val fileSystemByBucketName: MutableMap<String, CloudStorageFileSystem> =
mutableMapOf()

/**
* Returns the value of a "gs://bucket/file/path.txt"-style GCP location as a [Path].
*/
fun pathFromGsUri(uri: CloudStorageUri): CloudStoragePath {
val bucket = uri.bucket
val relativePath = uri.bucketRelativePath
val fs = synchronized(this.fileSystemByBucketName) {
this.fileSystemByBucketName.getOrPut(bucket) {
CloudStorageFileSystem.forBucket(bucket)
}
}
return fs.getPath(relativePath)
}
}
1 change: 1 addition & 0 deletions gazelle/kotlin/tests/simple_file2/BUILD.in
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# gazelle:resolve kotlin com.google.cloud.storage.contrib.nio @maven//:com_google_cloud_google_cloud_nio
9 changes: 9 additions & 0 deletions gazelle/kotlin/tests/simple_file2/BUILD.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
load("@io_bazel_rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library")

# gazelle:resolve kotlin com.google.cloud.storage.contrib.nio @maven//:com_google_cloud_google_cloud_nio

kt_jvm_library(
name = "simple_file",
srcs = ["lib.kt"],
deps = ["@maven//:com_google_cloud_google_cloud_nio"],
)
2 changes: 2 additions & 0 deletions gazelle/kotlin/tests/simple_file2/WORKSPACE
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# This is a Bazel workspace for the Gazelle test data.
workspace(name = "simple_file")
8 changes: 8 additions & 0 deletions gazelle/kotlin/tests/simple_file2/lib.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package xyz.example.gcsutil

import com.google.cloud.storage.contrib.nio.CloudStoragePath

fun hello(): CloudStoragePath? {
println("Hello world!")
return null
}
Loading