Show parameter hints for ambiguous calls

This commit is contained in:
Dmitry Jemerov
2017-04-22 12:43:35 +02:00
parent 26445397bb
commit 108e97b70e
2 changed files with 66 additions and 11 deletions
@@ -24,11 +24,14 @@ import com.intellij.lang.Language
import com.intellij.lang.java.JavaLanguage
import com.intellij.psi.PsiElement
import com.intellij.psi.codeStyle.CodeStyleSettingsManager
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.core.formatter.KotlinCodeStyleSettings
import org.jetbrains.kotlin.idea.core.resolveCandidates
import org.jetbrains.kotlin.idea.intentions.SpecifyTypeExplicitlyIntention
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.getReturnTypeReference
import org.jetbrains.kotlin.lexer.KtTokens
@@ -38,8 +41,10 @@ import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.renderer.RenderingFormat
import org.jetbrains.kotlin.resolve.calls.callUtil.getCall
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
@@ -121,21 +126,32 @@ private enum class HintType(desc: String, enabled: Boolean) {
private fun provideParameterInfo(element: KtCallExpression): List<InlayInfo> {
val ctx = element.analyze(BodyResolveMode.PARTIAL)
element.getResolvedCall(ctx)?.let { resolvedCall ->
return resolvedCall.valueArguments.mapNotNull { (valueParam: ValueParameterDescriptor, resolvedArg) ->
resolvedArg.arguments.firstOrNull()?.let { arg ->
arg.getArgumentExpression()?.let { argExp ->
if (!arg.isNamed() && !valueParam.name.isSpecial && argExp.isUnclearExpression()) {
val prefix = if (valueParam.varargElementType != null) "..." else ""
return@mapNotNull InlayInfo(prefix + valueParam.name.identifier, argExp.startOffset)
}
val call = element.getCall(ctx) ?: return emptyList()
val candidates = call.resolveCandidates(ctx, element.getResolutionFacade())
if (candidates.isEmpty()) return emptyList()
candidates.singleOrNull()?.let { return getParameterInfoForCallCandidate(it) }
return candidates.map { getParameterInfoForCallCandidate(it) }.reduce { infos1, infos2 ->
for (index in infos1.indices) {
if (index >= infos2.size || infos1[index] != infos2[index]) {
return@reduce infos1.subList(0, index)
}
}
infos1
}
}
private fun getParameterInfoForCallCandidate(resolvedCall: ResolvedCall<out CallableDescriptor>): List<InlayInfo> {
return resolvedCall.valueArguments.mapNotNull { (valueParam: ValueParameterDescriptor, resolvedArg) ->
resolvedArg.arguments.firstOrNull()?.let { arg ->
arg.getArgumentExpression()?.let { argExp ->
if (!arg.isNamed() && !valueParam.name.isSpecial && argExp.isUnclearExpression()) {
val prefix = if (valueParam.varargElementType != null) "..." else ""
return@mapNotNull InlayInfo(prefix + valueParam.name.identifier, argExp.startOffset)
}
}
null
}
null
}
return emptyList()
}
private fun KtExpression.isUnclearExpression() = when(this) {
@@ -91,5 +91,44 @@ class InlayParameterHintsTest : KotlinLightCodeInsightFixtureTestCase() {
""")
}
fun `test inline positive and negative numbers`() {
check("""
fun main() {
val obj = Any()
count(<hint text="test:"/>-1, obj);
count(<hint text="test:"/>+1, obj);
}
fun count(test: Int, obj: Any) {
}
}
""")
}
fun `test show ambiguous`() {
check("""
fun main() {
test(<hint text="a:"/>10, x);
}
fun test(a: Int, bS: String) {}
fun test(a: Int, bI: Int) {}
""")
}
fun `test show ambiguous constructor`() {
check("""
fun main() {
X(<hint text="a:"/>10, x);
}
}
class X {
constructor(a: Int, bI: Int) {}
constructor(a: Int, bS: String) {}
}
""")
}
}