From 4a3f85d40a8fe7da84a332bd77382534b8228b46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 23 Aug 2018 17:36:14 +0200 Subject: [PATCH] Documentation --- .../lang/java/ast/ASTCatchStatementTest.kt | 2 +- .../pmd/lang/java/ast/KotlinNodeExtensions.kt | 2 + .../pmd/lang/java/ast/KotlinParsingUtils.kt | 49 ++++++++++++++++++- 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTCatchStatementTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTCatchStatementTest.kt index e412a3704b..5285c084bd 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTCatchStatementTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTCatchStatementTest.kt @@ -14,7 +14,7 @@ class ASTCatchStatementTest : FunSpec({ parserTest("Test crash on multicatch", javaVersions = Earliest..J1_6) { - expectParseException("Cannot catch multiple exceptions when running in JDK inferior to 1.7 mode!") { + expectParseException("Cannot catch multiple exceptions when running in JDK inferior to 1.7 mode") { parseAstStatement("try { } catch (IOException | AssertionError e) { }") } diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/KotlinNodeExtensions.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/KotlinNodeExtensions.kt index 41f6196100..81acac6913 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/KotlinNodeExtensions.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/KotlinNodeExtensions.kt @@ -4,6 +4,8 @@ import net.sourceforge.pmd.lang.ast.Node /** Extension methods to make the Node API more Kotlin-like */ +// kotlin converts getters of java types into property accessors +// but it doesn't recognise jjtGet* methods as getters val Node.numChildren: Int get() = this.jjtGetNumChildren() diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/KotlinParsingUtils.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/KotlinParsingUtils.kt index 1038bbdc20..be9c43e075 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/KotlinParsingUtils.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/KotlinParsingUtils.kt @@ -81,16 +81,59 @@ fun AbstractFunSpec.parserTest(name: String, } +/** + * Extensible environment to describe parse/match testing workflows in a concise way. + * Can be used inside of a [io.kotlintest.specs.FunSpec] with [parserTest]. + * + * Parsing contexts allow to parse a string containing only the node you're interested + * in instead of writing up a full class that the parser can handle. See [parseAstExpression], + * [parseAstStatement]. + * + * The methods [parseExpression] and [parseStatement] add some sugar to those by skipping + * some nodes we're not interested in to find the node of interest using their reified type + * parameter. + * + * These are implicitly used by [matchExpr] and [matchStmt], which specify a matcher directly + * on the strings, using their type parameter and the info in this test context to parse, find + * the node, and execute the matcher in a single call. These may be used by [io.kotlintest.should], + * e.g. + * + * parserTest("Test ShiftExpression operator") { + * "1 >> 2" should matchExpr(ignoreChildren = true) { + * it.operator shouldBe ">>" + * } + * } + * + * + * Import statements in the parsing contexts can be configured by adding types to [importedTypes], + * or strings to [otherImports]. + * + * Technically the utilities provided by this class may be used outside of [io.kotlintest.specs.FunSpec]s, + * e.g. in regular JUnit tests, but I think we should strive to uniformize our testing style, + * especially since KotlinTest defines so many. + * + * TODO allow to reference an existing type as the parsing context, for full type resolution + * + * @property javaVersion The java version that will be used for parsing. + * @property importedTypes Types to import at the beginning of parsing contexts + * @property otherImports Other imports, without the `import` and semicolon + */ data class ParsingTestCtx(val javaVersion: JavaVersion = JavaVersion.Latest, val importedTypes: MutableList> = mutableListOf(), val otherImports: MutableList = mutableListOf()) { + /** Imports to add to the top of the parsing contexts. */ private val imports: List get() { val types = importedTypes.mapNotNull { it.canonicalName }.map { "import $it;" } return types + otherImports.map { "import $it;" } } + /** + * Returns a String matcher that parses the node using [parseExpression] with + * type param [N], then matches it against the [nodeSpec] using [matchNode]. + * + */ inline fun matchExpr(ignoreChildren: Boolean = false, noinline nodeSpec: NWrapper.() -> Unit): Matcher = object : Matcher { @@ -99,6 +142,10 @@ data class ParsingTestCtx(val javaVersion: JavaVersion = JavaVersion.Latest, } + /** + * Returns a String matcher that parses the node using [parseStatement] with + * type param [N], then matches it against the [nodeSpec] using [matchNode]. + */ inline fun matchStmt(ignoreChildren: Boolean = false, noinline nodeSpec: NWrapper.() -> Unit) = object : Matcher { @@ -143,7 +190,7 @@ data class ParsingTestCtx(val javaVersion: JavaVersion = JavaVersion.Latest, Object o = $expr; } } - """.trimIndent() + """.trimIndent() val acu = ParserTstUtil.parseAndTypeResolveJava(javaVersion.pmdName, source)