FIR IDE: resolve arrayOf calls in annotations
This commit is contained in:
committed by
Ilya Kirillov
parent
068e81570d
commit
35409fdb8d
+65
-23
@@ -19,10 +19,7 @@ import org.jetbrains.kotlin.fir.references.impl.FirSimpleNamedReference
|
|||||||
import org.jetbrains.kotlin.fir.resolve.calls.FirErrorReferenceWithCandidate
|
import org.jetbrains.kotlin.fir.resolve.calls.FirErrorReferenceWithCandidate
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
|
||||||
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
|
import org.jetbrains.kotlin.fir.types.*
|
||||||
import org.jetbrains.kotlin.fir.types.ConeKotlinErrorType
|
|
||||||
import org.jetbrains.kotlin.fir.types.classId
|
|
||||||
import org.jetbrains.kotlin.fir.types.coneType
|
|
||||||
import org.jetbrains.kotlin.analysis.api.fir.getCandidateSymbols
|
import org.jetbrains.kotlin.analysis.api.fir.getCandidateSymbols
|
||||||
import org.jetbrains.kotlin.analysis.api.fir.isImplicitFunctionCall
|
import org.jetbrains.kotlin.analysis.api.fir.isImplicitFunctionCall
|
||||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getOrBuildFir
|
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getOrBuildFir
|
||||||
@@ -32,6 +29,10 @@ import org.jetbrains.kotlin.analysis.api.components.KtCallResolver
|
|||||||
import org.jetbrains.kotlin.analysis.api.diagnostics.KtNonBoundToPsiErrorDiagnostic
|
import org.jetbrains.kotlin.analysis.api.diagnostics.KtNonBoundToPsiErrorDiagnostic
|
||||||
import org.jetbrains.kotlin.analysis.api.fir.KtFirAnalysisSession
|
import org.jetbrains.kotlin.analysis.api.fir.KtFirAnalysisSession
|
||||||
import org.jetbrains.kotlin.analysis.api.fir.buildSymbol
|
import org.jetbrains.kotlin.analysis.api.fir.buildSymbol
|
||||||
|
import org.jetbrains.kotlin.analysis.api.fir.symbols.KtFirArrayOfSymbolProvider.arrayOf
|
||||||
|
import org.jetbrains.kotlin.analysis.api.fir.symbols.KtFirArrayOfSymbolProvider.arrayOfSymbol
|
||||||
|
import org.jetbrains.kotlin.analysis.api.fir.symbols.KtFirArrayOfSymbolProvider.arrayTypeToArrayOfCall
|
||||||
|
import org.jetbrains.kotlin.analysis.api.fir.symbols.KtFirFunctionSymbol
|
||||||
import org.jetbrains.kotlin.analysis.api.symbols.*
|
import org.jetbrains.kotlin.analysis.api.symbols.*
|
||||||
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithMembers
|
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithMembers
|
||||||
import org.jetbrains.kotlin.analysis.api.tokens.ValidityToken
|
import org.jetbrains.kotlin.analysis.api.tokens.ValidityToken
|
||||||
@@ -106,6 +107,7 @@ internal class KtFirCallResolver(
|
|||||||
|
|
||||||
override fun resolveCall(call: KtCallElement): KtCall? = withValidityAssertion {
|
override fun resolveCall(call: KtCallElement): KtCall? = withValidityAssertion {
|
||||||
return when (val fir = call.getOrBuildFir(firResolveState)) {
|
return when (val fir = call.getOrBuildFir(firResolveState)) {
|
||||||
|
is FirArrayOfCall -> resolveArrayOfCall(fir)
|
||||||
is FirFunctionCall -> resolveCall(fir)
|
is FirFunctionCall -> resolveCall(fir)
|
||||||
is FirAnnotationCall -> fir.asAnnotationCall()
|
is FirAnnotationCall -> fir.asAnnotationCall()
|
||||||
is FirDelegatedConstructorCall -> fir.asDelegatedConstructorCall()
|
is FirDelegatedConstructorCall -> fir.asDelegatedConstructorCall()
|
||||||
@@ -122,6 +124,25 @@ internal class KtFirCallResolver(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun resolveArrayOfCall(arrayOfCall: FirArrayOfCall): KtCall? {
|
||||||
|
val arrayOfSymbol = with(analysisSession) {
|
||||||
|
val type = arrayOfCall.typeRef.coneTypeSafe<ConeClassLikeType>()
|
||||||
|
?: return run {
|
||||||
|
val defaultArrayOfSymbol = arrayOfSymbol(arrayOf) ?: return null
|
||||||
|
KtFunctionCall(
|
||||||
|
arrayOfCall.createArgumentMapping(defaultArrayOfSymbol),
|
||||||
|
KtErrorCallTarget(
|
||||||
|
listOf(defaultArrayOfSymbol),
|
||||||
|
KtNonBoundToPsiErrorDiagnostic(factoryName = null, "type of arrayOf call is not resolved", token)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val call = arrayTypeToArrayOfCall[type.lookupTag.classId] ?: arrayOf
|
||||||
|
arrayOfSymbol(call)
|
||||||
|
} ?: return null
|
||||||
|
return KtFunctionCall(arrayOfCall.createArgumentMapping(arrayOfSymbol), KtSuccessCallTarget(arrayOfSymbol))
|
||||||
|
}
|
||||||
|
|
||||||
private fun resolveCall(firCall: FirFunctionCall): KtCall? {
|
private fun resolveCall(firCall: FirFunctionCall): KtCall? {
|
||||||
val session = firResolveState.rootModuleSession
|
val session = firResolveState.rootModuleSession
|
||||||
return when {
|
return when {
|
||||||
@@ -210,35 +231,56 @@ internal class KtFirCallResolver(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun FirExpression.findSourceKtExpressionForCallArgument(): KtExpression? {
|
||||||
|
// For spread, named, and lambda arguments, the source is the KtValueArgument.
|
||||||
|
// For other arguments (including array indices), the source is the KtExpression.
|
||||||
|
return when (this) {
|
||||||
|
is FirNamedArgumentExpression, is FirSpreadArgumentExpression, is FirLambdaArgumentExpression ->
|
||||||
|
realPsi.safeAs<KtValueArgument>()?.getArgumentExpression()
|
||||||
|
else -> realPsi as? KtExpression
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun mapArgumentExpressionToParameter(
|
||||||
|
argumentExpression: FirExpression,
|
||||||
|
parameterSymbol: KtValueParameterSymbol,
|
||||||
|
argumentMapping: LinkedHashMap<KtExpression, KtValueParameterSymbol>
|
||||||
|
) {
|
||||||
|
if (argumentExpression is FirVarargArgumentsExpression) {
|
||||||
|
for (varargArgument in argumentExpression.arguments) {
|
||||||
|
val valueArgument = varargArgument.findSourceKtExpressionForCallArgument() ?: return
|
||||||
|
argumentMapping[valueArgument] = parameterSymbol
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val valueArgument = argumentExpression.findSourceKtExpressionForCallArgument() ?: return
|
||||||
|
argumentMapping[valueArgument] = parameterSymbol
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun FirCall.createArgumentMapping(): LinkedHashMap<KtExpression, KtValueParameterSymbol> {
|
private fun FirCall.createArgumentMapping(): LinkedHashMap<KtExpression, KtValueParameterSymbol> {
|
||||||
val ktArgumentMapping = LinkedHashMap<KtExpression, KtValueParameterSymbol>()
|
val ktArgumentMapping = LinkedHashMap<KtExpression, KtValueParameterSymbol>()
|
||||||
argumentMapping?.let {
|
argumentMapping?.let {
|
||||||
fun FirExpression.findKtExpression(): KtExpression? {
|
|
||||||
// For spread, named, and lambda arguments, the source is the KtValueArgument.
|
|
||||||
// For other arguments (including array indices), the source is the KtExpression.
|
|
||||||
return when (this) {
|
|
||||||
is FirNamedArgumentExpression, is FirSpreadArgumentExpression, is FirLambdaArgumentExpression ->
|
|
||||||
realPsi.safeAs<KtValueArgument>()?.getArgumentExpression()
|
|
||||||
else -> realPsi as? KtExpression
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for ((firExpression, firValueParameter) in it.entries) {
|
for ((firExpression, firValueParameter) in it.entries) {
|
||||||
val parameterSymbol = firValueParameter.buildSymbol(firSymbolBuilder) as KtValueParameterSymbol
|
val parameterSymbol = firValueParameter.buildSymbol(firSymbolBuilder) as KtValueParameterSymbol
|
||||||
if (firExpression is FirVarargArgumentsExpression) {
|
mapArgumentExpressionToParameter(firExpression, parameterSymbol, ktArgumentMapping)
|
||||||
for (varargArgument in firExpression.arguments) {
|
|
||||||
val valueArgument = varargArgument.findKtExpression() ?: continue
|
|
||||||
ktArgumentMapping[valueArgument] = parameterSymbol
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
val valueArgument = firExpression.findKtExpression() ?: continue
|
|
||||||
ktArgumentMapping[valueArgument] = parameterSymbol
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ktArgumentMapping
|
return ktArgumentMapping
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun FirArrayOfCall.createArgumentMapping(
|
||||||
|
arrayOfCallSymbol: KtFirFunctionSymbol
|
||||||
|
): LinkedHashMap<KtExpression, KtValueParameterSymbol> {
|
||||||
|
val ktArgumentMapping = LinkedHashMap<KtExpression, KtValueParameterSymbol>()
|
||||||
|
val parameterSymbol = arrayOfCallSymbol.firRef.withFir {
|
||||||
|
it.valueParameters.single().buildSymbol(firSymbolBuilder) as KtValueParameterSymbol
|
||||||
|
}
|
||||||
|
for (firExpression in argumentList.arguments) {
|
||||||
|
mapArgumentExpressionToParameter(firExpression, parameterSymbol, ktArgumentMapping)
|
||||||
|
}
|
||||||
|
return ktArgumentMapping
|
||||||
|
}
|
||||||
|
|
||||||
private fun FirErrorNamedReference.createErrorCallTarget(qualifiedAccessSource: FirSourceElement?): KtErrorCallTarget =
|
private fun FirErrorNamedReference.createErrorCallTarget(qualifiedAccessSource: FirSourceElement?): KtErrorCallTarget =
|
||||||
KtErrorCallTarget(
|
KtErrorCallTarget(
|
||||||
getCandidateSymbols().mapNotNull { it.fir.buildSymbol(firSymbolBuilder) as? KtFunctionLikeSymbol },
|
getCandidateSymbols().mapNotNull { it.fir.buildSymbol(firSymbolBuilder) as? KtFunctionLikeSymbol },
|
||||||
|
|||||||
+8
-33
@@ -5,20 +5,17 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.references
|
package org.jetbrains.kotlin.idea.references
|
||||||
|
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
|
|
||||||
import org.jetbrains.kotlin.fir.expressions.FirArrayOfCall
|
|
||||||
import org.jetbrains.kotlin.fir.resolve.symbolProvider
|
|
||||||
import org.jetbrains.kotlin.name.StandardClassIds
|
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
|
|
||||||
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
|
|
||||||
import org.jetbrains.kotlin.fir.types.coneTypeSafe
|
|
||||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getOrBuildFirSafe
|
|
||||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||||
import org.jetbrains.kotlin.analysis.api.fir.KtFirAnalysisSession
|
import org.jetbrains.kotlin.analysis.api.fir.KtFirAnalysisSession
|
||||||
|
import org.jetbrains.kotlin.analysis.api.fir.symbols.KtFirArrayOfSymbolProvider.arrayOf
|
||||||
|
import org.jetbrains.kotlin.analysis.api.fir.symbols.KtFirArrayOfSymbolProvider.arrayOfSymbol
|
||||||
|
import org.jetbrains.kotlin.analysis.api.fir.symbols.KtFirArrayOfSymbolProvider.arrayTypeToArrayOfCall
|
||||||
import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol
|
import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol
|
||||||
import org.jetbrains.kotlin.name.ClassId
|
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getOrBuildFirSafe
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.fir.expressions.FirArrayOfCall
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
|
||||||
|
import org.jetbrains.kotlin.fir.types.coneTypeSafe
|
||||||
|
|
||||||
import org.jetbrains.kotlin.psi.KtCollectionLiteralExpression
|
import org.jetbrains.kotlin.psi.KtCollectionLiteralExpression
|
||||||
|
|
||||||
class KtFirCollectionLiteralReference(
|
class KtFirCollectionLiteralReference(
|
||||||
@@ -31,26 +28,4 @@ class KtFirCollectionLiteralReference(
|
|||||||
val call = arrayTypeToArrayOfCall[type.lookupTag.classId] ?: arrayOf
|
val call = arrayTypeToArrayOfCall[type.lookupTag.classId] ?: arrayOf
|
||||||
return listOfNotNull(arrayOfSymbol(call))
|
return listOfNotNull(arrayOfSymbol(call))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun KtFirAnalysisSession.arrayOfSymbol(identifier: Name): KtSymbol? {
|
|
||||||
val fir = firResolveState.rootModuleSession.symbolProvider.getTopLevelCallableSymbols(kotlinPackage, identifier).firstOrNull {
|
|
||||||
/* choose (for byte array)
|
|
||||||
* public fun byteArrayOf(vararg elements: kotlin.Byte): kotlin.ByteArray
|
|
||||||
*/
|
|
||||||
(it as? FirFunctionSymbol<*>)?.fir?.valueParameters?.singleOrNull()?.isVararg == true
|
|
||||||
}?.fir as? FirSimpleFunction ?: return null
|
|
||||||
return firSymbolBuilder.functionLikeBuilder.buildFunctionSymbol(fir)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val kotlinPackage = FqName("kotlin")
|
|
||||||
private val arrayOf = Name.identifier("arrayOf")
|
|
||||||
private val arrayTypeToArrayOfCall = run {
|
|
||||||
StandardClassIds.primitiveArrayTypeByElementType.values + StandardClassIds.unsignedArrayTypeByElementType.values
|
|
||||||
}.associateWith { it.correspondingArrayOfCallFqName() }
|
|
||||||
|
|
||||||
private fun ClassId.correspondingArrayOfCallFqName(): Name =
|
|
||||||
Name.identifier("${shortClassName.identifier.replaceFirstChar(Char::lowercaseChar)}Of")
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+36
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* 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.kotlin.analysis.api.fir.symbols
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.analysis.api.fir.KtFirAnalysisSession
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.symbolProvider
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
|
||||||
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import org.jetbrains.kotlin.name.StandardClassIds
|
||||||
|
|
||||||
|
object KtFirArrayOfSymbolProvider {
|
||||||
|
internal fun KtFirAnalysisSession.arrayOfSymbol(identifier: Name): KtFirFunctionSymbol? {
|
||||||
|
val fir = firResolveState.rootModuleSession.symbolProvider.getTopLevelCallableSymbols(kotlinPackage, identifier).firstOrNull {
|
||||||
|
/* choose (for byte array)
|
||||||
|
* public fun byteArrayOf(vararg elements: kotlin.Byte): kotlin.ByteArray
|
||||||
|
*/
|
||||||
|
(it as? FirFunctionSymbol<*>)?.fir?.valueParameters?.singleOrNull()?.isVararg == true
|
||||||
|
}?.fir as? FirSimpleFunction ?: return null
|
||||||
|
return firSymbolBuilder.functionLikeBuilder.buildFunctionSymbol(fir)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val kotlinPackage = FqName("kotlin")
|
||||||
|
internal val arrayOf = Name.identifier("arrayOf")
|
||||||
|
internal val arrayTypeToArrayOfCall = run {
|
||||||
|
StandardClassIds.primitiveArrayTypeByElementType.values + StandardClassIds.unsignedArrayTypeByElementType.values
|
||||||
|
}.associateWith { it.correspondingArrayOfCallFqName() }
|
||||||
|
|
||||||
|
private fun ClassId.correspondingArrayOfCallFqName(): Name =
|
||||||
|
Name.identifier("${shortClassName.identifier.replaceFirstChar(Char::lowercaseChar)}Of")
|
||||||
|
}
|
||||||
+12
@@ -24,6 +24,12 @@ public class ResolveCallTestGenerated extends AbstractResolveCallTest {
|
|||||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/analysisSession/resolveCall"), Pattern.compile("^(.+)\\.kt$"), null, true);
|
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/analysisSession/resolveCall"), Pattern.compile("^(.+)\\.kt$"), null, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("arrayOfInAnnotation.kt")
|
||||||
|
public void testArrayOfInAnnotation() throws Exception {
|
||||||
|
runTest("analysis/analysis-api/testData/analysisSession/resolveCall/arrayOfInAnnotation.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("delegatedConstructorCall_super.kt")
|
@TestMetadata("delegatedConstructorCall_super.kt")
|
||||||
public void testDelegatedConstructorCall_super() throws Exception {
|
public void testDelegatedConstructorCall_super() throws Exception {
|
||||||
@@ -150,6 +156,12 @@ public class ResolveCallTestGenerated extends AbstractResolveCallTest {
|
|||||||
runTest("analysis/analysis-api/testData/analysisSession/resolveCall/indexedSetWithTooManyArgs.kt");
|
runTest("analysis/analysis-api/testData/analysisSession/resolveCall/indexedSetWithTooManyArgs.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("intArrayOfInAnnotation.kt")
|
||||||
|
public void testIntArrayOfInAnnotation() throws Exception {
|
||||||
|
runTest("analysis/analysis-api/testData/analysisSession/resolveCall/intArrayOfInAnnotation.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("javaFunctionCall.kt")
|
@TestMetadata("javaFunctionCall.kt")
|
||||||
public void testJavaFunctionCall() throws Exception {
|
public void testJavaFunctionCall() throws Exception {
|
||||||
|
|||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
annotation class RequiresPermission(val anyOf: IntArray)
|
||||||
|
|
||||||
|
@RequiresPermission(anyOf = <expr>arrayOf(1, 2, 3)</expr>)
|
||||||
|
fun foo(): Int = 5
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
KtFunctionCall:
|
||||||
|
argumentMapping = { 1 -> (vararg elements: T), 2 -> (vararg elements: T), 3 -> (vararg elements: T) }
|
||||||
|
targetFunction = kotlin/arrayOf(vararg elements: T): kotlin.Array<T>
|
||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
annotation class RequiresPermission(val anyOf: IntArray)
|
||||||
|
|
||||||
|
@RequiresPermission(anyOf = <expr>intArrayOf(1, 2, 3)</expr>)
|
||||||
|
fun foo(): Int = 5
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
KtFunctionCall:
|
||||||
|
argumentMapping = { 1 -> (vararg elements: kotlin.Int), 2 -> (vararg elements: kotlin.Int), 3 -> (vararg elements: kotlin.Int) }
|
||||||
|
targetFunction = kotlin/intArrayOf(vararg elements: kotlin.Int): kotlin.IntArray
|
||||||
Reference in New Issue
Block a user