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:
+2
-2
@@ -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
|
||||
|
||||
+5
-1
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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,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()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user