UAST: utilize identifier test base
This commit is contained in:
committed by
Ilya Kirillov
parent
da15f0ffe8
commit
c37123603c
@@ -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 ()
|
||||
|
||||
+43
@@ -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
|
||||
}
|
||||
}
|
||||
+78
@@ -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)
|
||||
Reference in New Issue
Block a user