Skip to content
Open
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
27 changes: 23 additions & 4 deletions ast-psi/src/test/kotlin/ktast/ast/psi/Corpus.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import java.io.File
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
import kotlin.io.path.pathString
import kotlin.streams.toList

object Corpus {
Expand All @@ -13,7 +14,11 @@ object Corpus {

private val fileExtensions = Regex("\\.kts?$")

val default: List<Unit> by lazy { localTestData + kotlinRepoTestData + stringTestData }
private val kotlinRepoPath: String by lazy {
System.getenv("KOTLIN_REPO") ?: error("No KOTLIN_REPO env var")
}

val default: List<Unit> by lazy { localTestData + kotlinRepoTestData + kotlinRepoScripts + stringTestData }

private val localTestData by lazy {
loadTestDataFromDir(File(javaClass.getResource("/localTestData").toURI()).toPath(), canSkip = false)
Expand All @@ -22,13 +27,23 @@ object Corpus {
private val kotlinRepoTestData by lazy {
// Recursive from $KOTLIN_REPO/compiler/testData/psi/**/*.kt and *.kts
val dir = Paths.get(
System.getenv("KOTLIN_REPO") ?: error("No KOTLIN_REPO env var"),
kotlinRepoPath,
"compiler/testData/psi"
).also { require(Files.isDirectory(it)) { "Dir not found at $it" } }

loadTestDataFromDir(dir, canSkip = true)
}

// all gradle kts scripts used in the Kotlin repo
private val kotlinRepoScripts by lazy {
val dir = Paths.get(kotlinRepoPath)

loadTestDataFromDir(dir, canSkip = true) {
it.fileName.toString().endsWith("gradle.kts") &&
!it.toString().contains("testProject") // test project sripts use placeholders and not valid
}
}

private val stringTestData by lazy {
listOf(
Unit.FromString("identifier including underscore", "const val c = FOO_BAR"),
Expand Down Expand Up @@ -158,8 +173,12 @@ object Corpus {
)
}

private fun loadTestDataFromDir(root: Path, canSkip: Boolean): List<Unit.FromFile> = Files.walk(root)
.filter { fileExtensions.containsMatchIn(it.fileName.toString()) }
private fun loadTestDataFromDir(
root: Path,
canSkip: Boolean,
pathFilter: (Path) -> Boolean = { fileExtensions.containsMatchIn(it.fileName.toString()) }
): List<Unit.FromFile> = Files.walk(root)
.filter(pathFilter)
.toList<Path>()
.map { ktPath ->
val relativePath = root.relativize(ktPath)
Expand Down
4 changes: 4 additions & 0 deletions ast-psi/src/test/resources/localTestData/script.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@file:Suppress("DEPRECATION")

import com.google.gson.Gson
import gradlebuild.basics.capitalize
import java.net.URI
Expand All @@ -6,6 +8,8 @@ wrapperUpdateTask("nightly", "nightly")
wrapperUpdateTask("rc", "release-candidate")
wrapperUpdateTask("current", "current")

val someVar = "someValue"

tasks.withType<Wrapper>().configureEach {
val jvmOpts = "-Dfile.encoding=UTF-8"
inputs.property("jvmOpts", jvmOpts)
Expand Down
5 changes: 5 additions & 0 deletions ast/src/commonMain/kotlin/ktast/ast/Node.kt
Original file line number Diff line number Diff line change
Expand Up @@ -1662,6 +1662,11 @@ sealed interface Node {
override val text = "when"
}

data class All(override val supplement: NodeSupplement = NodeSupplement()) : Keyword,
Modifier.AnnotationSet.AnnotationTarget {
override val text = "all"
}

data class Field(override val supplement: NodeSupplement = NodeSupplement()) : Keyword,
Modifier.AnnotationSet.AnnotationTarget {
override val text = "field"
Expand Down
20 changes: 18 additions & 2 deletions ast/src/commonMain/kotlin/ktast/ast/Writer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -524,7 +524,7 @@ open class Writer(
children(type)
commaSeparatedChildren(lPar, arguments, rPar)
val parentNode = path.parent?.node
if (parentNode is Node.Modifier.AnnotationSet && parentNode.rBracket == null) {
if (parentNode is Node.Modifier.AnnotationSet && parentNode.rBracket == null && parentNode.target !is Node.Keyword.File) {
nextHeuristicWhitespace = " " // Insert heuristic space after annotation if single form
}
}
Expand Down Expand Up @@ -685,7 +685,7 @@ open class Writer(
append(";")
}
}
if (node is Node.Declaration.PropertyDeclaration && next is Node.Declaration.ScriptInitializer) {
if (node is Node.Declaration.PropertyDeclaration && next is Node.Declaration.ScriptInitializer && next.startsWithGetterSetterLikeObject()) {
// if it's method that looks like a getter or setter and comes after a property declaration, we need to properly separate them
// see kotlin/compiler/testData/psi/script/topLevelPropertiesWithGetSet.kts
if (!containsSemicolon(extrasSinceLastNonSymbol)) {
Expand All @@ -699,6 +699,22 @@ open class Writer(
}
}

protected fun Node.Declaration.ScriptInitializer.startsWithGetterSetterLikeObject(): Boolean {
fun checkLeftMostCall(node: Node): Boolean = when (node) {
is Node.Expression.BinaryExpression -> checkLeftMostCall(node.lhs)
is Node.Expression.CallExpression -> {
if (node.calleeExpression is Node.Expression.NameExpression) {
node.calleeExpression.text == "get" || node.calleeExpression.text == "set"
} else {
checkLeftMostCall(node.calleeExpression)
}
}

else -> false
}
return checkLeftMostCall(this.body)
}

protected fun containsNewlineOrSemicolon(extras: List<Node.Extra>): Boolean {
return containsNewline(extras) || containsSemicolon(extras)
}
Expand Down