mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Uast: conversion possibility check optimisation
This commit is contained in:
+3
-6
@@ -11,23 +11,20 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.uast.*;
|
||||
import org.jetbrains.uast.visitor.AbstractUastNonRecursiveVisitor;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class AbstractBaseUastLocalInspectionTool extends LocalInspectionTool {
|
||||
|
||||
private static final Condition<PsiElement> PROBLEM_ELEMENT_CONDITION =
|
||||
Conditions.and(Conditions.instanceOf(PsiFile.class, PsiClass.class, PsiMethod.class, PsiField.class),
|
||||
Conditions.notInstanceOf(PsiTypeParameter.class));
|
||||
|
||||
private final List<Class<? extends UElement>> myUElementsTypesHint;
|
||||
private final Class<? extends UElement>[] myUElementsTypesHint;
|
||||
|
||||
protected AbstractBaseUastLocalInspectionTool() {
|
||||
myUElementsTypesHint = Arrays.asList(UFile.class, UClass.class, UMethod.class, UField.class);
|
||||
this(UFile.class, UClass.class, UMethod.class, UField.class);
|
||||
}
|
||||
|
||||
protected AbstractBaseUastLocalInspectionTool(Class<? extends UElement>... uElementsTypesHint) {
|
||||
myUElementsTypesHint = Arrays.asList(uElementsTypesHint);
|
||||
myUElementsTypesHint = uElementsTypesHint;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,7 +11,7 @@ import org.jetbrains.uast.visitor.AbstractUastNonRecursiveVisitor
|
||||
open class UastHintedVisitorAdapter(private val plugin: UastLanguagePlugin,
|
||||
private val visitor: AbstractUastNonRecursiveVisitor,
|
||||
private val directOnly: Boolean,
|
||||
private val uElementTypesHint: List<Class<out UElement>>
|
||||
private val uElementTypesHint: Array<Class<out UElement>>
|
||||
) : PsiElementVisitor() {
|
||||
|
||||
override fun visitElement(element: PsiElement) {
|
||||
@@ -27,7 +27,7 @@ open class UastHintedVisitorAdapter(private val plugin: UastLanguagePlugin,
|
||||
@JvmOverloads
|
||||
fun create(language: Language,
|
||||
visitor: AbstractUastNonRecursiveVisitor,
|
||||
uElementTypesHint: List<Class<out UElement>>,
|
||||
uElementTypesHint: Array<Class<out UElement>>,
|
||||
directOnly: Boolean = true): PsiElementVisitor {
|
||||
val uastLanguagePlugin = UastLanguagePlugin.byLanguage(language) ?: return EMPTY_VISITOR
|
||||
return UastHintedVisitorAdapter(uastLanguagePlugin, visitor, directOnly, uElementTypesHint)
|
||||
|
||||
@@ -116,7 +116,7 @@ fun <T : UElement> PsiElement?.toUElement(cls: Class<out T>): T? =
|
||||
fun <T : UElement> PsiElement?.toUElementOfExpectedTypes(vararg clss: Class<out T>): T? =
|
||||
this?.let {
|
||||
ServiceManager.getService(project, UastContext::class.java)
|
||||
.convertElementWithParent(this, if (clss.isNotEmpty()) clss.toList() else DEFAULT_TYPES_LIST) as T?
|
||||
.convertElementWithParent(this, if (clss.isNotEmpty()) clss else DEFAULT_TYPES_LIST) as T?
|
||||
}
|
||||
|
||||
|
||||
@@ -150,7 +150,7 @@ fun <T : UElement> PsiElement?.getUastParentOfType(cls: Class<out T>, strict: Bo
|
||||
inline fun <reified T : UElement> PsiElement?.getUastParentOfType(strict: Boolean = false): T? = getUastParentOfType(T::class.java, strict)
|
||||
|
||||
@JvmField
|
||||
val DEFAULT_TYPES_LIST: List<Class<out UElement>> = listOf(UElement::class.java)
|
||||
val DEFAULT_TYPES_LIST: Array<Class<out UElement>> = arrayOf(UElement::class.java)
|
||||
|
||||
@JvmField
|
||||
val DEFAULT_EXPRESSION_TYPES_LIST: List<Class<out UExpression>> = listOf(UExpression::class.java)
|
||||
val DEFAULT_EXPRESSION_TYPES_LIST: Array<Class<out UExpression>> = arrayOf(UExpression::class.java)
|
||||
|
||||
@@ -106,7 +106,7 @@ interface UastLanguagePlugin {
|
||||
fun isExpressionValueUsed(element: UExpression): Boolean
|
||||
|
||||
@JvmDefault
|
||||
fun <T : UElement> convertElementWithParent(element: PsiElement, requiredTypes: List<Class<out T>>): T? =
|
||||
fun <T : UElement> convertElementWithParent(element: PsiElement, requiredTypes: Array<out Class<out T>>): T? =
|
||||
when {
|
||||
requiredTypes.isEmpty() -> convertElementWithParent(element, null)
|
||||
requiredTypes.size == 1 -> convertElementWithParent(element, requiredTypes.single())
|
||||
@@ -116,7 +116,7 @@ interface UastLanguagePlugin {
|
||||
|
||||
|
||||
@JvmDefault
|
||||
fun <T : UElement> convertToAlternatives(element: PsiElement, requiredTypes: List<Class<out T>>): Sequence<T> =
|
||||
fun <T : UElement> convertToAlternatives(element: PsiElement, requiredTypes: Array<out Class<out T>>): Sequence<T> =
|
||||
sequenceOf(convertElementWithParent(element, requiredTypes)).filterNotNull()
|
||||
|
||||
}
|
||||
|
||||
@@ -9,28 +9,26 @@ class UElementToPsiElementMapping(val baseMapping: Map<Class<out UElement>, Clas
|
||||
|
||||
constructor(vararg mapping: Pair<Class<out UElement>, ClassSet>) : this(mapOf(*mapping))
|
||||
|
||||
private val internalMapping = ConcurrentHashMap<Class<out UElement>, List<ClassSet>>()
|
||||
private val internalMapping = ConcurrentHashMap<Class<out UElement>, ClassSet>()
|
||||
|
||||
private operator fun get(uCls: Class<out UElement>): List<ClassSet> {
|
||||
operator fun get(uCls: Class<out UElement>): ClassSet {
|
||||
internalMapping[uCls]?.let { return it }
|
||||
|
||||
var result = mutableListOf<ClassSet>()
|
||||
val applicableClassSets = mutableListOf<ClassSet>()
|
||||
|
||||
for ((key, set) in baseMapping.entries) {
|
||||
if (uCls.isAssignableFrom(key)) result.add(set)
|
||||
}
|
||||
|
||||
if (result.size > MERGING_CLASS_SET_LIMIT) {
|
||||
result = mutableListOf(ClassSet(*result.flatMap { it.initialClasses.toList() }.toTypedArray()))
|
||||
if (uCls.isAssignableFrom(key)) applicableClassSets.add(set)
|
||||
}
|
||||
|
||||
val result = mergeClassSets(applicableClassSets)
|
||||
internalMapping[uCls] = result
|
||||
return result
|
||||
}
|
||||
|
||||
fun canConvert(psiCls: Class<out PsiElement>, targets: List<Class<out UElement>>): Boolean {
|
||||
|
||||
fun canConvert(psiCls: Class<out PsiElement>, targets: Array<out Class<out UElement>>): Boolean {
|
||||
for (target in targets) {
|
||||
if (this[target].any { it.contains(psiCls) })
|
||||
if (this[target].contains(psiCls))
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@@ -39,17 +37,30 @@ class UElementToPsiElementMapping(val baseMapping: Map<Class<out UElement>, Clas
|
||||
|
||||
}
|
||||
|
||||
private const val MERGING_CLASS_SET_LIMIT = 5
|
||||
private const val SIMPLE_CLASS_SET_LIMIT = 5
|
||||
|
||||
private fun mergeClassSets(result: List<ClassSet>) = result.singleOrNull() ?: ClassSet(
|
||||
*result.flatMap { it.initialClasses.asIterable() }.toTypedArray())
|
||||
|
||||
class ClassSet(vararg val initialClasses: Class<*>) {
|
||||
|
||||
private val internalMapping = ConcurrentHashMap<Class<*>, Boolean>().apply {
|
||||
for (initialClass in initialClasses) {
|
||||
this[initialClass] = true
|
||||
}
|
||||
private val isSimple = initialClasses.size <= SIMPLE_CLASS_SET_LIMIT
|
||||
|
||||
private lateinit var internalMapping: ConcurrentHashMap<Class<*>, Boolean>
|
||||
|
||||
init {
|
||||
if (!isSimple)
|
||||
internalMapping = ConcurrentHashMap<Class<*>, Boolean>().apply {
|
||||
for (initialClass in initialClasses) {
|
||||
this[initialClass] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun contains(cls: Class<*>): Boolean =
|
||||
internalMapping[cls] ?: initialClasses.any { it.isAssignableFrom(cls) }.also { internalMapping[cls] = it }
|
||||
if (isSimple) anyAssignable(cls) else internalMapping[cls] ?: anyAssignable(cls).also { internalMapping[cls] = it }
|
||||
|
||||
private fun anyAssignable(cls: Class<*>): Boolean = initialClasses.any { it.isAssignableFrom(cls) }
|
||||
|
||||
}
|
||||
@@ -18,7 +18,6 @@ package org.jetbrains.uast.java
|
||||
|
||||
import com.intellij.lang.Language
|
||||
import com.intellij.lang.java.JavaLanguage
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.impl.source.tree.java.PsiLiteralExpressionImpl
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
@@ -29,8 +28,6 @@ import org.jetbrains.uast.java.expressions.JavaUSynchronizedExpression
|
||||
|
||||
class JavaUastLanguagePlugin : UastLanguagePlugin {
|
||||
|
||||
private val checkCanConvert = Registry.`is`("uast.java.use.psi.type.precheck")
|
||||
|
||||
override val priority: Int = 0
|
||||
|
||||
override fun isFileSupported(fileName: String): Boolean = fileName.endsWith(".java", ignoreCase = true)
|
||||
@@ -99,20 +96,19 @@ class JavaUastLanguagePlugin : UastLanguagePlugin {
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun <T : UElement> convertElement(element: PsiElement, parent: UElement?, requiredTypes: List<Class<out T>>): T? {
|
||||
fun <T : UElement> convertElement(element: PsiElement, parent: UElement?, requiredTypes: Array<out Class<out T>>): T? {
|
||||
val nonEmptyRequiredTypes = requiredTypes.nonEmptyOr(DEFAULT_TYPES_LIST)
|
||||
if (checkCanConvert && !canConvert(element.javaClass, nonEmptyRequiredTypes)) return null
|
||||
|
||||
if (!canConvert(element.javaClass, requiredTypes)) return null
|
||||
return (convertDeclaration(element, parent, nonEmptyRequiredTypes)
|
||||
?: JavaConverter.convertPsiElement(element, parent, nonEmptyRequiredTypes)) as? T
|
||||
}
|
||||
|
||||
override fun <T : UElement> convertElementWithParent(element: PsiElement, requiredTypes: List<Class<out T>>): T? {
|
||||
override fun <T : UElement> convertElementWithParent(element: PsiElement, requiredTypes: Array<out Class<out T>>): T? {
|
||||
return convertElement(element, null, requiredTypes)
|
||||
}
|
||||
|
||||
override fun <T : UElement> convertToAlternatives(element: PsiElement, requiredTypes: List<Class<out T>>) = when (element) {
|
||||
is `PsiMethodCallExpression` ->
|
||||
override fun <T : UElement> convertToAlternatives(element: PsiElement, requiredTypes: Array<out Class<out T>>) = when (element) {
|
||||
is PsiMethodCallExpression ->
|
||||
JavaConverter.psiMethodCallConversionAlternatives(element,
|
||||
null,
|
||||
requiredTypes.nonEmptyOr(DEFAULT_EXPRESSION_TYPES_LIST)) as Sequence<T>
|
||||
@@ -121,7 +117,7 @@ class JavaUastLanguagePlugin : UastLanguagePlugin {
|
||||
|
||||
private fun convertDeclaration(element: PsiElement,
|
||||
givenParent: UElement?,
|
||||
requiredType: List<Class<out UElement>>): UElement? {
|
||||
requiredType: Array<out Class<out UElement>>): UElement? {
|
||||
fun <P : PsiElement> build(ctor: (P, UElement?) -> UElement): () -> UElement? {
|
||||
return fun(): UElement? {
|
||||
return ctor(element as P, givenParent)
|
||||
@@ -155,15 +151,15 @@ internal inline fun <reified ActualT : UElement> Class<*>?.el(f: () -> UElement?
|
||||
return if (this == null || isAssignableFrom(ActualT::class.java)) f() else null
|
||||
}
|
||||
|
||||
internal inline fun <reified ActualT : UElement> List<Class<out UElement>>.el(f: () -> UElement?): UElement? {
|
||||
internal inline fun <reified ActualT : UElement> Array<out Class<out UElement>>.el(f: () -> UElement?): UElement? {
|
||||
return if (isAssignableFrom(ActualT::class.java)) f() else null
|
||||
}
|
||||
|
||||
internal inline fun <reified ActualT : UElement> List<Class<out UElement>>.expr(f: () -> UExpression?): UExpression? {
|
||||
internal inline fun <reified ActualT : UElement> Array<out Class<out UElement>>.expr(f: () -> UExpression?): UExpression? {
|
||||
return if (isAssignableFrom(ActualT::class.java)) f() else null
|
||||
}
|
||||
|
||||
internal fun List<Class<out UElement>>.isAssignableFrom(cls: Class<*>) = any { it.isAssignableFrom(cls) }
|
||||
internal fun Array<out Class<out UElement>>.isAssignableFrom(cls: Class<*>) = any { it.isAssignableFrom(cls) }
|
||||
|
||||
internal object JavaConverter {
|
||||
|
||||
@@ -181,7 +177,7 @@ internal object JavaConverter {
|
||||
|
||||
internal fun convertPsiElement(el: PsiElement,
|
||||
givenParent: UElement?,
|
||||
requiredType: List<Class<out UElement>> = DEFAULT_TYPES_LIST): UElement? {
|
||||
requiredType: Array<out Class<out UElement>> = DEFAULT_TYPES_LIST): UElement? {
|
||||
|
||||
fun <P : PsiElement> build(ctor: (P, UElement?) -> UElement): () -> UElement? {
|
||||
return fun(): UElement? {
|
||||
@@ -214,7 +210,7 @@ internal object JavaConverter {
|
||||
|
||||
internal fun convertReference(reference: PsiJavaCodeReferenceElement,
|
||||
givenParent: UElement?,
|
||||
requiredType: List<Class<out UElement>> = DEFAULT_TYPES_LIST): UExpression? {
|
||||
requiredType: Array<out Class<out UElement>> = DEFAULT_TYPES_LIST): UExpression? {
|
||||
return with(requiredType) {
|
||||
if (reference.isQualified) {
|
||||
expr<UQualifiedReferenceExpression> { JavaUQualifiedReferenceExpression(reference, givenParent) }
|
||||
@@ -228,7 +224,7 @@ internal object JavaConverter {
|
||||
|
||||
internal fun convertExpression(el: PsiExpression,
|
||||
givenParent: UElement?,
|
||||
requiredType: List<Class<out UElement>> = DEFAULT_EXPRESSION_TYPES_LIST): UExpression? {
|
||||
requiredType: Array<out Class<out UElement>> = DEFAULT_EXPRESSION_TYPES_LIST): UExpression? {
|
||||
fun <P : PsiElement> build(ctor: (P, UElement?) -> UExpression): () -> UExpression? {
|
||||
return fun(): UExpression? {
|
||||
return ctor(el as P, givenParent)
|
||||
@@ -271,7 +267,7 @@ internal object JavaConverter {
|
||||
|
||||
internal fun psiMethodCallConversionAlternatives(element: PsiMethodCallExpression,
|
||||
givenParent: UElement?,
|
||||
requiredTypes: List<Class<out UElement>>): Sequence<UExpression> {
|
||||
requiredTypes: Array<out Class<out UElement>>): Sequence<UExpression> {
|
||||
if (element.methodExpression.qualifierExpression == null) {
|
||||
return sequenceOf(requiredTypes.expr<UCallExpression> { JavaUCallExpression(element, givenParent) }).filterNotNull()
|
||||
}
|
||||
@@ -293,7 +289,7 @@ internal object JavaConverter {
|
||||
|
||||
internal fun convertStatement(el: PsiStatement,
|
||||
givenParent: UElement?,
|
||||
requiredType: List<Class<out UElement>> = DEFAULT_EXPRESSION_TYPES_LIST): UExpression? {
|
||||
requiredType: Array<out Class<out UElement>> = DEFAULT_EXPRESSION_TYPES_LIST): UExpression? {
|
||||
fun <P : PsiElement> build(ctor: (P, UElement?) -> UExpression): () -> UExpression? {
|
||||
return fun(): UExpression? {
|
||||
return ctor(el as P, givenParent)
|
||||
@@ -373,8 +369,9 @@ internal object JavaConverter {
|
||||
}
|
||||
}
|
||||
|
||||
private fun expressionTypes(requiredType: Class<out UElement>?) = requiredType?.let { listOf(it) } ?: DEFAULT_EXPRESSION_TYPES_LIST
|
||||
private fun expressionTypes(requiredType: Class<out UElement>?) = requiredType?.let { arrayOf(it) } ?: DEFAULT_EXPRESSION_TYPES_LIST
|
||||
|
||||
private fun elementTypes(requiredType: Class<out UElement>?) = requiredType?.let { listOf(it) } ?: DEFAULT_TYPES_LIST
|
||||
private fun elementTypes(requiredType: Class<out UElement>?) = requiredType?.let { arrayOf(it) } ?: DEFAULT_TYPES_LIST
|
||||
|
||||
private fun <T : UElement> List<Class<out T>>.nonEmptyOr(default: List<Class<out UElement>>) = takeIf { it.isNotEmpty() } ?: default
|
||||
private fun <T : UElement> Array<out Class<out T>>.nonEmptyOr(default: Array<out Class<out UElement>>) = takeIf { it.isNotEmpty() }
|
||||
?: default
|
||||
@@ -1,15 +1,30 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.uast.java
|
||||
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.impl.source.tree.java.PsiLiteralExpressionImpl
|
||||
import org.jetbrains.uast.*
|
||||
import org.jetbrains.uast.internal.ClassSet
|
||||
import org.jetbrains.uast.internal.UElementToPsiElementMapping
|
||||
|
||||
private val checkCanConvert = Registry.`is`("uast.java.use.psi.type.precheck")
|
||||
|
||||
internal fun canConvert(psiCls: Class<out PsiElement>, targets: List<Class<out UElement>>) =
|
||||
conversionMapping.canConvert(psiCls, targets)
|
||||
internal fun canConvert(psiCls: Class<out PsiElement>, targets: Array<out Class<out UElement>>): Boolean {
|
||||
if (!checkCanConvert) return true
|
||||
|
||||
if (targets.size == 1) {
|
||||
// checking the most popular cases before looking up in hashtable
|
||||
when (targets.single()) {
|
||||
UElement::class.java -> uElementClassSet.contains(psiCls)
|
||||
ULiteralExpression::class.java -> uLiteralClassSet.contains(psiCls)
|
||||
UCallExpression::class.java -> uCallClassSet.contains(psiCls)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return conversionMapping.canConvert(psiCls, targets)
|
||||
}
|
||||
|
||||
private val conversionMapping = UElementToPsiElementMapping(
|
||||
UClass::class.java to ClassSet(PsiClass::class.java),
|
||||
@@ -83,3 +98,9 @@ private val conversionMapping = UElementToPsiElementMapping(
|
||||
UExpression::class.java to ClassSet(PsiExpressionStatement::class.java),
|
||||
USwitchClauseExpression::class.java to ClassSet(PsiSwitchLabelStatementBase::class.java)
|
||||
)
|
||||
|
||||
val uElementClassSet = ClassSet(*conversionMapping.baseMapping.flatMap { it.value.initialClasses.asIterable() }.toTypedArray())
|
||||
|
||||
val uLiteralClassSet: ClassSet = conversionMapping[ULiteralExpression::class.java]
|
||||
|
||||
val uCallClassSet: ClassSet = conversionMapping[UCallExpression::class.java]
|
||||
|
||||
@@ -84,21 +84,21 @@ class JavaUastApiTest : AbstractJavaUastTest() {
|
||||
|
||||
val javaUastLanguagePlugin = UastLanguagePlugin.byLanguage(callExpression.language)!!
|
||||
|
||||
javaUastLanguagePlugin.convertToAlternatives(callExpression, listOf(UCallExpression::class.java)).let {
|
||||
javaUastLanguagePlugin.convertToAlternatives(callExpression, arrayOf(UCallExpression::class.java)).let {
|
||||
assertEquals("format(\"q\")", it.joinToString(transform = UExpression::asRenderString))
|
||||
}
|
||||
|
||||
javaUastLanguagePlugin.convertToAlternatives<UExpression>(callExpression, listOf(UQualifiedReferenceExpression::class.java,
|
||||
UCallExpression::class.java)).let {
|
||||
javaUastLanguagePlugin.convertToAlternatives<UExpression>(callExpression, arrayOf(UQualifiedReferenceExpression::class.java,
|
||||
UCallExpression::class.java)).let {
|
||||
assertEquals("String.format(\"q\"), format(\"q\")", it.joinToString(transform = UExpression::asRenderString))
|
||||
}
|
||||
|
||||
javaUastLanguagePlugin.convertToAlternatives<UExpression>(callExpression, listOf(UCallExpression::class.java,
|
||||
UQualifiedReferenceExpression::class.java)).let {
|
||||
javaUastLanguagePlugin.convertToAlternatives<UExpression>(callExpression, arrayOf(UCallExpression::class.java,
|
||||
UQualifiedReferenceExpression::class.java)).let {
|
||||
assertEquals("format(\"q\"), String.format(\"q\")", it.joinToString(transform = UExpression::asRenderString))
|
||||
}
|
||||
|
||||
javaUastLanguagePlugin.convertToAlternatives(callExpression, listOf(UExpression::class.java)).let {
|
||||
javaUastLanguagePlugin.convertToAlternatives(callExpression, arrayOf(UExpression::class.java)).let {
|
||||
assertEquals("String.format(\"q\"), format(\"q\")", it.joinToString(transform = UExpression::asRenderString))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user