UAST: utilize identifier test base

This commit is contained in:
Jinseong Jeon
2021-05-17 00:15:31 -07:00
committed by Ilya Kirillov
parent da15f0ffe8
commit c37123603c
22 changed files with 122 additions and 103 deletions
@@ -11,8 +11,16 @@ dependencies {
// BEWARE: UAST should not depend on IJ platform so that it can work in Android Lint CLI mode (where IDE is not available)
compileOnly(intellijCoreDep()) { includeJars("intellij-core", "asm-all", rootProject = rootProject) }
compileOnly(intellijPluginDep("java")) { includeJars("java-api", "java-impl") }
testImplementation(commonDep("junit:junit"))
testCompileOnly(intellijPluginDep("java")) { includeJars("java-api", "java-impl") }
testImplementation(projectTests(":compiler:tests-common"))
testCompileOnly(intellijDep("ideaIC"))
}
sourceSets {
"main" { projectDefault() }
"test" { projectDefault() }
}
testsJar ()
@@ -0,0 +1,43 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.uast.test.common.kotlin
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiElementVisitor
import org.jetbrains.uast.UFile
import kotlin.reflect.KClass
abstract class IndentedPrintingVisitor(val shouldIndent: (PsiElement) -> Boolean) : PsiElementVisitor() {
constructor(vararg kClasses: KClass<*>) : this({ psi -> kClasses.any { it.isInstance(psi) } })
private val builder = StringBuilder()
var level = 0
private set
override fun visitElement(element: PsiElement) {
val charSequence = render(element)
if (charSequence != null) {
builder.append(" ".repeat(level))
builder.append(charSequence)
builder.appendLine()
}
val shouldIndent = shouldIndent(element)
if (shouldIndent) level++
element.acceptChildren(this)
if (shouldIndent) level--
}
protected abstract fun render(element: PsiElement): CharSequence?
val result: String
get() = builder.toString()
fun visitUFileAndGetResult(uFile: UFile): String {
uFile.sourcePsi.accept(this)
return result
}
}
@@ -0,0 +1,78 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.uast.test.common.kotlin
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.psi.KtBlockExpression
import org.jetbrains.uast.*
import org.jetbrains.uast.visitor.AbstractUastVisitor
import org.jetbrains.uast.test.common.UElementToParentMap
import org.jetbrains.uast.test.common.visitUFileAndGetResult
import org.junit.ComparisonFailure
import kotlin.test.assertNotNull
import kotlin.test.fail
fun UFile.asIdentifiersWithParents() = object : IndentedPrintingVisitor(KtBlockExpression::class) {
override fun render(element: PsiElement): CharSequence? = element.toUElementOfType<UIdentifier>()?.let { uIdentifier ->
StringBuilder().apply {
append(uIdentifier.sourcePsiElement!!.text)
append(" -> ")
append(uIdentifier.uastParent?.asLogString())
}
}
}.visitUFileAndGetResult(this)
fun UFile.testIdentifiersParents() {
accept(object : AbstractUastVisitor() {
override fun visitElement(node: UElement): Boolean {
val uIdentifier = when (node) {
is UAnchorOwner -> node.uastAnchor ?: return false
is UBinaryExpression -> node.operatorIdentifier ?: return false
else -> return false
}
assertParents(node, uIdentifier)
val identifierSourcePsi = uIdentifier.sourcePsi ?: return false
val operatorIdentifierFromSource = identifierSourcePsi.toUElementOfType<UIdentifier>()
?: fail("$identifierSourcePsi of ${identifierSourcePsi.javaClass} should be convertible to UIdentifier")
assertParents(node, operatorIdentifierFromSource)
return false
}
})
}
private fun Sequence<UElement>.log() = this.joinToString { it.asLogString() }
private fun assertParents(node: UElement, uastAnchor: UIdentifier?) {
// skipping such elements because they are ambiguous (properties and getters for instance)
if (node.sourcePsi.toUElement() != node) return
if (uastAnchor == null)
throw AssertionError("no uast anchor for node = $node")
val nodeParents = node.withContainingElements.log()
val anchorParentParents = uastAnchor.uastParent?.withContainingElements?.log() ?: ""
// dropping node itself because we allow children to share identifiers with parents (primary constructor for instance)
val parentsSuffix = node.withContainingElements.drop(1).log()
if (!anchorParentParents.endsWith(parentsSuffix))
throw ComparisonFailure(
"wrong parents for '${uastAnchor.sourcePsi?.text}' owner: $node[${node.sourcePsi}[${node.sourcePsi?.text}]]",
nodeParents,
anchorParentParents
)
}
private fun refNameRetriever(psiElement: PsiElement): UElement? =
when (val uElement = psiElement.toUElementOfExpectedTypes(UCallExpression::class.java, UReferenceExpression::class.java)) {
is UReferenceExpression -> uElement.referenceNameElement
is UCallExpression -> uElement.classReference?.referenceNameElement
else -> null
}?.also {
assertNotNull(it.sourcePsi, "referenceNameElement should have physical source, origin = $psiElement")
}
fun UFile.asRefNames() = object : UElementToParentMap(::refNameRetriever) {
override fun renderSource(element: PsiElement): String = element.javaClass.simpleName
}.visitUFileAndGetResult(this)