K2 UAST: lookup properties in the containing class, not companion object
...because properties in the companion object are materialized in the containing class. ^KTIJ-27684 fixed
This commit is contained in:
+42
@@ -51,4 +51,46 @@ public class FirStandaloneNormalAnalysisLibraryBinaryModulePsiDeclarationProvide
|
|||||||
public void testMultifileFacade() throws Exception {
|
public void testMultifileFacade() throws Exception {
|
||||||
runTest("analysis/analysis-api/testData/standalone/multiModuleBinary/multifileFacade.kt");
|
runTest("analysis/analysis-api/testData/standalone/multiModuleBinary/multifileFacade.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("propertiesInCompanionObject.kt")
|
||||||
|
public void testPropertiesInCompanionObject() throws Exception {
|
||||||
|
runTest("analysis/analysis-api/testData/standalone/multiModuleBinary/propertiesInCompanionObject.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("propertiesInCompanionObject_JvmField.kt")
|
||||||
|
public void testPropertiesInCompanionObject_JvmField() throws Exception {
|
||||||
|
runTest("analysis/analysis-api/testData/standalone/multiModuleBinary/propertiesInCompanionObject_JvmField.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("propertiesInCompanionObject_JvmStatic.kt")
|
||||||
|
public void testPropertiesInCompanionObject_JvmStatic() throws Exception {
|
||||||
|
runTest("analysis/analysis-api/testData/standalone/multiModuleBinary/propertiesInCompanionObject_JvmStatic.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("propertiesInNamedCompanionObject.kt")
|
||||||
|
public void testPropertiesInNamedCompanionObject() throws Exception {
|
||||||
|
runTest("analysis/analysis-api/testData/standalone/multiModuleBinary/propertiesInNamedCompanionObject.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("propertiesInNamedCompanionObject_JvmField.kt")
|
||||||
|
public void testPropertiesInNamedCompanionObject_JvmField() throws Exception {
|
||||||
|
runTest("analysis/analysis-api/testData/standalone/multiModuleBinary/propertiesInNamedCompanionObject_JvmField.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("propertiesInNamedCompanionObject_JvmStatic.kt")
|
||||||
|
public void testPropertiesInNamedCompanionObject_JvmStatic() throws Exception {
|
||||||
|
runTest("analysis/analysis-api/testData/standalone/multiModuleBinary/propertiesInNamedCompanionObject_JvmStatic.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("propertiesInObject.kt")
|
||||||
|
public void testPropertiesInObject() throws Exception {
|
||||||
|
runTest("analysis/analysis-api/testData/standalone/multiModuleBinary/propertiesInObject.kt");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
// MODULE: lib
|
||||||
|
|
||||||
|
// FILE: some/Dependency.kt
|
||||||
|
package some
|
||||||
|
|
||||||
|
interface Flag<T>
|
||||||
|
|
||||||
|
class Dependency {
|
||||||
|
companion object {
|
||||||
|
@JvmField val JVM_FIELD_FLAG: Flag<*> = TODO()
|
||||||
|
@JvmStatic val JVM_STATIC_FLAG: Flag<*> = TODO()
|
||||||
|
val VAL_FLAG: Flag<*> = TODO()
|
||||||
|
var varFlag: Flag<*> = TODO()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val DEPENDENCY_TOP_LEVEL_VAL_FLAG: Flag<*> = TODO()
|
||||||
|
|
||||||
|
// MODULE: app
|
||||||
|
// DEPENDENCY: lib Binary
|
||||||
|
// FILE: main.kt
|
||||||
|
|
||||||
|
package some
|
||||||
|
|
||||||
|
private fun consumeFlag(p: Flag<*>) {
|
||||||
|
println(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
consumeFlag(Dependency.JVM_FIELD_FLAG)
|
||||||
|
consumeFlag(Dependency.JVM_STATIC_FLAG)
|
||||||
|
consumeFlag(Dependency.VAL_<caret>FLAG)
|
||||||
|
consumeFlag(Dependency.varFlag)
|
||||||
|
consumeFlag(DEPENDENCY_TOP_LEVEL_VAL_FLAG)
|
||||||
|
}
|
||||||
Vendored
+2
@@ -0,0 +1,2 @@
|
|||||||
|
Resolved to:
|
||||||
|
PsiField:VAL_FLAG
|
||||||
Vendored
+35
@@ -0,0 +1,35 @@
|
|||||||
|
// MODULE: lib
|
||||||
|
|
||||||
|
// FILE: some/Dependency.kt
|
||||||
|
package some
|
||||||
|
|
||||||
|
interface Flag<T>
|
||||||
|
|
||||||
|
class Dependency {
|
||||||
|
companion object {
|
||||||
|
@JvmField val JVM_FIELD_FLAG: Flag<*> = TODO()
|
||||||
|
@JvmStatic val JVM_STATIC_FLAG: Flag<*> = TODO()
|
||||||
|
val VAL_FLAG: Flag<*> = TODO()
|
||||||
|
var varFlag: Flag<*> = TODO()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val DEPENDENCY_TOP_LEVEL_VAL_FLAG: Flag<*> = TODO()
|
||||||
|
|
||||||
|
// MODULE: app
|
||||||
|
// DEPENDENCY: lib Binary
|
||||||
|
// FILE: main.kt
|
||||||
|
|
||||||
|
package some
|
||||||
|
|
||||||
|
private fun consumeFlag(p: Flag<*>) {
|
||||||
|
println(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
consumeFlag(Dependency.JVM_<caret>FIELD_FLAG)
|
||||||
|
consumeFlag(Dependency.JVM_STATIC_FLAG)
|
||||||
|
consumeFlag(Dependency.VAL_FLAG)
|
||||||
|
consumeFlag(Dependency.varFlag)
|
||||||
|
consumeFlag(DEPENDENCY_TOP_LEVEL_VAL_FLAG)
|
||||||
|
}
|
||||||
analysis/analysis-api/testData/standalone/multiModuleBinary/propertiesInCompanionObject_JvmField.txt
Vendored
+2
@@ -0,0 +1,2 @@
|
|||||||
|
Resolved to:
|
||||||
|
PsiField:JVM_FIELD_FLAG
|
||||||
analysis/analysis-api/testData/standalone/multiModuleBinary/propertiesInCompanionObject_JvmStatic.kt
Vendored
+35
@@ -0,0 +1,35 @@
|
|||||||
|
// MODULE: lib
|
||||||
|
|
||||||
|
// FILE: some/Dependency.kt
|
||||||
|
package some
|
||||||
|
|
||||||
|
interface Flag<T>
|
||||||
|
|
||||||
|
class Dependency {
|
||||||
|
companion object {
|
||||||
|
@JvmField val JVM_FIELD_FLAG: Flag<*> = TODO()
|
||||||
|
@JvmStatic val JVM_STATIC_FLAG: Flag<*> = TODO()
|
||||||
|
val VAL_FLAG: Flag<*> = TODO()
|
||||||
|
var varFlag: Flag<*> = TODO()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val DEPENDENCY_TOP_LEVEL_VAL_FLAG: Flag<*> = TODO()
|
||||||
|
|
||||||
|
// MODULE: app
|
||||||
|
// DEPENDENCY: lib Binary
|
||||||
|
// FILE: main.kt
|
||||||
|
|
||||||
|
package some
|
||||||
|
|
||||||
|
private fun consumeFlag(p: Flag<*>) {
|
||||||
|
println(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
consumeFlag(Dependency.JVM_FIELD_FLAG)
|
||||||
|
consumeFlag(Dependency.JVM_<caret>STATIC_FLAG)
|
||||||
|
consumeFlag(Dependency.VAL_FLAG)
|
||||||
|
consumeFlag(Dependency.varFlag)
|
||||||
|
consumeFlag(DEPENDENCY_TOP_LEVEL_VAL_FLAG)
|
||||||
|
}
|
||||||
+2
@@ -0,0 +1,2 @@
|
|||||||
|
Resolved to:
|
||||||
|
PsiField:JVM_STATIC_FLAG
|
||||||
Vendored
+35
@@ -0,0 +1,35 @@
|
|||||||
|
// MODULE: lib
|
||||||
|
|
||||||
|
// FILE: some/Dependency.kt
|
||||||
|
package some
|
||||||
|
|
||||||
|
interface Flag<T>
|
||||||
|
|
||||||
|
class Dependency {
|
||||||
|
companion object Named {
|
||||||
|
@JvmField val JVM_FIELD_FLAG: Flag<*> = TODO()
|
||||||
|
@JvmStatic val JVM_STATIC_FLAG: Flag<*> = TODO()
|
||||||
|
val VAL_FLAG: Flag<*> = TODO()
|
||||||
|
var varFlag: Flag<*> = TODO()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val DEPENDENCY_TOP_LEVEL_VAL_FLAG: Flag<*> = TODO()
|
||||||
|
|
||||||
|
// MODULE: app
|
||||||
|
// DEPENDENCY: lib Binary
|
||||||
|
// FILE: main.kt
|
||||||
|
|
||||||
|
package some
|
||||||
|
|
||||||
|
private fun consumeFlag(p: Flag<*>) {
|
||||||
|
println(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
consumeFlag(Dependency.JVM_FIELD_FLAG)
|
||||||
|
consumeFlag(Dependency.JVM_STATIC_FLAG)
|
||||||
|
consumeFlag(Dependency.VAL_<caret>FLAG)
|
||||||
|
consumeFlag(Dependency.varFlag)
|
||||||
|
consumeFlag(DEPENDENCY_TOP_LEVEL_VAL_FLAG)
|
||||||
|
}
|
||||||
Vendored
+2
@@ -0,0 +1,2 @@
|
|||||||
|
Resolved to:
|
||||||
|
PsiField:VAL_FLAG
|
||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
// MODULE: lib
|
||||||
|
|
||||||
|
// FILE: some/Dependency.kt
|
||||||
|
package some
|
||||||
|
|
||||||
|
interface Flag<T>
|
||||||
|
|
||||||
|
class Dependency {
|
||||||
|
companion object Named {
|
||||||
|
@JvmField val JVM_FIELD_FLAG: Flag<*> = TODO()
|
||||||
|
@JvmStatic val JVM_STATIC_FLAG: Flag<*> = TODO()
|
||||||
|
val VAL_FLAG: Flag<*> = TODO()
|
||||||
|
var varFlag: Flag<*> = TODO()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val DEPENDENCY_TOP_LEVEL_VAL_FLAG: Flag<*> = TODO()
|
||||||
|
|
||||||
|
// MODULE: app
|
||||||
|
// DEPENDENCY: lib Binary
|
||||||
|
// FILE: main.kt
|
||||||
|
|
||||||
|
package some
|
||||||
|
|
||||||
|
private fun consumeFlag(p: Flag<*>) {
|
||||||
|
println(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
consumeFlag(Dependency.JVM_<caret>FIELD_FLAG)
|
||||||
|
consumeFlag(Dependency.JVM_STATIC_FLAG)
|
||||||
|
consumeFlag(Dependency.VAL_FLAG)
|
||||||
|
consumeFlag(Dependency.varFlag)
|
||||||
|
consumeFlag(DEPENDENCY_TOP_LEVEL_VAL_FLAG)
|
||||||
|
}
|
||||||
+2
@@ -0,0 +1,2 @@
|
|||||||
|
Resolved to:
|
||||||
|
PsiField:JVM_FIELD_FLAG
|
||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
// MODULE: lib
|
||||||
|
|
||||||
|
// FILE: some/Dependency.kt
|
||||||
|
package some
|
||||||
|
|
||||||
|
interface Flag<T>
|
||||||
|
|
||||||
|
class Dependency {
|
||||||
|
companion object Named {
|
||||||
|
@JvmField val JVM_FIELD_FLAG: Flag<*> = TODO()
|
||||||
|
@JvmStatic val JVM_STATIC_FLAG: Flag<*> = TODO()
|
||||||
|
val VAL_FLAG: Flag<*> = TODO()
|
||||||
|
var varFlag: Flag<*> = TODO()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val DEPENDENCY_TOP_LEVEL_VAL_FLAG: Flag<*> = TODO()
|
||||||
|
|
||||||
|
// MODULE: app
|
||||||
|
// DEPENDENCY: lib Binary
|
||||||
|
// FILE: main.kt
|
||||||
|
|
||||||
|
package some
|
||||||
|
|
||||||
|
private fun consumeFlag(p: Flag<*>) {
|
||||||
|
println(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
consumeFlag(Dependency.JVM_FIELD_FLAG)
|
||||||
|
consumeFlag(Dependency.JVM_<caret>STATIC_FLAG)
|
||||||
|
consumeFlag(Dependency.VAL_FLAG)
|
||||||
|
consumeFlag(Dependency.varFlag)
|
||||||
|
consumeFlag(DEPENDENCY_TOP_LEVEL_VAL_FLAG)
|
||||||
|
}
|
||||||
+2
@@ -0,0 +1,2 @@
|
|||||||
|
Resolved to:
|
||||||
|
PsiField:JVM_STATIC_FLAG
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
// MODULE: lib
|
||||||
|
|
||||||
|
// FILE: some/DependencyObject.kt
|
||||||
|
package some
|
||||||
|
|
||||||
|
interface Flag<T>
|
||||||
|
|
||||||
|
object DependencyObject {
|
||||||
|
val VAL_FLAG: Flag<*> = TODO()
|
||||||
|
var varFlag: Flag<*> = TODO()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MODULE: app
|
||||||
|
// DEPENDENCY: lib Binary
|
||||||
|
// FILE: main.kt
|
||||||
|
|
||||||
|
package some
|
||||||
|
|
||||||
|
private fun consumeFlag(p: Flag<*>) {
|
||||||
|
println(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
consumeFlag(DependencyObject.VAL_FLAG)
|
||||||
|
consumeFlag(DependencyObject.var<caret>Flag)
|
||||||
|
}
|
||||||
+2
@@ -0,0 +1,2 @@
|
|||||||
|
Resolved to:
|
||||||
|
PsiField:varFlag
|
||||||
+9
-3
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.analysis.providers
|
|||||||
|
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.psi.PsiField
|
||||||
import com.intellij.psi.search.GlobalSearchScope
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||||
import org.jetbrains.kotlin.analysis.api.symbols.*
|
import org.jetbrains.kotlin.analysis.api.symbols.*
|
||||||
@@ -58,10 +59,15 @@ object DecompiledPsiDeclarationProvider {
|
|||||||
project: Project
|
project: Project
|
||||||
): PsiElement? {
|
): PsiElement? {
|
||||||
return variableLikeSymbol.callableIdIfNonLocal?.let {
|
return variableLikeSymbol.callableIdIfNonLocal?.let {
|
||||||
project.createPsiDeclarationProvider(variableLikeSymbol.scope(project))
|
val candidates = project.createPsiDeclarationProvider(variableLikeSymbol.scope(project))
|
||||||
?.getProperties(it)
|
?.getProperties(it)
|
||||||
// TODO: needs to pick field/getter/setter accordingly?
|
if (candidates?.size == 1)
|
||||||
?.firstOrNull()
|
candidates.single()
|
||||||
|
else {
|
||||||
|
// Weigh [PsiField]
|
||||||
|
candidates?.firstOrNull { psiMember -> psiMember is PsiField }
|
||||||
|
?: candidates?.firstOrNull()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+18
-7
@@ -91,10 +91,12 @@ private class KotlinStaticPsiDeclarationFromBinaryModuleProvider(
|
|||||||
return listOfNotNull(javaFileManager.findClass(classId.asFqNameString(), scope))
|
return listOfNotNull(javaFileManager.findClass(classId.asFqNameString(), scope))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(dimonchik0036): support 'is' accessor
|
|
||||||
override fun getProperties(callableId: CallableId): Collection<PsiMember> {
|
override fun getProperties(callableId: CallableId): Collection<PsiMember> {
|
||||||
val classes = callableId.classId?.let { classId ->
|
val classes = callableId.classId?.let { classId ->
|
||||||
getClassesByClassId(classId)
|
val classFromCurrentClassId = getClassesByClassId(classId)
|
||||||
|
// property in companion object is actually materialized at the containing class.
|
||||||
|
val classFromOuterClassID = classId.outerClassId?.let { getClassesByClassId(it) } ?: emptyList()
|
||||||
|
classFromCurrentClassId + classFromOuterClassID
|
||||||
} ?: clsClassImplsInPackage(callableId.packageName)
|
} ?: clsClassImplsInPackage(callableId.packageName)
|
||||||
return classes.flatMap { psiClass ->
|
return classes.flatMap { psiClass ->
|
||||||
psiClass.children
|
psiClass.children
|
||||||
@@ -102,17 +104,26 @@ private class KotlinStaticPsiDeclarationFromBinaryModuleProvider(
|
|||||||
.filter { psiMember ->
|
.filter { psiMember ->
|
||||||
if (psiMember !is PsiMethod && psiMember !is PsiField) return@filter false
|
if (psiMember !is PsiMethod && psiMember !is PsiField) return@filter false
|
||||||
val name = psiMember.name ?: return@filter false
|
val name = psiMember.name ?: return@filter false
|
||||||
|
val id = callableId.callableName.identifier
|
||||||
// PsiField a.k.a. backing field
|
// PsiField a.k.a. backing field
|
||||||
name == callableId.callableName.identifier ||
|
if (name == id) return@filter true
|
||||||
// PsiMethod, i.e., accessors
|
// PsiMethod, i.e., accessors
|
||||||
(name.startsWith("get") || name.startsWith("set")) &&
|
val nameWithoutPrefix = name.nameWithoutAccessorPrefix ?: return@filter false
|
||||||
|
// E.g., getJVM_FIELD -> JVM_FIELD
|
||||||
|
nameWithoutPrefix == id ||
|
||||||
// E.g., getFooBar -> FooBar -> fooBar
|
// E.g., getFooBar -> FooBar -> fooBar
|
||||||
(name.substring(3).decapitalizeSmart().endsWith(callableId.callableName.identifier))
|
nameWithoutPrefix.decapitalizeSmart().endsWith(id)
|
||||||
|
|
||||||
}
|
}
|
||||||
}.toList()
|
}.toList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val String.nameWithoutAccessorPrefix: String?
|
||||||
|
get() = when {
|
||||||
|
this.startsWith("get") || this.startsWith("set") -> substring(3)
|
||||||
|
this.startsWith("is") -> substring(2)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
override fun getFunctions(callableId: CallableId): Collection<PsiMethod> {
|
override fun getFunctions(callableId: CallableId): Collection<PsiMethod> {
|
||||||
val classes = callableId.classId?.let { classId ->
|
val classes = callableId.classId?.let { classId ->
|
||||||
getClassesByClassId(classId)
|
getClassesByClassId(classId)
|
||||||
|
|||||||
Reference in New Issue
Block a user