New J2K: do not print qualified names if corresponding qualifier can unambiguously resolved

This speeds up shortening qualified references phase
This commit is contained in:
Ilya Kirillov
2019-09-27 19:18:36 +03:00
parent 678cc35008
commit 043a669812
20 changed files with 148 additions and 56 deletions
@@ -8,7 +8,7 @@ package org.jetbrains.kotlin.nj2k.postProcessing.processings
import com.intellij.openapi.editor.RangeMarker
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.idea.core.ShortenReferences
import org.jetbrains.kotlin.nj2k.ImportStorage
import org.jetbrains.kotlin.nj2k.JKImportStorage
import org.jetbrains.kotlin.nj2k.NewJ2kConverterContext
import org.jetbrains.kotlin.nj2k.postProcessing.GeneralPostProcessing
import org.jetbrains.kotlin.nj2k.postProcessing.runUndoTransparentActionInEdt
@@ -19,7 +19,7 @@ class ShortenReferenceProcessing : GeneralPostProcessing {
private val filter = filter@{ element: PsiElement ->
when (element) {
is KtQualifiedExpression -> when {
ImportStorage.isImportNeededForCall(element) -> ShortenReferences.FilterResult.PROCESS
JKImportStorage.isImportNeededForCall(element) -> ShortenReferences.FilterResult.PROCESS
else -> ShortenReferences.FilterResult.SKIP
}
else -> ShortenReferences.FilterResult.PROCESS
@@ -13,7 +13,7 @@ import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtQualifiedExpression
import org.jetbrains.kotlin.resolve.calls.callUtil.getCalleeExpressionIfAny
class ImportStorage {
class JKImportStorage {
private val imports = mutableSetOf<FqName>()
fun addImport(import: FqName) {
@@ -22,6 +22,10 @@ class ImportStorage {
}
}
fun addImport(import: String) {
addImport(FqName(import))
}
fun getImports(): Set<FqName> = imports
companion object {
@@ -0,0 +1,88 @@
/*
* Copyright 2010-2019 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.kotlin.nj2k
import com.intellij.openapi.project.Project
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.PsiShortNamesCache
import com.intellij.util.Processor
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.nj2k.symbols.*
import org.jetbrains.kotlin.nj2k.tree.JKClassAccessExpression
import org.jetbrains.kotlin.nj2k.tree.JKQualifiedExpression
import org.jetbrains.kotlin.nj2k.tree.JKTreeElement
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
class JKSymbolRenderer(private val importStorage: JKImportStorage, project: Project) {
private val canBeShortenedClassNameCache = CanBeShortenedCache(project)
private fun JKSymbol.isFqNameExpected(owner: JKTreeElement?): Boolean {
if (owner?.isSelectorOfQualifiedExpression() == true) return false
return this is JKClassSymbol || isStaticMember || isEnumConstant
}
private fun JKSymbol.isFromJavaLangPackage() =
fqName.startsWith(JAVA_LANG_FQ_PREFIX)
fun renderSymbol(symbol: JKSymbol, owner: JKTreeElement?): String {
val name = symbol.name.escaped()
if (!symbol.isFqNameExpected(owner)) return name
val fqName = symbol.getDisplayFqName().escapedAsQualifiedName()
if (owner is JKClassAccessExpression && symbol.isFromJavaLangPackage()) return fqName
return when {
symbol is JKClassSymbol && canBeShortenedClassNameCache.canBeShortened(symbol) -> {
importStorage.addImport(fqName)
name
}
symbol.isStaticMember && symbol.containingClass?.isUnnamedCompanion == true -> {
val containingClass = symbol.containingClass ?: return fqName
val classContainingCompanion = containingClass.containingClass ?: return fqName
if (!canBeShortenedClassNameCache.canBeShortened(classContainingCompanion)) return fqName
importStorage.addImport(classContainingCompanion.getDisplayFqName())
"${classContainingCompanion.name.escaped()}.${SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT}.$name"
}
symbol.isEnumConstant || symbol.isStaticMember -> {
val containingClass = symbol.containingClass ?: return fqName
if (!canBeShortenedClassNameCache.canBeShortened(containingClass)) return fqName
importStorage.addImport(containingClass.getDisplayFqName())
"${containingClass.name.escaped()}.$name"
}
else -> fqName
}
}
private fun JKTreeElement.isSelectorOfQualifiedExpression() =
parent?.safeAs<JKQualifiedExpression>()?.selector == this
companion object {
private const val JAVA_LANG_FQ_PREFIX = "java.lang"
}
}
private class CanBeShortenedCache(project: Project) {
private val shortNameCache = PsiShortNamesCache.getInstance(project)
private val searchScope = GlobalSearchScope.allScope(project)
private val canBeShortenedCache = mutableMapOf<String, Boolean>().apply {
CLASS_NAMES_WHICH_HAVE_DIFFERENT_MEANINGS_IN_KOTLIN_AND_JAVA.forEach { name ->
this[name] = false
}
}
fun canBeShortened(symbol: JKClassSymbol): Boolean = canBeShortenedCache.getOrPut(symbol.name) {
!shortNameCache.processClassesWithName(symbol.name, Processor.FALSE, searchScope, null)
}
companion object {
private val CLASS_NAMES_WHICH_HAVE_DIFFERENT_MEANINGS_IN_KOTLIN_AND_JAVA = setOf(
"Function",
"Serializable"
)
}
}
@@ -57,7 +57,7 @@ class JavaToJKTreeBuilder constructor(
private val symbolProvider: JKSymbolProvider,
private val typeFactory: JKTypeFactory,
converterServices: NewJavaToKotlinServices,
private val importStorage: ImportStorage
private val importStorage: JKImportStorage
) {
private fun PsiType?.toJK(): JKType {
if (this == null) return JKNoType
@@ -5,8 +5,10 @@
package org.jetbrains.kotlin.nj2k
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.j2k.ast.Nullability
import org.jetbrains.kotlin.nj2k.symbols.*
import org.jetbrains.kotlin.nj2k.symbols.JKSymbol
import org.jetbrains.kotlin.nj2k.symbols.getDisplayFqName
import org.jetbrains.kotlin.nj2k.tree.*
import org.jetbrains.kotlin.nj2k.tree.visitors.JKVisitorWithCommentsPrinting
import org.jetbrains.kotlin.nj2k.types.*
@@ -15,7 +17,7 @@ import org.jetbrains.kotlin.utils.addToStdlib.safeAs
class NewCodeBuilder(context: NewJ2kConverterContext) {
private val elementInfoStorage = context.elementsInfoStorage
private val printer = JKPrinter(elementInfoStorage)
private val printer = JKPrinter(context.project, context.importStorage, elementInfoStorage)
private fun classKindString(kind: JKClass.ClassKind): String = when (kind) {
JKClass.ClassKind.ANNOTATION -> "annotation class"
@@ -689,7 +691,7 @@ class NewCodeBuilder(context: NewJ2kConverterContext) {
printer.print("::")
val needFqName = methodReferenceExpression.qualifier is JKStubExpression
val displayName =
if (needFqName) methodReferenceExpression.identifier.getDisplayName()
if (needFqName) methodReferenceExpression.identifier.getDisplayFqName()
else methodReferenceExpression.identifier.name
printer.print(displayName.escapedAsQualifiedName())
@@ -816,7 +818,7 @@ class NewCodeBuilder(context: NewJ2kConverterContext) {
override fun visitAnnotationRaw(annotation: JKAnnotation) {
printer.print("@")
printer.print(annotation.classSymbol.fqName.escapedAsQualifiedName())
printer.renderSymbol(annotation.classSymbol, annotation)
if (annotation.arguments.isNotEmpty()) {
printer.par {
printer.renderList(annotation.arguments) { it.accept(this) }
@@ -880,9 +882,13 @@ enum class ParenthesisKind(val open: String, val close: String) {
ANGLE("<", ">")
}
private class JKPrinter(
project: Project,
importStorage: JKImportStorage,
private val elementInfoStorage: JKElementInfoStorage
) {
val symbolRenderer = JKSymbolRenderer(importStorage, project)
private val stringBuilder: StringBuilder = StringBuilder()
private var currentIndent = 0;
private val indentSymbol = " ".repeat(4)
@@ -921,15 +927,11 @@ private class JKPrinter(
}
inline fun par(kind: ParenthesisKind = ParenthesisKind.ROUND, body: () -> Unit) {
this.print(kind.open)
print(kind.open)
body()
this.print(kind.close)
print(kind.close)
}
private fun JKSymbol.needFqName(): Boolean {
if (this !is JKClassSymbol && !isStaticMember && !isEnumConstant) return false
return fqName !in mappedToKotlinFqNames
}
private fun JKType.renderTypeInfo() {
this@JKPrinter.print(elementInfoStorage.getOrCreateInfoForElement(this).render())
@@ -984,9 +986,7 @@ private class JKPrinter(
}
fun renderSymbol(symbol: JKSymbol, owner: JKTreeElement?) {
val needFqName = symbol.needFqName() && owner?.isSelectorOfQualifiedExpression() != true
val displayName = if (needFqName) symbol.getDisplayName() else symbol.name
this.print(displayName.escapedAsQualifiedName())
print(symbolRenderer.renderSymbol(symbol, owner))
}
inline fun <T> renderList(list: List<T>, separator: String = ", ", renderElement: (T) -> Unit) =
@@ -1000,13 +1000,10 @@ private class JKPrinter(
renderElement(it)
}
}
private fun JKTreeElement.isSelectorOfQualifiedExpression() =
parent?.safeAs<JKQualifiedExpression>()?.selector == this
}
private fun String.escapedAsQualifiedName(): String =
fun String.escapedAsQualifiedName(): String =
split('.')
.map { it.escaped() }
.joinToString(".") { it }
@@ -1016,14 +1013,3 @@ private fun <T> List<T>.headTail(): Pair<T?, List<T>?> {
val tail = if (size <= 1) null else subList(1, size)
return head to tail
}
private val mappedToKotlinFqNames =
setOf(
"java.util.ArrayList",
"java.util.LinkedHashMap",
"java.util.HashMap",
"java.util.LinkedHashSet",
"java.util.HashSet"
)
@@ -15,7 +15,7 @@ data class NewJ2kConverterContext(
val typeFactory: JKTypeFactory,
val converter: NewJavaToKotlinConverter,
val inConversionContext: (PsiElement) -> Boolean,
val importStorage: ImportStorage,
val importStorage: JKImportStorage,
val elementsInfoStorage: JKElementInfoStorage
) : ConverterContext {
val project: Project
@@ -102,7 +102,7 @@ class NewJavaToKotlinConverter(
val typeFactory = JKTypeFactory(symbolProvider)
symbolProvider.typeFactory = typeFactory
symbolProvider.preBuildTree(inputElements)
val importStorage = ImportStorage()
val importStorage = JKImportStorage()
val treeBuilder = JavaToJKTreeBuilder(symbolProvider, typeFactory, converterServices, importStorage)
@@ -5,7 +5,7 @@
package org.jetbrains.kotlin.nj2k.conversions
import org.jetbrains.kotlin.nj2k.ImportStorage
import org.jetbrains.kotlin.nj2k.JKImportStorage
import org.jetbrains.kotlin.nj2k.NewJ2kConverterContext
import org.jetbrains.kotlin.nj2k.tree.JKImportList
import org.jetbrains.kotlin.nj2k.tree.JKTreeElement
@@ -14,7 +14,7 @@ class FilterImportsConversion(context: NewJ2kConverterContext) : RecursiveApplic
override fun applyToElement(element: JKTreeElement): JKTreeElement {
if (element !is JKImportList) return recurse(element)
element.imports = element.imports.filter { import ->
ImportStorage.isImportNeeded(import.name.value)
JKImportStorage.isImportNeeded(import.name.value)
}
return recurse(element)
}
@@ -12,6 +12,7 @@ import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiModifierListOwner
import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName
import org.jetbrains.kotlin.idea.search.declarationsSearch.findDeepestSuperMethodsNoWrapping
import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.nj2k.isObjectOrCompanionObject
import org.jetbrains.kotlin.nj2k.psi
import org.jetbrains.kotlin.nj2k.tree.*
@@ -23,7 +24,7 @@ import org.jetbrains.kotlin.utils.addToStdlib.safeAs
val JKSymbol.isUnresolved
get() = this is JKUnresolvedSymbol
fun JKSymbol.getDisplayName(): String {
fun JKSymbol.getDisplayFqName(): String {
fun JKSymbol.isDisplayable() = this is JKClassSymbol || this is JKPackageSymbol
if (this !is JKUniverseSymbol<*>) return fqName
return generateSequence(declaredIn?.takeIf { it.isDisplayable() }) { symbol ->
@@ -31,11 +32,6 @@ fun JKSymbol.getDisplayName(): String {
}.fold(name) { acc, symbol -> "${symbol.name}.$acc" }
}
fun JKSymbol.fqNameToImport(): String? = when {
this is JKClassSymbol && this !is JKUniverseClassSymbol -> fqName
else -> null
}
fun JKSymbol.deepestFqName(): String? {
fun Any.deepestFqNameForTarget(): String? =
when (this) {
@@ -47,6 +43,9 @@ fun JKSymbol.deepestFqName(): String? {
return target.deepestFqNameForTarget() ?: fqName
}
val JKSymbol.containingClass
get() = declaredIn as? JKClassSymbol
val JKSymbol.isStaticMember
get() = when (val target = target) {
is PsiModifierListOwner -> target.hasModifier(JvmModifier.STATIC)
@@ -66,3 +65,11 @@ val JKSymbol.isEnumConstant
is KtEnumEntry -> true
else -> false
}
val JKSymbol.isUnnamedCompanion
get() = when (val target = target) {
is JKClass -> target.classKind == JKClass.ClassKind.COMPANION
is KtObjectDeclaration -> target.isCompanion() && target.name == SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT.toString()
else -> false
}
+1 -2
View File
@@ -18,8 +18,7 @@ class A {
}
}
}
assert(element is PsiMethod
) { "Method accepts only kotlin functions/properties and java methods, but '" + element.getText().toString() + "' was found" }
assert(element is PsiMethod) { "Method accepts only kotlin functions/properties and java methods, but '" + element.getText().toString() + "' was found" }
return JetRefactoringUtil.formatPsiMethod(element as PsiMethod, true, false)
}
+1 -2
View File
@@ -1,4 +1,3 @@
// ERROR: Unresolved reference: Test
// !forceNotNullTypes: false
// !specifyLocalVariableTypeByDefault: true
package test
@@ -18,7 +17,7 @@ class Test(str: String) {
val test = "String2"
sout(test)
sout(dummy(test))
test.Test(test)
Test(test)
}
init {
+1 -2
View File
@@ -1,4 +1,3 @@
// ERROR: Unresolved reference: Test
// !specifyLocalVariableTypeByDefault: true
package test
@@ -17,7 +16,7 @@ class Test(str: String?) {
val test = "String2"
sout(test)
sout(dummy(test))
test.Test(test)
Test(test)
}
init {
@@ -1,3 +1,5 @@
import A.Nested
internal class A @JvmOverloads constructor(nested: Nested? = Nested(Nested.FIELD)) {
internal class Nested(p: Int) {
companion object {
@@ -7,5 +9,5 @@ internal class A @JvmOverloads constructor(nested: Nested? = Nested(Nested.FIELD
}
internal class B {
var nested: A.Nested? = null
var nested: Nested? = null
}
@@ -1,5 +1,7 @@
package pack
import pack.A.Nested
internal class A @JvmOverloads constructor(nested: Nested? = Nested(Nested.FIELD)) {
internal class Nested(p: Int) {
companion object {
@@ -9,5 +11,5 @@ internal class A @JvmOverloads constructor(nested: Nested? = Nested(Nested.FIELD
}
internal class B {
var nested: A.Nested? = null
var nested: Nested? = null
}
@@ -1,5 +1,7 @@
package pack
import pack.A.Nested
internal class A @JvmOverloads constructor(nested: Nested? = Nested(Nested.FIELD)) {
internal class Nested(p: Int) {
companion object {
@@ -9,5 +11,5 @@ internal class A @JvmOverloads constructor(nested: Nested? = Nested(Nested.FIELD
}
internal class B {
var nested: A.Nested? = null
var nested: Nested? = null
}
+3 -1
View File
@@ -1,3 +1,5 @@
import A.I
open class A {
interface I {
fun f()
@@ -6,5 +8,5 @@ open class A {
class B : A()
class Test {
var z: A.I? = null
var z: I? = null
}
+3 -1
View File
@@ -1,5 +1,7 @@
import Foo.SomeClass
internal interface FooInterface {
fun foo(): ArrayList<out Foo.SomeClass?>?
fun foo(): ArrayList<out SomeClass?>?
}
class Foo : FooInterface {
-1
View File
@@ -1,4 +1,3 @@
import java.util.Comparator
import java.util.stream.Collectors
internal class Test {
@@ -1,4 +1,3 @@
import java.util.Comparator
import java.util.stream.Collectors
internal class Test {
@@ -1,11 +1,13 @@
package demo
import demo.Foo.Bar
internal class Foo {
internal class Bar
}
internal class User {
fun main() {
val boo = Foo.Bar()
val boo = Bar()
}
}