diff --git a/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/components/KtFe10CallResolver.kt b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/components/KtFe10CallResolver.kt index 82c0373ac8f..f8829abdbbc 100644 --- a/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/components/KtFe10CallResolver.kt +++ b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/components/KtFe10CallResolver.kt @@ -30,6 +30,8 @@ import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.diagnostics.Severity import org.jetbrains.kotlin.lexer.KtTokens 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.resolve.BindingContext import org.jetbrains.kotlin.resolve.DelegatingBindingTrace @@ -156,7 +158,9 @@ internal class KtFe10CallResolver( handleAsCheckNotNullCall(unwrappedPsi)?.let { return@with it } handleAsFunctionCall(this, unwrappedPsi) } - else -> handleAsFunctionCall(this, unwrappedPsi) ?: handleAsPropertyRead(this, unwrappedPsi) + else -> handleAsFunctionCall(this, unwrappedPsi) + ?: handleAsPropertyRead(this, unwrappedPsi) + ?: handleAsGenericTypeQualifier(unwrappedPsi) } ?: handleResolveErrors(this, psi) } @@ -385,6 +389,23 @@ internal class KtFe10CallResolver( return call.toPropertyRead(context)?.let { createCallInfo(context, element, it, listOf(call)) } } + /** + * Handles call expressions like `Foo` or `test.Foo` in calls like `Foo::foo` and `test.Foo::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? { val partiallyAppliedSymbol = toPartiallyAppliedVariableSymbol(context) ?: return null return KtSimpleVariableAccessCall(partiallyAppliedSymbol, KtSimpleVariableAccess.Read) diff --git a/analysis/analysis-api-fe10/tests-gen/org/jetbrains/kotlin/analysis/api/fe10/test/cases/generated/cases/components/callResolver/Fe10IdeNormalAnalysisSourceModuleResolveCallTestGenerated.java b/analysis/analysis-api-fe10/tests-gen/org/jetbrains/kotlin/analysis/api/fe10/test/cases/generated/cases/components/callResolver/Fe10IdeNormalAnalysisSourceModuleResolveCallTestGenerated.java index 11c52b7124d..03ea6caed28 100644 --- a/analysis/analysis-api-fe10/tests-gen/org/jetbrains/kotlin/analysis/api/fe10/test/cases/generated/cases/components/callResolver/Fe10IdeNormalAnalysisSourceModuleResolveCallTestGenerated.java +++ b/analysis/analysis-api-fe10/tests-gen/org/jetbrains/kotlin/analysis/api/fe10/test/cases/generated/cases/components/callResolver/Fe10IdeNormalAnalysisSourceModuleResolveCallTestGenerated.java @@ -214,6 +214,18 @@ public class Fe10IdeNormalAnalysisSourceModuleResolveCallTestGenerated extends A 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 @TestMetadata("calleeExpressionOfImplicitInvoke.kt") public void testCalleeExpressionOfImplicitInvoke() throws Exception { diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirCallResolver.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirCallResolver.kt index 7404f0fce0c..876b08f86a0 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirCallResolver.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirCallResolver.kt @@ -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.low.level.api.fir.api.getOrBuildFir 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.analysis.checkers.toRegularClassSymbol 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.FirResolvedNamedReference 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.Candidate 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.psi.* 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.toKtPsiSourceElement import org.jetbrains.kotlin.util.OperatorNameConventions @@ -123,6 +122,9 @@ internal class KtFirCallResolver( ): KtCallInfo? { if (this is FirCheckNotNullCall) return KtSuccessCallInfo(KtCheckNotNullCall(token, argumentList.arguments.first().psi as KtExpression)) + + createGenericTypeQualifierCallIfApplicable(this, psi)?.let { return it } + if (resolveCalleeExpressionOfFunctionCall && this is FirImplicitInvokeCall) { // For implicit invoke, we resolve the calleeExpression of the CallExpression to the call that creates the receiver of this // implicit invoke call. For example, @@ -192,6 +194,21 @@ internal class KtFirCallResolver( } } + /** + * Resolves call expressions like `Foo` or `test.Foo` in calls like `Foo::foo` and `test.Foo::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 * corresponding FIR element is the `FirFunctionCall`, etc. Implicit invoke is then specially handled after obtaining the diff --git a/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/components/callResolver/FirIdeNormalAnalysisSourceModuleResolveCallTestGenerated.java b/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/components/callResolver/FirIdeNormalAnalysisSourceModuleResolveCallTestGenerated.java index 79cc1269406..d40debc5e36 100644 --- a/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/components/callResolver/FirIdeNormalAnalysisSourceModuleResolveCallTestGenerated.java +++ b/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/components/callResolver/FirIdeNormalAnalysisSourceModuleResolveCallTestGenerated.java @@ -214,6 +214,18 @@ public class FirIdeNormalAnalysisSourceModuleResolveCallTestGenerated extends Ab 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 @TestMetadata("calleeExpressionOfImplicitInvoke.kt") public void testCalleeExpressionOfImplicitInvoke() throws Exception { diff --git a/analysis/analysis-api-standalone/tests-gen/org/jetbrains/kotlin/analysis/api/standalone/fir/test/cases/generated/cases/components/callResolver/FirStandaloneNormalAnalysisSourceModuleResolveCallTestGenerated.java b/analysis/analysis-api-standalone/tests-gen/org/jetbrains/kotlin/analysis/api/standalone/fir/test/cases/generated/cases/components/callResolver/FirStandaloneNormalAnalysisSourceModuleResolveCallTestGenerated.java index fb0f8d4cccf..df1b8ab0920 100644 --- a/analysis/analysis-api-standalone/tests-gen/org/jetbrains/kotlin/analysis/api/standalone/fir/test/cases/generated/cases/components/callResolver/FirStandaloneNormalAnalysisSourceModuleResolveCallTestGenerated.java +++ b/analysis/analysis-api-standalone/tests-gen/org/jetbrains/kotlin/analysis/api/standalone/fir/test/cases/generated/cases/components/callResolver/FirStandaloneNormalAnalysisSourceModuleResolveCallTestGenerated.java @@ -214,6 +214,18 @@ public class FirStandaloneNormalAnalysisSourceModuleResolveCallTestGenerated ext 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 @TestMetadata("calleeExpressionOfImplicitInvoke.kt") public void testCalleeExpressionOfImplicitInvoke() throws Exception { diff --git a/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/calls/KtCall.kt b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/calls/KtCall.kt index ef4d8f03737..9315a78fe85 100644 --- a/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/calls/KtCall.kt +++ b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/calls/KtCall.kt @@ -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.types.KtType import org.jetbrains.kotlin.analysis.api.lifetime.withValidityAssertion +import org.jetbrains.kotlin.psi.KtCallExpression import org.jetbrains.kotlin.psi.KtExpression /** @@ -114,6 +115,33 @@ public class KtInapplicableCallCandidateInfo( */ 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::size + * } + * ``` + * + * Both `Collection<*>` and `List` 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 * properties, the missing information is the type of access (read, write, or compound access) to this property. For functions, the missing diff --git a/analysis/analysis-api/testData/components/callResolver/resolveCall/callableReference_genericQualifier1.kt b/analysis/analysis-api/testData/components/callResolver/resolveCall/callableReference_genericQualifier1.kt new file mode 100644 index 00000000000..d90c8c06416 --- /dev/null +++ b/analysis/analysis-api/testData/components/callResolver/resolveCall/callableReference_genericQualifier1.kt @@ -0,0 +1,9 @@ +package test + +class Test { + fun foo() {} +} + +class Bar + +val bar = Test::foo \ No newline at end of file diff --git a/analysis/analysis-api/testData/components/callResolver/resolveCall/callableReference_genericQualifier1.txt b/analysis/analysis-api/testData/components/callResolver/resolveCall/callableReference_genericQualifier1.txt new file mode 100644 index 00000000000..52eef9aa990 --- /dev/null +++ b/analysis/analysis-api/testData/components/callResolver/resolveCall/callableReference_genericQualifier1.txt @@ -0,0 +1,3 @@ +KtSuccessCallInfo: + call = KtGenericTypeQualifier: + qualifier = Test \ No newline at end of file diff --git a/analysis/analysis-api/testData/components/callResolver/resolveCall/callableReference_genericQualifier2.kt b/analysis/analysis-api/testData/components/callResolver/resolveCall/callableReference_genericQualifier2.kt new file mode 100644 index 00000000000..d2c92ba85bc --- /dev/null +++ b/analysis/analysis-api/testData/components/callResolver/resolveCall/callableReference_genericQualifier2.kt @@ -0,0 +1,9 @@ +package test + +class Test { + fun foo() {} +} + +class Bar + +val bar = test.Test::foo \ No newline at end of file diff --git a/analysis/analysis-api/testData/components/callResolver/resolveCall/callableReference_genericQualifier2.txt b/analysis/analysis-api/testData/components/callResolver/resolveCall/callableReference_genericQualifier2.txt new file mode 100644 index 00000000000..40643bc314e --- /dev/null +++ b/analysis/analysis-api/testData/components/callResolver/resolveCall/callableReference_genericQualifier2.txt @@ -0,0 +1,3 @@ +KtSuccessCallInfo: + call = KtGenericTypeQualifier: + qualifier = test.Test \ No newline at end of file