Skip to content

Commit

Permalink
feat: DependencyExtractor includes all statements in DependencyContai…
Browse files Browse the repository at this point in the history
…ner.
  • Loading branch information
autonomousapps committed Nov 5, 2024
1 parent 426aabf commit b18fcb1
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cash.grammar.kotlindsl.model.gradle

import cash.grammar.kotlindsl.model.DependencyDeclaration
import com.squareup.cash.grammar.KotlinParser.StatementContext

/**
* A container for all the [Statements][com.squareup.cash.grammar.KotlinParser.StatementsContext] in
Expand All @@ -20,10 +21,38 @@ public class DependencyContainer(
return statements.filterIsInstance<DependencyDeclaration>()
}

@Deprecated(
message = "use getExpressions",
replaceWith = ReplaceWith("getExpressions()")
)
public fun getNonDeclarations(): List<String> {
return getExpressions()
}

/**
* A common example of an expression in a dependencies block is
* ```
* add("extraImplementation", "com.foo:bar:1.0")
* ```
*/
public fun getExpressions(): List<String> {
return statements.filterIsInstance<String>()
}

/**
* Might include an [if-expression][com.squareup.cash.grammar.KotlinParser.IfExpressionContext] like
* ```
* if (functionReturningABoolean()) { ... }
* ```
* or a [property declaration][com.squareup.cash.grammar.KotlinParser.PropertyDeclarationContext] like
* ```
* val string = "a:complex:$value"
* ```
*/
public fun getStatements(): List<StatementContext> {
return statements.filterIsInstance<StatementContext>()
}

internal companion object {
val EMPTY = DependencyContainer(emptyList())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,18 @@ public class DependencyExtractor(
}

val statements = ctx.statements().statement()
if (statements == null || statements.isEmpty()) return DependencyContainer.EMPTY
if (statements.isNullOrEmpty()) return DependencyContainer.EMPTY

return statements
.mapNotNull { it.leafRule() as? PostfixUnaryExpressionContext }
.map { parseDependencyDeclaration(it) }
.map { stmt ->
val leaf = stmt.leafRule()
if (leaf is PostfixUnaryExpressionContext) {
parseDependencyDeclaration(leaf)
} else {
// If it's not a known type, we just grab the raw KotlinParser.StatementContext
stmt
}
}
.asContainer()
}

Expand Down Expand Up @@ -222,7 +229,7 @@ public class DependencyExtractor(
* ```
*/
private fun parseFunctionCall(statement: PostfixUnaryExpressionContext): Any {
// TODO(tsr): anything more interesting?
// TODO(tsr): we should consider modeling function calls separately, since it's a well-known use-case.
return statement.fullText(input)!!
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import cash.grammar.kotlindsl.model.DependencyDeclaration.Type
import cash.grammar.kotlindsl.parse.Parser
import cash.grammar.kotlindsl.utils.test.TestErrorListener
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.MethodSource

Expand All @@ -23,16 +24,65 @@ internal class DependencyExtractorTest {
""".trimIndent()

// When
val scriptListener = Parser(
val scriptListener = listenerFor(buildScript)

// Then
assertThat(scriptListener.dependencyDeclarations).containsExactly(testCase.toDependencyDeclaration())
}

@Test fun `a complex script can be fully parsed`() {
// Given
val buildScript = """
dependencies {
api(libs.magic)
add("extraImplementation", libs.fortyTwo)
val complex = "a:complex:${'$'}expression"
if (org.apache.tools.ant.taskdefs.condition.Os.isArch("aarch64")) {
// Multi-line comment about why we're
// doing this.
testImplementation("io.github.ganadist.sqlite4java:libsqlite4java-osx-aarch64:1.0.392")
}
}
""".trimIndent()

// When
val scriptListener = listenerFor(buildScript)

// Then
assertThat(scriptListener.dependencyDeclarations).containsExactly(
DependencyDeclaration(
configuration = "api",
identifier = "libs.magic".asSimpleIdentifier()!!,
capability = DependencyDeclaration.Capability.DEFAULT,
type = DependencyDeclaration.Type.MODULE,
fullText = "api(libs.magic)",
)
)
assertThat(scriptListener.expressions).containsExactly("add(\"extraImplementation\", libs.fortyTwo)")
assertThat(scriptListener.statements).containsExactly(
"val complex = \"a:complex:${'$'}expression\"",
// The whitespace below is a bit wonky, but it's an artifact of the test fixture, not the API.
"""
if (org.apache.tools.ant.taskdefs.condition.Os.isArch("aarch64")) {
// Multi-line comment about why we're
// doing this.
testImplementation("io.github.ganadist.sqlite4java:libsqlite4java-osx-aarch64:1.0.392")
}
""".trimIndent()
)
}

private fun listenerFor(buildScript: String): TestListener {
return Parser(
file = buildScript,
errorListener = TestErrorListener {
throw RuntimeException("Syntax error: ${it?.message}", it)
},
listenerFactory = { input, tokens, _ -> TestListener(input, tokens) }
).listener()

// Then
assertThat(scriptListener.dependencyDeclarations).containsExactly(testCase.toDependencyDeclaration())
}

private companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import cash.grammar.kotlindsl.model.DependencyDeclaration
import cash.grammar.kotlindsl.utils.Blocks.isBuildscript
import cash.grammar.kotlindsl.utils.Blocks.isDependencies
import cash.grammar.kotlindsl.utils.Blocks.isSubprojects
import cash.grammar.kotlindsl.utils.Context.fullText
import com.squareup.cash.grammar.KotlinParser
import com.squareup.cash.grammar.KotlinParserBaseListener
import org.antlr.v4.runtime.CharStream
Expand All @@ -24,6 +25,8 @@ internal class TestListener(
val dependencyExtractor = DependencyExtractor(input, tokens, indent)

val dependencyDeclarations = mutableListOf<DependencyDeclaration>()
val expressions = mutableListOf<String>()
val statements = mutableListOf<String>()

override fun exitNamedBlock(ctx: KotlinParser.NamedBlockContext) {
if (ctx.isSubprojects) {
Expand All @@ -33,8 +36,11 @@ internal class TestListener(
whitespace = Whitespace.getWhitespaceToLeft(tokens, ctx)
}
if (ctx.isDependencies) {
dependencyDeclarations += dependencyExtractor.collectDependencies(ctx)
.getDependencyDeclarations()
val dependencyContainer = dependencyExtractor.collectDependencies(ctx)

dependencyDeclarations += dependencyContainer.getDependencyDeclarations()
expressions += dependencyContainer.getExpressions()
statements += dependencyContainer.getStatements().map { it.fullText(input)!!.trim() }
}
}
}

0 comments on commit b18fcb1

Please sign in to comment.