[FIR IDE] Add a special type of KtCall for generic type qualifiers
^KTIJ-21672 Fixed
This commit is contained in:
+22
-1
@@ -30,6 +30,8 @@ import org.jetbrains.kotlin.diagnostics.Errors
|
|||||||
import org.jetbrains.kotlin.diagnostics.Severity
|
import org.jetbrains.kotlin.diagnostics.Severity
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.getPossiblyQualifiedCallExpression
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelector
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.DelegatingBindingTrace
|
import org.jetbrains.kotlin.resolve.DelegatingBindingTrace
|
||||||
@@ -156,7 +158,9 @@ internal class KtFe10CallResolver(
|
|||||||
handleAsCheckNotNullCall(unwrappedPsi)?.let { return@with it }
|
handleAsCheckNotNullCall(unwrappedPsi)?.let { return@with it }
|
||||||
handleAsFunctionCall(this, unwrappedPsi)
|
handleAsFunctionCall(this, unwrappedPsi)
|
||||||
}
|
}
|
||||||
else -> handleAsFunctionCall(this, unwrappedPsi) ?: handleAsPropertyRead(this, unwrappedPsi)
|
else -> handleAsFunctionCall(this, unwrappedPsi)
|
||||||
|
?: handleAsPropertyRead(this, unwrappedPsi)
|
||||||
|
?: handleAsGenericTypeQualifier(unwrappedPsi)
|
||||||
} ?: handleResolveErrors(this, psi)
|
} ?: handleResolveErrors(this, psi)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -385,6 +389,23 @@ internal class KtFe10CallResolver(
|
|||||||
return call.toPropertyRead(context)?.let { createCallInfo(context, element, it, listOf(call)) }
|
return call.toPropertyRead(context)?.let { createCallInfo(context, element, it, listOf(call)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles call expressions like `Foo<Bar>` or `test.Foo<Bar>` in calls like `Foo<Bar>::foo` and `test.Foo<Bar>::foo`.
|
||||||
|
*
|
||||||
|
* ATM does not perform any resolve checks, since it does not seem possible with [BindingContext], so it might give some
|
||||||
|
* false positives.
|
||||||
|
*/
|
||||||
|
private fun handleAsGenericTypeQualifier(element: KtElement): KtCallInfo? {
|
||||||
|
if (element !is KtExpression) return null
|
||||||
|
|
||||||
|
val wholeQualifier = element.getQualifiedExpressionForSelector() as? KtDotQualifiedExpression ?: element
|
||||||
|
|
||||||
|
val call = wholeQualifier.getPossiblyQualifiedCallExpression() ?: return null
|
||||||
|
if (call.typeArgumentList == null || call.valueArgumentList != null) return null
|
||||||
|
|
||||||
|
return KtSuccessCallInfo(KtGenericTypeQualifier(token, wholeQualifier))
|
||||||
|
}
|
||||||
|
|
||||||
private fun ResolvedCall<*>.toPropertyRead(context: BindingContext): KtVariableAccessCall? {
|
private fun ResolvedCall<*>.toPropertyRead(context: BindingContext): KtVariableAccessCall? {
|
||||||
val partiallyAppliedSymbol = toPartiallyAppliedVariableSymbol(context) ?: return null
|
val partiallyAppliedSymbol = toPartiallyAppliedVariableSymbol(context) ?: return null
|
||||||
return KtSimpleVariableAccessCall(partiallyAppliedSymbol, KtSimpleVariableAccess.Read)
|
return KtSimpleVariableAccessCall(partiallyAppliedSymbol, KtSimpleVariableAccess.Read)
|
||||||
|
|||||||
+12
@@ -214,6 +214,18 @@ public class Fe10IdeNormalAnalysisSourceModuleResolveCallTestGenerated extends A
|
|||||||
runTest("analysis/analysis-api/testData/components/callResolver/resolveCall/builderInference.kt");
|
runTest("analysis/analysis-api/testData/components/callResolver/resolveCall/builderInference.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("callableReference_genericQualifier1.kt")
|
||||||
|
public void testCallableReference_genericQualifier1() throws Exception {
|
||||||
|
runTest("analysis/analysis-api/testData/components/callResolver/resolveCall/callableReference_genericQualifier1.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("callableReference_genericQualifier2.kt")
|
||||||
|
public void testCallableReference_genericQualifier2() throws Exception {
|
||||||
|
runTest("analysis/analysis-api/testData/components/callResolver/resolveCall/callableReference_genericQualifier2.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("calleeExpressionOfImplicitInvoke.kt")
|
@TestMetadata("calleeExpressionOfImplicitInvoke.kt")
|
||||||
public void testCalleeExpressionOfImplicitInvoke() throws Exception {
|
public void testCalleeExpressionOfImplicitInvoke() throws Exception {
|
||||||
|
|||||||
+19
-2
@@ -22,7 +22,6 @@ import org.jetbrains.kotlin.analysis.api.types.KtSubstitutor
|
|||||||
import org.jetbrains.kotlin.analysis.api.lifetime.withValidityAssertion
|
import org.jetbrains.kotlin.analysis.api.lifetime.withValidityAssertion
|
||||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getOrBuildFir
|
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getOrBuildFir
|
||||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.resolver.AllCandidatesResolver
|
import org.jetbrains.kotlin.analysis.low.level.api.fir.resolver.AllCandidatesResolver
|
||||||
import org.jetbrains.kotlin.diagnostics.KtDiagnostic
|
|
||||||
import org.jetbrains.kotlin.fir.FirElement
|
import org.jetbrains.kotlin.fir.FirElement
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.toRegularClassSymbol
|
import org.jetbrains.kotlin.fir.analysis.checkers.toRegularClassSymbol
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirValueParameter
|
import org.jetbrains.kotlin.fir.declarations.FirValueParameter
|
||||||
@@ -35,7 +34,6 @@ import org.jetbrains.kotlin.fir.references.FirErrorNamedReference
|
|||||||
import org.jetbrains.kotlin.fir.references.FirNamedReference
|
import org.jetbrains.kotlin.fir.references.FirNamedReference
|
||||||
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
||||||
import org.jetbrains.kotlin.fir.references.FirSuperReference
|
import org.jetbrains.kotlin.fir.references.FirSuperReference
|
||||||
import org.jetbrains.kotlin.fir.resolve.ScopeSession
|
|
||||||
import org.jetbrains.kotlin.fir.resolve.calls.AbstractCandidate
|
import org.jetbrains.kotlin.fir.resolve.calls.AbstractCandidate
|
||||||
import org.jetbrains.kotlin.fir.resolve.calls.Candidate
|
import org.jetbrains.kotlin.fir.resolve.calls.Candidate
|
||||||
import org.jetbrains.kotlin.fir.resolve.createConeDiagnosticForCandidateWithError
|
import org.jetbrains.kotlin.fir.resolve.createConeDiagnosticForCandidateWithError
|
||||||
@@ -55,6 +53,7 @@ import org.jetbrains.kotlin.lexer.KtTokens
|
|||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.KtPsiUtil.deparenthesize
|
import org.jetbrains.kotlin.psi.KtPsiUtil.deparenthesize
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.getPossiblyQualifiedCallExpression
|
||||||
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
|
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
|
||||||
import org.jetbrains.kotlin.toKtPsiSourceElement
|
import org.jetbrains.kotlin.toKtPsiSourceElement
|
||||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||||
@@ -123,6 +122,9 @@ internal class KtFirCallResolver(
|
|||||||
): KtCallInfo? {
|
): KtCallInfo? {
|
||||||
if (this is FirCheckNotNullCall)
|
if (this is FirCheckNotNullCall)
|
||||||
return KtSuccessCallInfo(KtCheckNotNullCall(token, argumentList.arguments.first().psi as KtExpression))
|
return KtSuccessCallInfo(KtCheckNotNullCall(token, argumentList.arguments.first().psi as KtExpression))
|
||||||
|
|
||||||
|
createGenericTypeQualifierCallIfApplicable(this, psi)?.let { return it }
|
||||||
|
|
||||||
if (resolveCalleeExpressionOfFunctionCall && this is FirImplicitInvokeCall) {
|
if (resolveCalleeExpressionOfFunctionCall && this is FirImplicitInvokeCall) {
|
||||||
// For implicit invoke, we resolve the calleeExpression of the CallExpression to the call that creates the receiver of this
|
// For implicit invoke, we resolve the calleeExpression of the CallExpression to the call that creates the receiver of this
|
||||||
// implicit invoke call. For example,
|
// implicit invoke call. For example,
|
||||||
@@ -192,6 +194,21 @@ internal class KtFirCallResolver(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves call expressions like `Foo<Bar>` or `test.Foo<Bar>` in calls like `Foo<Bar>::foo` and `test.Foo<Bar>::foo`.
|
||||||
|
*
|
||||||
|
* We have a separate [KtGenericTypeQualifier] type of [KtCall].
|
||||||
|
*/
|
||||||
|
private fun createGenericTypeQualifierCallIfApplicable(firElement: FirElement, psiElement: KtElement): KtCallInfo? {
|
||||||
|
if (psiElement !is KtExpression) return null
|
||||||
|
if (firElement !is FirResolvedQualifier) return null
|
||||||
|
|
||||||
|
val call = psiElement.getPossiblyQualifiedCallExpression() ?: return null
|
||||||
|
if (call.typeArgumentList == null || call.valueArgumentList != null) return null
|
||||||
|
|
||||||
|
return KtSuccessCallInfo(KtGenericTypeQualifier(token, psiElement))
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When resolving the calleeExpression of a `KtCallExpression`, we resolve the entire `KtCallExpression` instead. This way, the
|
* When resolving the calleeExpression of a `KtCallExpression`, we resolve the entire `KtCallExpression` instead. This way, the
|
||||||
* corresponding FIR element is the `FirFunctionCall`, etc. Implicit invoke is then specially handled after obtaining the
|
* corresponding FIR element is the `FirFunctionCall`, etc. Implicit invoke is then specially handled after obtaining the
|
||||||
|
|||||||
+12
@@ -214,6 +214,18 @@ public class FirIdeNormalAnalysisSourceModuleResolveCallTestGenerated extends Ab
|
|||||||
runTest("analysis/analysis-api/testData/components/callResolver/resolveCall/builderInference.kt");
|
runTest("analysis/analysis-api/testData/components/callResolver/resolveCall/builderInference.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("callableReference_genericQualifier1.kt")
|
||||||
|
public void testCallableReference_genericQualifier1() throws Exception {
|
||||||
|
runTest("analysis/analysis-api/testData/components/callResolver/resolveCall/callableReference_genericQualifier1.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("callableReference_genericQualifier2.kt")
|
||||||
|
public void testCallableReference_genericQualifier2() throws Exception {
|
||||||
|
runTest("analysis/analysis-api/testData/components/callResolver/resolveCall/callableReference_genericQualifier2.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("calleeExpressionOfImplicitInvoke.kt")
|
@TestMetadata("calleeExpressionOfImplicitInvoke.kt")
|
||||||
public void testCalleeExpressionOfImplicitInvoke() throws Exception {
|
public void testCalleeExpressionOfImplicitInvoke() throws Exception {
|
||||||
|
|||||||
+12
@@ -214,6 +214,18 @@ public class FirStandaloneNormalAnalysisSourceModuleResolveCallTestGenerated ext
|
|||||||
runTest("analysis/analysis-api/testData/components/callResolver/resolveCall/builderInference.kt");
|
runTest("analysis/analysis-api/testData/components/callResolver/resolveCall/builderInference.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("callableReference_genericQualifier1.kt")
|
||||||
|
public void testCallableReference_genericQualifier1() throws Exception {
|
||||||
|
runTest("analysis/analysis-api/testData/components/callResolver/resolveCall/callableReference_genericQualifier1.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("callableReference_genericQualifier2.kt")
|
||||||
|
public void testCallableReference_genericQualifier2() throws Exception {
|
||||||
|
runTest("analysis/analysis-api/testData/components/callResolver/resolveCall/callableReference_genericQualifier2.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("calleeExpressionOfImplicitInvoke.kt")
|
@TestMetadata("calleeExpressionOfImplicitInvoke.kt")
|
||||||
public void testCalleeExpressionOfImplicitInvoke() throws Exception {
|
public void testCalleeExpressionOfImplicitInvoke() throws Exception {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.analysis.api.symbols.*
|
|||||||
import org.jetbrains.kotlin.analysis.api.lifetime.KtLifetimeToken
|
import org.jetbrains.kotlin.analysis.api.lifetime.KtLifetimeToken
|
||||||
import org.jetbrains.kotlin.analysis.api.types.KtType
|
import org.jetbrains.kotlin.analysis.api.types.KtType
|
||||||
import org.jetbrains.kotlin.analysis.api.lifetime.withValidityAssertion
|
import org.jetbrains.kotlin.analysis.api.lifetime.withValidityAssertion
|
||||||
|
import org.jetbrains.kotlin.psi.KtCallExpression
|
||||||
import org.jetbrains.kotlin.psi.KtExpression
|
import org.jetbrains.kotlin.psi.KtExpression
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -114,6 +115,33 @@ public class KtInapplicableCallCandidateInfo(
|
|||||||
*/
|
*/
|
||||||
public sealed class KtCall : KtLifetimeOwner
|
public sealed class KtCall : KtLifetimeOwner
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A special call for type qualifiers with generic parameters, which, from the PSI perspective, are [KtCallExpression]-s.
|
||||||
|
*
|
||||||
|
* Examples:
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* fun test() {
|
||||||
|
* Collection<*>::isEmpty
|
||||||
|
*
|
||||||
|
* kotlin.List<Int>::size
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* Both `Collection<*>` and `List<Int>` are [KtCallExpression]-s, so we need to be able to successfully resolve them to something
|
||||||
|
* sensible - that's why we need [KtGenericTypeQualifier].
|
||||||
|
*/
|
||||||
|
public class KtGenericTypeQualifier(
|
||||||
|
override val token: KtLifetimeToken,
|
||||||
|
private val _qualifier: KtExpression,
|
||||||
|
) : KtCall() {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The full qualifier - either a [KtCallExpression] or a [org.jetbrains.kotlin.psi.KtDotQualifiedExpression].
|
||||||
|
*/
|
||||||
|
public val qualifier: KtExpression get() = withValidityAssertion { _qualifier }
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A callable symbol partially applied with receivers and type arguments. Essentially, this is a call that misses some information. For
|
* A callable symbol partially applied with receivers and type arguments. Essentially, this is a call that misses some information. For
|
||||||
* properties, the missing information is the type of access (read, write, or compound access) to this property. For functions, the missing
|
* properties, the missing information is the type of access (read, write, or compound access) to this property. For functions, the missing
|
||||||
|
|||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
class Test<T> {
|
||||||
|
fun foo() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Bar
|
||||||
|
|
||||||
|
val bar = <expr>Test<Bar></expr>::foo
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
KtSuccessCallInfo:
|
||||||
|
call = KtGenericTypeQualifier:
|
||||||
|
qualifier = Test<Bar>
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
class Test<T> {
|
||||||
|
fun foo() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Bar
|
||||||
|
|
||||||
|
val bar = test.<expr>Test<Bar></expr>::foo
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
KtSuccessCallInfo:
|
||||||
|
call = KtGenericTypeQualifier:
|
||||||
|
qualifier = test.Test<Bar>
|
||||||
Reference in New Issue
Block a user