From 0ed802bca416308a4590f9b15d82c8c071edc943 Mon Sep 17 00:00:00 2001 From: Mark Punzalan Date: Mon, 31 Jan 2022 17:58:23 +0000 Subject: [PATCH] Analysis API: Add tests for KtCallResolver.resolveCandidates(). --- .../api/fir/components/KtFirCallResolver.kt | 5 +- .../FirResolveCandidatesTestGenerated.java | 106 ++++++++++++++++++ .../components/AbstractResolveCallTest.kt | 93 --------------- .../AbstractResolveCandidatesTest.kt | 54 +++++++++ .../impl/base/test/components/testUtils.kt | 100 +++++++++++++++++ .../analysis/api/components/KtCallResolver.kt | 5 +- .../resolveCandidates/noCandidates.kt | 3 + .../resolveCandidates/noCandidates.txt | 1 + .../singleCandidate/functionCall.kt | 5 + .../singleCandidate/functionCall.txt | 24 ++++ .../functionCallWithNotEnoughArgs.kt | 5 + .../functionCallWithNotEnoughArgs.txt | 32 ++++++ .../singleCandidate/indexedGet.kt | 7 ++ .../singleCandidate/indexedGet.txt | 36 ++++++ .../indexedGetWithNotEnoughArgs.kt | 7 ++ .../indexedGetWithNotEnoughArgs.txt | 34 ++++++ .../singleCandidate/indexedSet.kt | 7 ++ .../singleCandidate/indexedSet.txt | 46 ++++++++ .../indexedSetWithNotEnoughArgs.kt | 7 ++ .../indexedSetWithNotEnoughArgs.txt | 44 ++++++++ .../singleCandidate/samConstructorCall.kt | 1 + .../singleCandidate/samConstructorCall.txt | 24 ++++ .../singleCandidate/variableAsFunction.kt | 3 + .../singleCandidate/variableAsFunction.txt | 24 ++++ .../variableAsFunctionLikeCall.kt | 5 + .../variableAsFunctionLikeCall.txt | 12 ++ .../tests/analysis/api/analysisApi.kt | 7 ++ 27 files changed, 597 insertions(+), 100 deletions(-) create mode 100644 analysis/analysis-api-fir/tests/org/jetbrains/kotlin/analysis/api/fir/components/FirResolveCandidatesTestGenerated.java create mode 100644 analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/components/AbstractResolveCandidatesTest.kt create mode 100644 analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/components/testUtils.kt create mode 100644 analysis/analysis-api/testData/components/callResolver/resolveCandidates/noCandidates.kt create mode 100644 analysis/analysis-api/testData/components/callResolver/resolveCandidates/noCandidates.txt create mode 100644 analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/functionCall.kt create mode 100644 analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/functionCall.txt create mode 100644 analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/functionCallWithNotEnoughArgs.kt create mode 100644 analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/functionCallWithNotEnoughArgs.txt create mode 100644 analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/indexedGet.kt create mode 100644 analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/indexedGet.txt create mode 100644 analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/indexedGetWithNotEnoughArgs.kt create mode 100644 analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/indexedGetWithNotEnoughArgs.txt create mode 100644 analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/indexedSet.kt create mode 100644 analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/indexedSet.txt create mode 100644 analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/indexedSetWithNotEnoughArgs.kt create mode 100644 analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/indexedSetWithNotEnoughArgs.txt create mode 100644 analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/samConstructorCall.kt create mode 100644 analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/samConstructorCall.txt create mode 100644 analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/variableAsFunction.kt create mode 100644 analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/variableAsFunction.txt create mode 100644 analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/variableAsFunctionLikeCall.kt create mode 100644 analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/variableAsFunctionLikeCall.txt 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 b3af21768af..239b8f77e78 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 @@ -19,7 +19,6 @@ import org.jetbrains.kotlin.analysis.api.symbols.* import org.jetbrains.kotlin.analysis.api.tokens.ValidityToken import org.jetbrains.kotlin.analysis.api.types.KtSubstitutor import org.jetbrains.kotlin.analysis.api.withValidityAssertion -import org.jetbrains.kotlin.analysis.low.level.api.fir.api.LowLevelFirApiFacadeForResolveOnAir.getTowerContextProvider import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getOrBuildFir import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getOrBuildFirFile import org.jetbrains.kotlin.diagnostics.KtDiagnostic @@ -640,7 +639,7 @@ internal class KtFirCallResolver( override fun resolveCandidates(psi: KtElement): List = withValidityAssertion { val firCall = when (val fir = psi.getOrBuildFir(firResolveState)) { is FirFunctionCall -> fir - is FirSafeCallExpression -> fir.regularQualifiedAccess.safeAs() + is FirSafeCallExpression -> fir.selector.safeAs() // TODO: FirDelegatedConstructorColl, FirAnnotationCall, FirArrayOfCall, FirConstructor else -> null } ?: return@withValidityAssertion emptyList() @@ -671,7 +670,7 @@ internal class KtFirCallResolver( @OptIn(PrivateForInline::class) fun getAllCandidates(functionCall: FirFunctionCall, element: KtElement): List { - val towerContext = firResolveState.getTowerContextProvider().getClosestAvailableParentContext(element) + val towerContext = firResolveState.getTowerContextProvider(element.containingKtFile).getClosestAvailableParentContext(element) towerContext?.let { bodyResolveComponents.context.replaceTowerDataContext(it) } // Note: All candidate symbols should have the same name val name = diff --git a/analysis/analysis-api-fir/tests/org/jetbrains/kotlin/analysis/api/fir/components/FirResolveCandidatesTestGenerated.java b/analysis/analysis-api-fir/tests/org/jetbrains/kotlin/analysis/api/fir/components/FirResolveCandidatesTestGenerated.java new file mode 100644 index 00000000000..a8825004fe7 --- /dev/null +++ b/analysis/analysis-api-fir/tests/org/jetbrains/kotlin/analysis/api/fir/components/FirResolveCandidatesTestGenerated.java @@ -0,0 +1,106 @@ +/* + * 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.components; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.util.KtTestUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.kotlin.analysis.api.impl.barebone.test.FrontendApiTestConfiguratorService; +import org.jetbrains.kotlin.analysis.api.fir.FirFrontendApiTestConfiguratorService; +import org.jetbrains.kotlin.analysis.api.impl.base.test.components.AbstractResolveCandidatesTest; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link GenerateNewCompilerTests.kt}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("analysis/analysis-api/testData/components/callResolver/resolveCandidates") +@TestDataPath("$PROJECT_ROOT") +public class FirResolveCandidatesTestGenerated extends AbstractResolveCandidatesTest { + @NotNull + @Override + public FrontendApiTestConfiguratorService getConfigurator() { + return FirFrontendApiTestConfiguratorService.INSTANCE; + } + + @Test + public void testAllFilesPresentInResolveCandidates() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/components/callResolver/resolveCandidates"), Pattern.compile("^(.+)\\.kt$"), null, true); + } + + @Test + @TestMetadata("noCandidates.kt") + public void testNoCandidates() throws Exception { + runTest("analysis/analysis-api/testData/components/callResolver/resolveCandidates/noCandidates.kt"); + } + + @Nested + @TestMetadata("analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate") + @TestDataPath("$PROJECT_ROOT") + public class SingleCandidate { + @Test + public void testAllFilesPresentInSingleCandidate() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate"), Pattern.compile("^(.+)\\.kt$"), null, true); + } + + @Test + @TestMetadata("functionCall.kt") + public void testFunctionCall() throws Exception { + runTest("analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/functionCall.kt"); + } + + @Test + @TestMetadata("functionCallWithNotEnoughArgs.kt") + public void testFunctionCallWithNotEnoughArgs() throws Exception { + runTest("analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/functionCallWithNotEnoughArgs.kt"); + } + + @Test + @TestMetadata("indexedGet.kt") + public void testIndexedGet() throws Exception { + runTest("analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/indexedGet.kt"); + } + + @Test + @TestMetadata("indexedGetWithNotEnoughArgs.kt") + public void testIndexedGetWithNotEnoughArgs() throws Exception { + runTest("analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/indexedGetWithNotEnoughArgs.kt"); + } + + @Test + @TestMetadata("indexedSet.kt") + public void testIndexedSet() throws Exception { + runTest("analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/indexedSet.kt"); + } + + @Test + @TestMetadata("indexedSetWithNotEnoughArgs.kt") + public void testIndexedSetWithNotEnoughArgs() throws Exception { + runTest("analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/indexedSetWithNotEnoughArgs.kt"); + } + + @Test + @TestMetadata("samConstructorCall.kt") + public void testSamConstructorCall() throws Exception { + runTest("analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/samConstructorCall.kt"); + } + + @Test + @TestMetadata("variableAsFunction.kt") + public void testVariableAsFunction() throws Exception { + runTest("analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/variableAsFunction.kt"); + } + + @Test + @TestMetadata("variableAsFunctionLikeCall.kt") + public void testVariableAsFunctionLikeCall() throws Exception { + runTest("analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/variableAsFunctionLikeCall.kt"); + } + } +} diff --git a/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/components/AbstractResolveCallTest.kt b/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/components/AbstractResolveCallTest.kt index 77869499baa..a544eb4e448 100644 --- a/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/components/AbstractResolveCallTest.kt +++ b/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/components/AbstractResolveCallTest.kt @@ -8,23 +8,12 @@ package org.jetbrains.kotlin.analysis.api.impl.base.test.components import com.intellij.psi.PsiElement import org.jetbrains.kotlin.analysis.api.KtAnalysisSession import org.jetbrains.kotlin.analysis.api.calls.KtCallInfo -import org.jetbrains.kotlin.analysis.api.diagnostics.KtDiagnostic -import org.jetbrains.kotlin.analysis.api.impl.barebone.test.FrontendApiTestConfiguratorService import org.jetbrains.kotlin.analysis.api.impl.barebone.test.expressionMarkerProvider -import org.jetbrains.kotlin.analysis.api.impl.base.KtMapBackedSubstitutor import org.jetbrains.kotlin.analysis.api.impl.base.test.test.framework.AbstractHLApiSingleModuleTest -import org.jetbrains.kotlin.analysis.api.symbols.* -import org.jetbrains.kotlin.analysis.api.symbols.DebugSymbolRenderer.render -import org.jetbrains.kotlin.analysis.api.types.KtSubstitutor -import org.jetbrains.kotlin.analysis.api.types.KtType -import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.test.model.TestModule import org.jetbrains.kotlin.test.services.TestServices import org.jetbrains.kotlin.test.services.assertions -import kotlin.reflect.KProperty1 -import kotlin.reflect.KVisibility -import kotlin.reflect.full.memberProperties abstract class AbstractResolveCallTest : AbstractHLApiSingleModuleTest() { override fun doTestByFileStructure(ktFiles: List, module: TestModule, testServices: TestServices) { @@ -56,85 +45,3 @@ abstract class AbstractResolveCallTest : AbstractHLApiSingleModuleTest() { } } - -private fun KtAnalysisSession.stringRepresentation(call: KtCallInfo): String { - fun Any.stringValue(): String { - fun KtType.render() = asStringForDebugging().replace('/', '.') - fun String.indented() = replace("\n", "\n ") - return when (this) { - is KtFunctionLikeSymbol -> buildString { - append( - when (this@stringValue) { - is KtFunctionSymbol -> callableIdIfNonLocal ?: name - is KtSamConstructorSymbol -> callableIdIfNonLocal ?: name - is KtConstructorSymbol -> "" - is KtPropertyGetterSymbol -> callableIdIfNonLocal ?: "" - is KtPropertySetterSymbol -> callableIdIfNonLocal ?: "" - else -> error("unexpected symbol kind in KtCall: ${this@stringValue::class.java}") - } - ) - append("(") - (this@stringValue as? KtFunctionSymbol)?.receiverType?.let { receiver -> - append(": ${receiver.render()}") - if (valueParameters.isNotEmpty()) append(", ") - } - - @Suppress("DEPRECATION") - (this@stringValue as? KtCallableSymbol)?.getDispatchReceiverType()?.let { dispatchReceiverType -> - append(": ${dispatchReceiverType.render()}") - if (valueParameters.isNotEmpty()) append(", ") - } - valueParameters.joinTo(this) { it.stringValue() } - append(")") - append(": ${returnType.render()}") - } - is KtValueParameterSymbol -> "${if (isVararg) "vararg " else ""}$name: ${returnType.render()}" - is KtTypeParameterSymbol -> this.nameOrAnonymous.asString() - is KtVariableSymbol -> "${if (isVal) "val" else "var"} $name: ${returnType.render()}" - is KtSymbol -> render(this) - is Boolean -> toString() - is Map<*, *> -> if (isEmpty()) "{}" else entries.joinToString( - separator = ",\n ", - prefix = "{\n ", - postfix = "\n}" - ) { (k, v) -> "${k?.stringValue()?.indented()} -> (${v?.stringValue()?.indented()})" } - is Collection<*> -> if (isEmpty()) "[]" else joinToString( - separator = ",\n ", - prefix = "[\n ", - postfix = "\n]" - ) { - it?.stringValue()?.indented() ?: "null" - } - is PsiElement -> this.text - is KtSubstitutor.Empty -> "" - is KtMapBackedSubstitutor -> { - val mappingText = getAsMap().entries - .joinToString(prefix = "{", postfix = "}") { (k, v) -> k.stringValue() + " = " + v.asStringForDebugging() } - "" - } - is KtSubstitutor -> "" - is KtDiagnostic -> "$severity<$factoryName: $defaultMessage>" - is KtType -> render() - is Enum<*> -> name - is Name -> asString() - else -> buildString { - val clazz = this@stringValue::class - append(clazz.simpleName!!) - appendLine(":") - clazz.memberProperties - .filter { it.name != "token" && it.visibility == KVisibility.PUBLIC } - .joinTo(this, separator = "\n ", prefix = " ") { property -> - val name = property.name - - @Suppress("UNCHECKED_CAST") - val value = - (property as KProperty1).get(this@stringValue)?.stringValue()?.indented() - "$name = $value" - } - } - } - } - - return call.stringValue() -} - diff --git a/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/components/AbstractResolveCandidatesTest.kt b/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/components/AbstractResolveCandidatesTest.kt new file mode 100644 index 00000000000..247db770518 --- /dev/null +++ b/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/components/AbstractResolveCandidatesTest.kt @@ -0,0 +1,54 @@ +/* + * 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.impl.base.test.components + +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.analysis.api.KtAnalysisSession +import org.jetbrains.kotlin.analysis.api.calls.KtCall +import org.jetbrains.kotlin.analysis.api.calls.KtCallInfo +import org.jetbrains.kotlin.analysis.api.impl.barebone.test.FrontendApiTestConfiguratorService +import org.jetbrains.kotlin.analysis.api.impl.barebone.test.expressionMarkerProvider +import org.jetbrains.kotlin.analysis.api.impl.base.test.test.framework.AbstractHLApiSingleModuleTest +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.test.model.TestModule +import org.jetbrains.kotlin.test.services.TestServices +import org.jetbrains.kotlin.test.services.assertions + +abstract class AbstractResolveCandidatesTest : AbstractHLApiSingleModuleTest() { + override fun doTestByFileStructure(ktFiles: List, module: TestModule, testServices: TestServices) { + val ktFile = ktFiles.first() + val expression = testServices.expressionMarkerProvider.getSelectedElement(ktFile) + + val actual = executeOnPooledThreadInReadAction { + analyseForTest(expression) { + val candidates = resolveCandidates(expression) + if (candidates.isEmpty()) { + "NO_CANDIDATES" + } else { + candidates.joinToString("\n\n") { stringRepresentation(it) } + } + } + } + testServices.assertions.assertEqualsToTestDataFileSibling(actual) + } + + private fun KtAnalysisSession.resolveCandidates(element: PsiElement): List = when (element) { + is KtValueArgument -> resolveCandidates(element.getArgumentExpression()!!) + is KtDeclarationModifierList -> { + val annotationEntry = element.annotationEntries.singleOrNull() + ?: error("Only single annotation entry is supported for now") + annotationEntry.resolveCandidates() + } + is KtFileAnnotationList -> { + val annotationEntry = element.annotationEntries.singleOrNull() + ?: error("Only single annotation entry is supported for now") + annotationEntry.resolveCandidates() + } + is KtElement -> element.resolveCandidates() + else -> error("Selected element type (${element::class.simpleName}) is not supported for resolveCandidates()") + } + +} diff --git a/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/components/testUtils.kt b/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/components/testUtils.kt new file mode 100644 index 00000000000..b979e53e1a6 --- /dev/null +++ b/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/components/testUtils.kt @@ -0,0 +1,100 @@ +/* + * 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.impl.base.test.components + +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.analysis.api.KtAnalysisSession +import org.jetbrains.kotlin.analysis.api.calls.KtCallInfo +import org.jetbrains.kotlin.analysis.api.diagnostics.KtDiagnostic +import org.jetbrains.kotlin.analysis.api.impl.base.KtMapBackedSubstitutor +import org.jetbrains.kotlin.analysis.api.symbols.* +import org.jetbrains.kotlin.analysis.api.types.KtSubstitutor +import org.jetbrains.kotlin.analysis.api.types.KtType +import org.jetbrains.kotlin.name.Name +import kotlin.reflect.KProperty1 +import kotlin.reflect.KVisibility +import kotlin.reflect.full.memberProperties + +internal fun KtAnalysisSession.stringRepresentation(call: KtCallInfo): String { + fun Any.stringValue(): String { + fun KtType.render() = asStringForDebugging().replace('/', '.') + fun String.indented() = replace("\n", "\n ") + return when (this) { + is KtFunctionLikeSymbol -> buildString { + append( + when (this@stringValue) { + is KtFunctionSymbol -> callableIdIfNonLocal ?: name + is KtSamConstructorSymbol -> callableIdIfNonLocal ?: name + is KtConstructorSymbol -> "" + is KtPropertyGetterSymbol -> callableIdIfNonLocal ?: "" + is KtPropertySetterSymbol -> callableIdIfNonLocal ?: "" + else -> error("unexpected symbol kind in KtCall: ${this@stringValue::class.java}") + } + ) + append("(") + (this@stringValue as? KtFunctionSymbol)?.receiverType?.let { receiver -> + append(": ${receiver.render()}") + if (valueParameters.isNotEmpty()) append(", ") + } + + @Suppress("DEPRECATION") + (this@stringValue as? KtCallableSymbol)?.getDispatchReceiverType()?.let { dispatchReceiverType -> + append(": ${dispatchReceiverType.render()}") + if (valueParameters.isNotEmpty()) append(", ") + } + valueParameters.joinTo(this) { it.stringValue() } + append(")") + append(": ${returnType.render()}") + } + is KtValueParameterSymbol -> "${if (isVararg) "vararg " else ""}$name: ${returnType.render()}" + is KtTypeParameterSymbol -> this.nameOrAnonymous.asString() + is KtVariableSymbol -> "${if (isVal) "val" else "var"} $name: ${returnType.render()}" + is KtSymbol -> DebugSymbolRenderer.render(this) + is Boolean -> toString() + is Map<*, *> -> if (isEmpty()) "{}" else entries.joinToString( + separator = ",\n ", + prefix = "{\n ", + postfix = "\n}" + ) { (k, v) -> "${k?.stringValue()?.indented()} -> (${v?.stringValue()?.indented()})" } + is Collection<*> -> if (isEmpty()) "[]" else joinToString( + separator = ",\n ", + prefix = "[\n ", + postfix = "\n]" + ) { + it?.stringValue()?.indented() ?: "null" + } + is PsiElement -> this.text + is KtSubstitutor.Empty -> "" + is KtMapBackedSubstitutor -> { + val mappingText = getAsMap().entries + .joinToString(prefix = "{", postfix = "}") { (k, v) -> k.stringValue() + " = " + v.asStringForDebugging() } + "" + } + is KtSubstitutor -> "" + is KtDiagnostic -> "$severity<$factoryName: $defaultMessage>" + is KtType -> render() + is Enum<*> -> name + is Name -> asString() + else -> buildString { + val clazz = this@stringValue::class + append(clazz.simpleName!!) + appendLine(":") + clazz.memberProperties + .filter { it.name != "token" && it.visibility == KVisibility.PUBLIC } + .joinTo(this, separator = "\n ", prefix = " ") { property -> + val name = property.name + + @Suppress("UNCHECKED_CAST") + val value = + (property as KProperty1).get(this@stringValue)?.stringValue()?.indented() + "$name = $value" + } + } + } + } + + return call.stringValue() +} \ No newline at end of file diff --git a/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/components/KtCallResolver.kt b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/components/KtCallResolver.kt index faa4942cd62..64c849175ab 100644 --- a/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/components/KtCallResolver.kt +++ b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/components/KtCallResolver.kt @@ -31,9 +31,6 @@ public interface KtCallResolverMixIn : KtAnalysisSessionMixIn { public fun KtArrayAccessExpression.resolveCall(): KtCallInfo = analysisSession.callResolver.resolveCall(this) ?: error("KtArrayAccessExpression should always resolve to a KtCallInfo") - public fun KtCallElement.resolveCandidates(): List = - analysisSession.callResolver.resolveCandidates(this) - - public fun KtArrayAccessExpression.resolveCandidates(): List = + public fun KtElement.resolveCandidates(): List = analysisSession.callResolver.resolveCandidates(this) } \ No newline at end of file diff --git a/analysis/analysis-api/testData/components/callResolver/resolveCandidates/noCandidates.kt b/analysis/analysis-api/testData/components/callResolver/resolveCandidates/noCandidates.kt new file mode 100644 index 00000000000..e77e2d1d9ab --- /dev/null +++ b/analysis/analysis-api/testData/components/callResolver/resolveCandidates/noCandidates.kt @@ -0,0 +1,3 @@ +fun call() { + function(1) +} diff --git a/analysis/analysis-api/testData/components/callResolver/resolveCandidates/noCandidates.txt b/analysis/analysis-api/testData/components/callResolver/resolveCandidates/noCandidates.txt new file mode 100644 index 00000000000..1d7254b6ac6 --- /dev/null +++ b/analysis/analysis-api/testData/components/callResolver/resolveCandidates/noCandidates.txt @@ -0,0 +1 @@ +NO_CANDIDATES \ No newline at end of file diff --git a/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/functionCall.kt b/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/functionCall.kt new file mode 100644 index 00000000000..0e1f84b6548 --- /dev/null +++ b/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/functionCall.kt @@ -0,0 +1,5 @@ +fun function(a: Int) {} + +fun call() { + function(1) +} diff --git a/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/functionCall.txt b/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/functionCall.txt new file mode 100644 index 00000000000..e0beb315d70 --- /dev/null +++ b/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/functionCall.txt @@ -0,0 +1,24 @@ +KtSuccessCallInfo: + call = KtSimpleFunctionCall: + isImplicitInvoke = false + partiallyAppliedSymbol = KtPartiallyAppliedSymbol: + dispatchReceiver = null + extensionReceiver = null + signature = KtFunctionLikeSignature: + receiverType = null + returnType = kotlin.Unit + symbol = /function(a: kotlin.Int): kotlin.Unit + valueParameters = [ + KtVariableLikeSignature: + name = a + receiverType = null + returnType = kotlin.Int + symbol = a: kotlin.Int + ] + argumentMapping = { + 1 -> (KtVariableLikeSignature: + name = a + receiverType = null + returnType = kotlin.Int + symbol = a: kotlin.Int) + } diff --git a/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/functionCallWithNotEnoughArgs.kt b/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/functionCallWithNotEnoughArgs.kt new file mode 100644 index 00000000000..22b83541a78 --- /dev/null +++ b/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/functionCallWithNotEnoughArgs.kt @@ -0,0 +1,5 @@ +fun function(a: Int, b: String) {} + +fun call() { + function(1) +} diff --git a/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/functionCallWithNotEnoughArgs.txt b/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/functionCallWithNotEnoughArgs.txt new file mode 100644 index 00000000000..821c1a3ea36 --- /dev/null +++ b/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/functionCallWithNotEnoughArgs.txt @@ -0,0 +1,32 @@ +KtErrorCallInfo: + candidateCalls = [ + KtSimpleFunctionCall: + isImplicitInvoke = false + partiallyAppliedSymbol = KtPartiallyAppliedSymbol: + dispatchReceiver = null + extensionReceiver = null + signature = KtFunctionLikeSignature: + receiverType = null + returnType = kotlin.Unit + symbol = /function(a: kotlin.Int, b: kotlin.String): kotlin.Unit + valueParameters = [ + KtVariableLikeSignature: + name = a + receiverType = null + returnType = kotlin.Int + symbol = a: kotlin.Int, + KtVariableLikeSignature: + name = b + receiverType = null + returnType = kotlin.String + symbol = b: kotlin.String + ] + argumentMapping = { + 1 -> (KtVariableLikeSignature: + name = a + receiverType = null + returnType = kotlin.Int + symbol = a: kotlin.Int) + } + ] + diagnostic = ERROR diff --git a/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/indexedGet.kt b/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/indexedGet.kt new file mode 100644 index 00000000000..39bbf11a773 --- /dev/null +++ b/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/indexedGet.kt @@ -0,0 +1,7 @@ +class C { + operator fun get(a: Int, b: String): Boolean = true +} + +fun call(c: C) { + val res = c[1, "foo"] +} \ No newline at end of file diff --git a/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/indexedGet.txt b/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/indexedGet.txt new file mode 100644 index 00000000000..e93b4bd1a66 --- /dev/null +++ b/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/indexedGet.txt @@ -0,0 +1,36 @@ +KtSuccessCallInfo: + call = KtSimpleFunctionCall: + isImplicitInvoke = false + partiallyAppliedSymbol = KtPartiallyAppliedSymbol: + dispatchReceiver = KtExplicitReceiverValue: + expression = c + isSafeNavigation = false + extensionReceiver = null + signature = KtFunctionLikeSignature: + receiverType = null + returnType = kotlin.Boolean + symbol = /C.get(: C, a: kotlin.Int, b: kotlin.String): kotlin.Boolean + valueParameters = [ + KtVariableLikeSignature: + name = a + receiverType = null + returnType = kotlin.Int + symbol = a: kotlin.Int, + KtVariableLikeSignature: + name = b + receiverType = null + returnType = kotlin.String + symbol = b: kotlin.String + ] + argumentMapping = { + 1 -> (KtVariableLikeSignature: + name = a + receiverType = null + returnType = kotlin.Int + symbol = a: kotlin.Int), + "foo" -> (KtVariableLikeSignature: + name = b + receiverType = null + returnType = kotlin.String + symbol = b: kotlin.String) + } diff --git a/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/indexedGetWithNotEnoughArgs.kt b/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/indexedGetWithNotEnoughArgs.kt new file mode 100644 index 00000000000..5a9352ce703 --- /dev/null +++ b/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/indexedGetWithNotEnoughArgs.kt @@ -0,0 +1,7 @@ +class C { + operator fun get(a: Int, b: String): Boolean = true +} + +fun call(c: C) { + val res = c[1] +} \ No newline at end of file diff --git a/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/indexedGetWithNotEnoughArgs.txt b/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/indexedGetWithNotEnoughArgs.txt new file mode 100644 index 00000000000..6ea774bd9d8 --- /dev/null +++ b/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/indexedGetWithNotEnoughArgs.txt @@ -0,0 +1,34 @@ +KtErrorCallInfo: + candidateCalls = [ + KtSimpleFunctionCall: + isImplicitInvoke = false + partiallyAppliedSymbol = KtPartiallyAppliedSymbol: + dispatchReceiver = KtExplicitReceiverValue: + expression = c + isSafeNavigation = false + extensionReceiver = null + signature = KtFunctionLikeSignature: + receiverType = null + returnType = kotlin.Boolean + symbol = /C.get(: C, a: kotlin.Int, b: kotlin.String): kotlin.Boolean + valueParameters = [ + KtVariableLikeSignature: + name = a + receiverType = null + returnType = kotlin.Int + symbol = a: kotlin.Int, + KtVariableLikeSignature: + name = b + receiverType = null + returnType = kotlin.String + symbol = b: kotlin.String + ] + argumentMapping = { + 1 -> (KtVariableLikeSignature: + name = a + receiverType = null + returnType = kotlin.Int + symbol = a: kotlin.Int) + } + ] + diagnostic = ERROR diff --git a/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/indexedSet.kt b/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/indexedSet.kt new file mode 100644 index 00000000000..2eeabf05d6b --- /dev/null +++ b/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/indexedSet.kt @@ -0,0 +1,7 @@ +class C { + operator fun set(a: Int, b: String, value: Boolean) {} +} + +fun call(c: C) { + c[1, "foo"] = false +} \ No newline at end of file diff --git a/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/indexedSet.txt b/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/indexedSet.txt new file mode 100644 index 00000000000..7f549521842 --- /dev/null +++ b/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/indexedSet.txt @@ -0,0 +1,46 @@ +KtSuccessCallInfo: + call = KtSimpleFunctionCall: + isImplicitInvoke = false + partiallyAppliedSymbol = KtPartiallyAppliedSymbol: + dispatchReceiver = KtExplicitReceiverValue: + expression = c + isSafeNavigation = false + extensionReceiver = null + signature = KtFunctionLikeSignature: + receiverType = null + returnType = kotlin.Unit + symbol = /C.set(: C, a: kotlin.Int, b: kotlin.String, value: kotlin.Boolean): kotlin.Unit + valueParameters = [ + KtVariableLikeSignature: + name = a + receiverType = null + returnType = kotlin.Int + symbol = a: kotlin.Int, + KtVariableLikeSignature: + name = b + receiverType = null + returnType = kotlin.String + symbol = b: kotlin.String, + KtVariableLikeSignature: + name = value + receiverType = null + returnType = kotlin.Boolean + symbol = value: kotlin.Boolean + ] + argumentMapping = { + 1 -> (KtVariableLikeSignature: + name = a + receiverType = null + returnType = kotlin.Int + symbol = a: kotlin.Int), + "foo" -> (KtVariableLikeSignature: + name = b + receiverType = null + returnType = kotlin.String + symbol = b: kotlin.String), + false -> (KtVariableLikeSignature: + name = value + receiverType = null + returnType = kotlin.Boolean + symbol = value: kotlin.Boolean) + } diff --git a/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/indexedSetWithNotEnoughArgs.kt b/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/indexedSetWithNotEnoughArgs.kt new file mode 100644 index 00000000000..877f0199fd9 --- /dev/null +++ b/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/indexedSetWithNotEnoughArgs.kt @@ -0,0 +1,7 @@ +class C { + operator fun set(a: Int, b: String, value: Boolean) {} +} + +fun call(c: C) { + c[1] = false +} \ No newline at end of file diff --git a/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/indexedSetWithNotEnoughArgs.txt b/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/indexedSetWithNotEnoughArgs.txt new file mode 100644 index 00000000000..1946907b8a3 --- /dev/null +++ b/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/indexedSetWithNotEnoughArgs.txt @@ -0,0 +1,44 @@ +KtErrorCallInfo: + candidateCalls = [ + KtSimpleFunctionCall: + isImplicitInvoke = false + partiallyAppliedSymbol = KtPartiallyAppliedSymbol: + dispatchReceiver = KtExplicitReceiverValue: + expression = c + isSafeNavigation = false + extensionReceiver = null + signature = KtFunctionLikeSignature: + receiverType = null + returnType = kotlin.Unit + symbol = /C.set(: C, a: kotlin.Int, b: kotlin.String, value: kotlin.Boolean): kotlin.Unit + valueParameters = [ + KtVariableLikeSignature: + name = a + receiverType = null + returnType = kotlin.Int + symbol = a: kotlin.Int, + KtVariableLikeSignature: + name = b + receiverType = null + returnType = kotlin.String + symbol = b: kotlin.String, + KtVariableLikeSignature: + name = value + receiverType = null + returnType = kotlin.Boolean + symbol = value: kotlin.Boolean + ] + argumentMapping = { + 1 -> (KtVariableLikeSignature: + name = a + receiverType = null + returnType = kotlin.Int + symbol = a: kotlin.Int), + false -> (KtVariableLikeSignature: + name = value + receiverType = null + returnType = kotlin.Boolean + symbol = value: kotlin.Boolean) + } + ] + diagnostic = ERROR diff --git a/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/samConstructorCall.kt b/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/samConstructorCall.kt new file mode 100644 index 00000000000..2985bd0c52a --- /dev/null +++ b/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/samConstructorCall.kt @@ -0,0 +1 @@ +val c = Comparator {x: Int, y: Int -> 1} diff --git a/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/samConstructorCall.txt b/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/samConstructorCall.txt new file mode 100644 index 00000000000..c3ede5f588b --- /dev/null +++ b/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/samConstructorCall.txt @@ -0,0 +1,24 @@ +KtSuccessCallInfo: + call = KtSimpleFunctionCall: + isImplicitInvoke = false + partiallyAppliedSymbol = KtPartiallyAppliedSymbol: + dispatchReceiver = null + extensionReceiver = null + signature = KtFunctionLikeSignature: + receiverType = null + returnType = java.util.Comparator> + symbol = java/util/Comparator(function: kotlin.Function2, ft, kotlin.Int>): java.util.Comparator + valueParameters = [ + KtVariableLikeSignature: + name = function + receiverType = null + returnType = kotlin.Function2, ft, kotlin.Int> + symbol = function: kotlin.Function2, ft, kotlin.Int> + ] + argumentMapping = { + {x: Int, y: Int -> 1} -> (KtVariableLikeSignature: + name = function + receiverType = null + returnType = kotlin.Function2, ft, kotlin.Int> + symbol = function: kotlin.Function2, ft, kotlin.Int>) + } diff --git a/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/variableAsFunction.kt b/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/variableAsFunction.kt new file mode 100644 index 00000000000..ccdfd1b8eb1 --- /dev/null +++ b/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/variableAsFunction.kt @@ -0,0 +1,3 @@ +fun call(x: (Int) -> String) { + x(1) +} diff --git a/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/variableAsFunction.txt b/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/variableAsFunction.txt new file mode 100644 index 00000000000..ee494377fdd --- /dev/null +++ b/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/variableAsFunction.txt @@ -0,0 +1,24 @@ +KtSuccessCallInfo: + call = KtSimpleFunctionCall: + isImplicitInvoke = true + partiallyAppliedSymbol = KtPartiallyAppliedSymbol: + dispatchReceiver = null + extensionReceiver = null + signature = KtFunctionLikeSignature: + receiverType = null + returnType = kotlin.String + symbol = kotlin/Function1.invoke(: kotlin.Function1, p1: P1): R + valueParameters = [ + KtVariableLikeSignature: + name = p1 + receiverType = null + returnType = kotlin.Int + symbol = p1: P1 + ] + argumentMapping = { + 1 -> (KtVariableLikeSignature: + name = p1 + receiverType = null + returnType = kotlin.Int + symbol = p1: P1) + } diff --git a/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/variableAsFunctionLikeCall.kt b/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/variableAsFunctionLikeCall.kt new file mode 100644 index 00000000000..78124268ff5 --- /dev/null +++ b/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/variableAsFunctionLikeCall.kt @@ -0,0 +1,5 @@ +operator fun Int.invoke(): String {} + +fun call(x: kotlin.Int) { + x() +} diff --git a/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/variableAsFunctionLikeCall.txt b/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/variableAsFunctionLikeCall.txt new file mode 100644 index 00000000000..db78693e0a0 --- /dev/null +++ b/analysis/analysis-api/testData/components/callResolver/resolveCandidates/singleCandidate/variableAsFunctionLikeCall.txt @@ -0,0 +1,12 @@ +KtSuccessCallInfo: + call = KtSimpleFunctionCall: + isImplicitInvoke = true + partiallyAppliedSymbol = KtPartiallyAppliedSymbol: + dispatchReceiver = null + extensionReceiver = null + signature = KtFunctionLikeSignature: + receiverType = kotlin.Int + returnType = kotlin.String + symbol = /invoke(: kotlin.Int): kotlin.String + valueParameters = [] + argumentMapping = {} diff --git a/generators/analysis-api-generator/tests/org/jetbrains/kotlin/generators/tests/analysis/api/analysisApi.kt b/generators/analysis-api-generator/tests/org/jetbrains/kotlin/generators/tests/analysis/api/analysisApi.kt index c3daad46d3c..6b287dddfa3 100644 --- a/generators/analysis-api-generator/tests/org/jetbrains/kotlin/generators/tests/analysis/api/analysisApi.kt +++ b/generators/analysis-api-generator/tests/org/jetbrains/kotlin/generators/tests/analysis/api/analysisApi.kt @@ -14,6 +14,7 @@ import org.jetbrains.kotlin.analysis.api.impl.base.test.annotations.AbstractAnal import org.jetbrains.kotlin.analysis.api.impl.base.test.annotations.AbstractAnalysisApiAnnotationsOnFilesTest import org.jetbrains.kotlin.analysis.api.impl.base.test.annotations.AbstractAnalysisApiAnnotationsOnTypesTest import org.jetbrains.kotlin.analysis.api.impl.base.test.components.AbstractResolveCallTest +import org.jetbrains.kotlin.analysis.api.impl.base.test.components.AbstractResolveCandidatesTest import org.jetbrains.kotlin.analysis.api.impl.base.test.components.compileTimeConstantProvider.AbstractCompileTimeConstantEvaluatorTest import org.jetbrains.kotlin.analysis.api.impl.base.test.components.diagnosticProvider.AbstractCollectDiagnosticsTest import org.jetbrains.kotlin.analysis.api.impl.base.test.components.expressionInfoProvider.AbstractReturnTargetSymbolTest @@ -134,6 +135,12 @@ private fun TestGroupSuite.generateAnalysisApiComponentsTests() { test(AbstractResolveCallTest::class) { model("resolveCall") } + test( + AbstractResolveCandidatesTest::class, + generateFe10 = false // TODO: Not yet implemented + ) { + model("resolveCandidates") + } } component("compileTimeConstantProvider") {