Parameter info to use resolve candidates instead of ReferenceVariantsHelper

#KT-6164 Fixed
 #KT-8931 Fixed
This commit is contained in:
Valentin Kipyatkov
2015-10-05 17:23:09 +03:00
parent 80bb1e7430
commit f27d24e640
16 changed files with 286 additions and 118 deletions
@@ -596,14 +596,15 @@ public class CallResolver {
@NotNull ResolutionTask<D, F> task,
@NotNull CallTransformer<D, F> callTransformer
) {
List<CallCandidateResolutionContext<D>> contexts = collectCallCandidateContext(task, callTransformer, EXIT_ON_FIRST_ERROR);
CandidateResolveMode mode = task.collectAllCandidates ? FULLY : EXIT_ON_FIRST_ERROR;
List<CallCandidateResolutionContext<D>> contexts = collectCallCandidateContext(task, callTransformer, mode);
boolean isSuccess = ContainerUtil.exists(contexts, new Condition<CallCandidateResolutionContext<D>>() {
@Override
public boolean value(CallCandidateResolutionContext<D> context) {
return context.candidateCall.getStatus().possibleTransformToSuccess();
}
});
if (!isSuccess) {
if (!isSuccess && mode == EXIT_ON_FIRST_ERROR) {
contexts = collectCallCandidateContext(task, callTransformer, FULLY);
}
@@ -104,6 +104,7 @@ public class TypeUtils {
}
}
@NotNull
public static final JetType NO_EXPECTED_TYPE = new SpecialType("NO_EXPECTED_TYPE");
public static final JetType UNIT_EXPECTED_TYPE = new SpecialType("UNIT_EXPECTED_TYPE");
@@ -23,25 +23,37 @@ import com.intellij.psi.PsiElement
import com.intellij.ui.Gray
import com.intellij.ui.JBColor
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.codeInsight.ReferenceVariantsHelper
import org.jetbrains.kotlin.idea.core.isVisible
import org.jetbrains.kotlin.idea.core.getResolutionScope
import org.jetbrains.kotlin.idea.resolve.frontendService
import org.jetbrains.kotlin.lexer.JetTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.allChildren
import org.jetbrains.kotlin.psi.psiUtil.getCallNameExpression
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DelegatingBindingTrace
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo
import org.jetbrains.kotlin.resolve.calls.CallResolver
import org.jetbrains.kotlin.resolve.calls.callUtil.getCall
import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext
import org.jetbrains.kotlin.resolve.calls.context.CheckArgumentTypesMode
import org.jetbrains.kotlin.resolve.calls.context.ContextDependency
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults
import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus
import org.jetbrains.kotlin.resolve.descriptorUtil.hasDefaultValue
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindExclude
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.checker.JetTypeChecker
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils
import org.jetbrains.kotlin.types.typeUtil.containsError
import java.awt.Color
import java.util.*
@@ -71,42 +83,39 @@ class KotlinFunctionParameterInfoHandler : ParameterInfoHandlerWithTabActionSupp
val argumentList = file.findElementAt(context.offset)?.getStrictParentOfType<JetValueArgumentList>() ?: return null
val callNameExpression = getCallNameExpression(argumentList) ?: return null
val references = callNameExpression.references
if (references.isEmpty()) return null
val callElement = argumentList.parent as? JetCallElement ?: return null
val bindingContext = callElement.analyze(BodyResolveMode.PARTIAL)
val call = callElement.getCall(bindingContext) ?: return null
val resolutionFacade = file.getResolutionFacade()
val bindingContext = callNameExpression.analyze(BodyResolveMode.FULL)
val resolutionScope = callElement.getResolutionScope(bindingContext, resolutionFacade)
val inDescriptor = resolutionScope.ownerDescriptor
val scope = bindingContext.get(BindingContext.RESOLUTION_SCOPE, callNameExpression)
val placeDescriptor = scope?.getContainingDeclaration()
val dataFlowInfo = bindingContext.getDataFlowInfo(call.calleeExpression)
val bindingTrace = DelegatingBindingTrace(bindingContext, "Temporary trace")
val expectedType = (callElement as? JetExpression)?.let {
bindingContext[BindingContext.EXPECTED_EXPRESSION_TYPE, it.getQualifiedExpressionForSelectorOrThis()]
} ?: TypeUtils.NO_EXPECTED_TYPE
val callResolutionContext = BasicCallResolutionContext.create(
bindingTrace, resolutionScope, call, expectedType, dataFlowInfo,
ContextDependency.INDEPENDENT, CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS,
CallChecker.DoNothing, false/*TODO?*/
).replaceCollectAllCandidates(true)
val callResolver = resolutionFacade.frontendService<CallResolver>()
val visibilityFilter = { descriptor: DeclarationDescriptor ->
placeDescriptor == null
|| descriptor !is DeclarationDescriptorWithVisibility
|| descriptor.isVisible(placeDescriptor, bindingContext, callNameExpression)
}
val refName = callNameExpression.getReferencedNameAsName()
val descriptorKindFilter = DescriptorKindFilter(DescriptorKindFilter.FUNCTIONS_MASK or DescriptorKindFilter.CLASSIFIERS_MASK, emptyList<DescriptorKindExclude>())
val variants = ReferenceVariantsHelper(bindingContext, resolutionFacade, visibilityFilter)
.getReferenceVariants(callNameExpression, descriptorKindFilter, { it == refName })
val results: OverloadResolutionResults<FunctionDescriptor> = callResolver.resolveFunctionCall(callResolutionContext)
val itemsToShow = ArrayList<DeclarationDescriptor>()
for (variant in variants) {
if (variant is FunctionDescriptor) {
//todo: renamed functions?
itemsToShow.add(variant)
}
else if (variant is ClassDescriptor) {
//todo: renamed classes?
for (constructorDescriptor in variant.constructors) {
itemsToShow.add(constructorDescriptor)
}
}
for (candidate in results.allCandidates!!) {
val status = candidate.status
if (status == ResolutionStatus.RECEIVER_TYPE_ERROR || status == ResolutionStatus.RECEIVER_PRESENCE_ERROR) continue
var descriptor = candidate.resultingDescriptor
val thisReceiver = ExpressionTypingUtils.normalizeReceiverValueForVisibility(candidate.dispatchReceiver, bindingContext)
if (!Visibilities.isVisible(thisReceiver, descriptor, inDescriptor)) continue
itemsToShow.add(descriptor)
}
context.itemsToShow = itemsToShow.toArray()
@@ -305,8 +314,14 @@ class KotlinFunctionParameterInfoHandler : ParameterInfoHandlerWithTabActionSupp
return "..."
}
private fun getActualParameterType(descriptor: ValueParameterDescriptor)
= descriptor.varargElementType ?: descriptor.type
private fun getActualParameterType(descriptor: ValueParameterDescriptor): JetType {
var type = descriptor.varargElementType ?: descriptor.type
if (type.containsError()) {
val original = descriptor.original
type = original.varargElementType ?: original.type
}
return type
}
private fun isArgumentTypeValid(bindingContext: BindingContext, argument: JetValueArgument, param: ValueParameterDescriptor): Boolean {
val expression = argument.getArgumentExpression() ?: return false
@@ -327,8 +342,8 @@ class KotlinFunctionParameterInfoHandler : ParameterInfoHandlerWithTabActionSupp
): Boolean {
val callNameExpression = getCallNameExpression(argumentList)
if (callNameExpression != null) {
val declarationDescriptor = bindingContext.get(BindingContext.REFERENCE_TARGET, callNameExpression)
if (declarationDescriptor === functionDescriptor) return true
val declarationDescriptor = bindingContext[BindingContext.REFERENCE_TARGET, callNameExpression]
if (declarationDescriptor?.original === functionDescriptor.original) return true
}
return false
@@ -0,0 +1,8 @@
fun x(name: String, next: (name: String) -> String): String {
return next(<caret>)
}
//TODO: use parameter names from functional type
/*
Text: (<highlight>p1: String</highlight>), Disabled: false, Strikeout: false, Green: false
*/
@@ -0,0 +1,12 @@
val header = { s: String, s1: String -> }
fun header(name: String, value: Int) { }
fun call() {
header(<caret>"sdf", "sdjfn")
}
//TODO: use parameter names from functional type
/*
Text: (<highlight>name: String</highlight>, value: Int), Disabled: false, Strikeout: false, Green: false
Text: (<highlight>p1: String</highlight>, p2: String), Disabled: false, Strikeout: false, Green: false
*/
@@ -0,0 +1,15 @@
package test
class A {
fun invoke(i: Int) {
}
}
fun main(args: Array<String>) {
val a = A()
a(<caret>)
}
/*
Text: (<highlight>i: Int</highlight>), Disabled: false, Strikeout: false, Green: false
*/
@@ -1,10 +1,10 @@
open class A(x: Int) {
fun println(x: String) {}
fun println() {}
fun println(x: Boolean) {}
fun xprintln(x: String) {}
fun xprintln() {}
fun xprintln(x: Boolean) {}
fun d(x: Int) {
println(<caret>)
xprintln(<caret>)
}
}
/*
@@ -0,0 +1,11 @@
fun test() {
foo(mapOf(<caret>))
}
fun foo(map: Map<Int, String>) {}
/*
Text: (<highlight>keyValuePair: Pair<Int, String></highlight>), Disabled: false, Strikeout: false, Green: false
Text: (<highlight>vararg values: Pair<Int, String></highlight>), Disabled: false, Strikeout: false, Green: false
Text: (<no parameters>), Disabled: true, Strikeout: false, Green: true
*/
@@ -0,0 +1,9 @@
fun test() {
mapOf<Int, String>(<caret>)
}
/*
Text: (<highlight>keyValuePair: Pair<Int, String></highlight>), Disabled: false, Strikeout: false, Green: false
Text: (<highlight>vararg values: Pair<Int, String></highlight>), Disabled: false, Strikeout: false, Green: false
Text: (<no parameters>), Disabled: true, Strikeout: false, Green: true
*/
@@ -0,0 +1,9 @@
fun test() {
mapOf(<caret>1 to "")
}
/*
Text: (<highlight>keyValuePair: Pair<Int, String></highlight>), Disabled: false, Strikeout: false, Green: true
Text: (<highlight>vararg values: Pair<Int, String></highlight>), Disabled: false, Strikeout: false, Green: false
Text: (<no parameters>), Disabled: true, Strikeout: false, Green: false
*/
@@ -0,0 +1,9 @@
fun <T1, T2> f(p: Int, t1: T1, t2: T2){}
fun test() {
f(<caret>1, "", 1)
}
/*
Text: (<highlight>p: Int</highlight>, t1: String, t2: Int), Disabled: false, Strikeout: false, Green: true
*/
@@ -0,0 +1,9 @@
fun <T1, T2> f(p: Int, t1: T1, t2: T2){}
fun test() {
f(1, "", <caret>)
}
/*
Text: (p: Int, t1: String, <highlight>t2: T2</highlight>), Disabled: false, Strikeout: false, Green: true
*/
@@ -0,0 +1,15 @@
fun <T1, T2> f(p: Int, t: T1, pair: Pair<T1, T2>){}
fun test() {
f(1, "", <caret>)
}
//TODO:
/*
Text: (p: Int, t: String, <highlight>pair: Pair<String, T2></highlight>), Disabled: false, Strikeout: false, Green: true
*/
//currently:
/*
Text: (p: Int, t: String, <highlight>pair: Pair<T1, T2></highlight>), Disabled: false, Strikeout: false, Green: true
*/
@@ -1,69 +0,0 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.parameterInfo;
import com.intellij.psi.PsiElement;
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
import org.jetbrains.kotlin.descriptors.FunctionDescriptor;
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase;
import org.jetbrains.kotlin.lexer.JetTokens;
import org.jetbrains.kotlin.psi.JetFile;
import org.jetbrains.kotlin.psi.JetValueArgumentList;
public abstract class AbstractFunctionParameterInfoTest extends LightCodeInsightFixtureTestCase {
@Override
protected void setUp() throws Exception {
super.setUp();
myFixture.setTestDataPath(PluginTestCaseBase.getTestDataPathBase() + "/parameterInfo/functionParameterInfo");
}
protected void doTest(String fileName) {
myFixture.configureByFile(fileName);
JetFile file = (JetFile) myFixture.getFile();
PsiElement lastChild = file.getLastChild();
assert lastChild != null;
String expectedResultText = null;
if (lastChild.getNode().getElementType().equals(JetTokens.BLOCK_COMMENT)) {
String lastChildText = lastChild.getText();
expectedResultText = lastChildText.substring(2, lastChildText.length() - 2).trim();
}
else if (lastChild.getNode().getElementType().equals(JetTokens.EOL_COMMENT)) {
expectedResultText = lastChild.getText().substring(2).trim();
}
assert expectedResultText != null;
KotlinFunctionParameterInfoHandler parameterInfoHandler = new KotlinFunctionParameterInfoHandler();
MockCreateParameterInfoContext mockCreateParameterInfoContext = new MockCreateParameterInfoContext(file, myFixture);
JetValueArgumentList parameterOwner = parameterInfoHandler.findElementForParameterInfo(mockCreateParameterInfoContext);
MockUpdateParameterInfoContext updateContext = new MockUpdateParameterInfoContext(file, myFixture);
//to update current parameter index
JetValueArgumentList elementForUpdating = parameterInfoHandler.findElementForUpdatingParameterInfo(updateContext);
if (elementForUpdating != null) {
parameterInfoHandler.updateParameterInfo(elementForUpdating, updateContext);
}
MockParameterInfoUIContext parameterInfoUIContext =
new MockParameterInfoUIContext(parameterOwner, updateContext.getCurrentParameter());
for (Object item : mockCreateParameterInfoContext.getItemsToShow()) {
//noinspection unchecked
parameterInfoHandler.updateUI((FunctionDescriptor)item, parameterInfoUIContext);
}
assertEquals(expectedResultText, parameterInfoUIContext.getResultText());
}
}
@@ -0,0 +1,69 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.parameterInfo
import com.intellij.psi.PsiWhiteSpace
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.idea.test.JetWithJdkAndRuntimeLightProjectDescriptor
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
import org.jetbrains.kotlin.lexer.JetTokens
import org.jetbrains.kotlin.psi.JetFile
import org.jetbrains.kotlin.psi.psiUtil.allChildren
import org.junit.Assert
abstract class AbstractFunctionParameterInfoTest : LightCodeInsightFixtureTestCase() {
override fun getProjectDescriptor() = JetWithJdkAndRuntimeLightProjectDescriptor.INSTANCE
override fun setUp() {
super.setUp()
myFixture.testDataPath = PluginTestCaseBase.getTestDataPathBase() + "/parameterInfo/functionParameterInfo"
}
protected fun doTest(fileName: String) {
myFixture.configureByFile(fileName)
val file = myFixture.file as JetFile
val lastChild = file.allChildren.filter { it !is PsiWhiteSpace }.last()
val expectedResultText = when (lastChild.node.elementType) {
JetTokens.BLOCK_COMMENT -> lastChild.text.substring(2, lastChild.text.length() - 2).trim()
JetTokens.EOL_COMMENT -> lastChild.text.substring(2).trim()
else -> error("Unexpected last file child")
}
val parameterInfoHandler = KotlinFunctionParameterInfoHandler()
val mockCreateParameterInfoContext = MockCreateParameterInfoContext(file, myFixture)
val parameterOwner = parameterInfoHandler.findElementForParameterInfo(mockCreateParameterInfoContext)
val updateContext = MockUpdateParameterInfoContext(file, myFixture)
//to update current parameter index
val elementForUpdating = parameterInfoHandler.findElementForUpdatingParameterInfo(updateContext)
if (elementForUpdating != null) {
parameterInfoHandler.updateParameterInfo(elementForUpdating, updateContext)
}
val parameterInfoUIContext = MockParameterInfoUIContext(parameterOwner, updateContext.currentParameter)
for (item in mockCreateParameterInfoContext.itemsToShow) {
//noinspection unchecked
parameterInfoHandler.updateUI(item as FunctionDescriptor, parameterInfoUIContext)
}
Assert.assertEquals(expectedResultText, parameterInfoUIContext.resultText)
}
}
@@ -47,6 +47,18 @@ public class FunctionParameterInfoTestGenerated extends AbstractFunctionParamete
doTest(fileName);
}
@TestMetadata("FunctionalValue1.kt")
public void testFunctionalValue1() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/parameterInfo/functionParameterInfo/FunctionalValue1.kt");
doTest(fileName);
}
@TestMetadata("FunctionalValue2.kt")
public void testFunctionalValue2() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/parameterInfo/functionParameterInfo/FunctionalValue2.kt");
doTest(fileName);
}
@TestMetadata("InheritedFunctions.kt")
public void testInheritedFunctions() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/parameterInfo/functionParameterInfo/InheritedFunctions.kt");
@@ -59,6 +71,12 @@ public class FunctionParameterInfoTestGenerated extends AbstractFunctionParamete
doTest(fileName);
}
@TestMetadata("Invoke.kt")
public void testInvoke() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/parameterInfo/functionParameterInfo/Invoke.kt");
doTest(fileName);
}
@TestMetadata("NamedAndDefaultParameter.kt")
public void testNamedAndDefaultParameter() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/parameterInfo/functionParameterInfo/NamedAndDefaultParameter.kt");
@@ -119,6 +137,42 @@ public class FunctionParameterInfoTestGenerated extends AbstractFunctionParamete
doTest(fileName);
}
@TestMetadata("SubstituteExpectedType.kt")
public void testSubstituteExpectedType() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/parameterInfo/functionParameterInfo/SubstituteExpectedType.kt");
doTest(fileName);
}
@TestMetadata("SubstituteExplicitTypeArgs.kt")
public void testSubstituteExplicitTypeArgs() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/parameterInfo/functionParameterInfo/SubstituteExplicitTypeArgs.kt");
doTest(fileName);
}
@TestMetadata("SubstituteFromParameters1.kt")
public void testSubstituteFromParameters1() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/parameterInfo/functionParameterInfo/SubstituteFromParameters1.kt");
doTest(fileName);
}
@TestMetadata("SubstituteFromParameters2.kt")
public void testSubstituteFromParameters2() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/parameterInfo/functionParameterInfo/SubstituteFromParameters2.kt");
doTest(fileName);
}
@TestMetadata("SubstituteFromParameters3.kt")
public void testSubstituteFromParameters3() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/parameterInfo/functionParameterInfo/SubstituteFromParameters3.kt");
doTest(fileName);
}
@TestMetadata("SubstituteFromParameters4.kt")
public void testSubstituteFromParameters4() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/parameterInfo/functionParameterInfo/SubstituteFromParameters4.kt");
doTest(fileName);
}
@TestMetadata("SuperConstructorCall.kt")
public void testSuperConstructorCall() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/parameterInfo/functionParameterInfo/SuperConstructorCall.kt");