KT-4909 Smart completion does not work for named arguments

KT-7668 Named argument completion does not work after vararg

 #KT-4909 Fixed
 #KT-7668 Fixed
This commit is contained in:
Valentin Kipyatkov
2015-05-07 19:48:52 +03:00
parent 58ac497bd7
commit 67cfd9d516
45 changed files with 474 additions and 215 deletions
@@ -69,7 +69,7 @@ public class Concat : IntrinsicMethod() {
codegen: ExpressionCodegen codegen: ExpressionCodegen
): StackValue { ): StackValue {
return StackValue.operation(returnType) { return StackValue.operation(returnType) {
val arguments = resolvedCall.getCall().getValueArguments().map { it.getArgumentExpression() } val arguments = resolvedCall.getCall().getValueArguments().map { it.getArgumentExpression()!! }
val actualType = generateImpl( val actualType = generateImpl(
codegen, it, returnType, codegen, it, returnType,
resolvedCall.getCall().getCallElement(), resolvedCall.getCall().getCallElement(),
@@ -48,7 +48,7 @@ public interface Call {
@ReadOnly @ReadOnly
@NotNull @NotNull
List<JetFunctionLiteralArgument> getFunctionLiteralArguments(); List<? extends FunctionLiteralArgument> getFunctionLiteralArguments();
@ReadOnly @ReadOnly
@NotNull @NotNull
@@ -19,14 +19,14 @@ package org.jetbrains.kotlin.psi
import com.intellij.lang.ASTNode import com.intellij.lang.ASTNode
import com.intellij.psi.PsiWhiteSpace import com.intellij.psi.PsiWhiteSpace
public class JetFunctionLiteralArgument(node: ASTNode) : JetValueArgument(node) { public class JetFunctionLiteralArgument(node: ASTNode) : JetValueArgument(node), FunctionLiteralArgument {
private fun assertFL() = throw AssertionError("Function literal argument doesn't contain function literal expression: " + private fun assertFL() = throw AssertionError("Function literal argument doesn't contain function literal expression: " +
"${super.getArgumentExpression()?.getText()} (it should be guaranteed by parser)") "${super<JetValueArgument>.getArgumentExpression()?.getText()} (it should be guaranteed by parser)")
override fun getArgumentExpression() = super.getArgumentExpression() ?: assertFL() override fun getArgumentExpression() = super<JetValueArgument>.getArgumentExpression() ?: assertFL()
public fun getFunctionLiteral(): JetFunctionLiteralExpression = unpackFunctionLiteral(getArgumentExpression()) override fun getFunctionLiteral(): JetFunctionLiteralExpression = unpackFunctionLiteral(getArgumentExpression())
private fun unpackFunctionLiteral(expression: JetExpression?): JetFunctionLiteralExpression = private fun unpackFunctionLiteral(expression: JetExpression?): JetFunctionLiteralExpression =
when (expression) { when (expression) {
@@ -14,29 +14,29 @@
* limitations under the License. * limitations under the License.
*/ */
package org.jetbrains.kotlin.psi; package org.jetbrains.kotlin.psi
import com.intellij.psi.impl.source.tree.LeafPsiElement; import com.intellij.psi.impl.source.tree.LeafPsiElement
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public interface ValueArgument { public trait ValueArgument {
@Nullable IfNotParsed
@IfNotParsed public fun getArgumentExpression(): JetExpression?
JetExpression getArgumentExpression();
@Nullable public fun getArgumentName(): JetValueArgumentName?
JetValueArgumentName getArgumentName();
boolean isNamed(); public fun isNamed(): Boolean
@NotNull public fun asElement(): JetElement
JetElement asElement();
/* The '*' in something like foo(*arr) i.e. pass an array as a number of vararg arguments */ /* The '*' in something like foo(*arr) i.e. pass an array as a number of vararg arguments */
@Nullable public fun getSpreadElement(): LeafPsiElement?
LeafPsiElement getSpreadElement();
/* The argument is placed externally to call element, e.g. in 'when' condition with subject: 'when (a) { in c -> }' */ /* The argument is placed externally to call element, e.g. in 'when' condition with subject: 'when (a) { in c -> }' */
boolean isExternal(); public fun isExternal(): Boolean
}
public trait FunctionLiteralArgument : ValueArgument {
public fun getFunctionLiteral(): JetFunctionLiteralExpression
override fun getArgumentExpression(): JetExpression
} }
@@ -229,11 +229,11 @@ public class CallCompleter(
) { ) {
if (valueArgument.isExternal()) return if (valueArgument.isExternal()) return
val expression = valueArgument.getArgumentExpression() val expression = valueArgument.getArgumentExpression() ?: return
val deparenthesized = ArgumentTypeResolver.getLastElementDeparenthesized(expression, context) val deparenthesized = ArgumentTypeResolver.getLastElementDeparenthesized(expression, context)
if (deparenthesized == null) return if (deparenthesized == null) return
val recordedType = expression?.let { context.trace.getType(it) } val recordedType = expression.let { context.trace.getType(it) }
var updatedType: JetType? = recordedType var updatedType: JetType? = recordedType
val results = completeCallForArgument(deparenthesized, context) val results = completeCallForArgument(deparenthesized, context)
@@ -164,7 +164,7 @@ public class CallTransformer<D extends CallableDescriptor, F extends D> {
@NotNull @NotNull
@Override @Override
public List<JetFunctionLiteralArgument> getFunctionLiteralArguments() { public List<FunctionLiteralArgument> getFunctionLiteralArguments() {
return Collections.emptyList(); return Collections.emptyList();
} }
@@ -213,9 +213,9 @@ import static org.jetbrains.kotlin.resolve.calls.ValueArgumentsToParametersMappe
D candidate = candidateCall.getCandidateDescriptor(); D candidate = candidateCall.getCandidateDescriptor();
List<ValueParameterDescriptor> valueParameters = candidate.getValueParameters(); List<ValueParameterDescriptor> valueParameters = candidate.getValueParameters();
List<JetFunctionLiteralArgument> functionLiteralArguments = call.getFunctionLiteralArguments(); List<? extends FunctionLiteralArgument> functionLiteralArguments = call.getFunctionLiteralArguments();
if (!functionLiteralArguments.isEmpty()) { if (!functionLiteralArguments.isEmpty()) {
JetFunctionLiteralArgument functionLiteralArgument = functionLiteralArguments.get(0); FunctionLiteralArgument functionLiteralArgument = functionLiteralArguments.get(0);
JetExpression possiblyLabeledFunctionLiteral = functionLiteralArgument.getArgumentExpression(); JetExpression possiblyLabeledFunctionLiteral = functionLiteralArgument.getArgumentExpression();
if (valueParameters.isEmpty()) { if (valueParameters.isEmpty()) {
@@ -162,7 +162,7 @@ public class CallMaker {
@NotNull @NotNull
@Override @Override
public List<JetFunctionLiteralArgument> getFunctionLiteralArguments() { public List<FunctionLiteralArgument> getFunctionLiteralArguments() {
return Collections.emptyList(); return Collections.emptyList();
} }
@NotNull @NotNull
@@ -301,7 +301,7 @@ public class CallMaker {
@Override @Override
@NotNull @NotNull
public List<JetFunctionLiteralArgument> getFunctionLiteralArguments() { public List<? extends FunctionLiteralArgument> getFunctionLiteralArguments() {
return callElement.getFunctionLiteralArguments(); return callElement.getFunctionLiteralArguments();
} }
@@ -72,7 +72,7 @@ public class DelegatingCall implements Call {
@Override @Override
@NotNull @NotNull
public List<JetFunctionLiteralArgument> getFunctionLiteralArguments() { public List<? extends FunctionLiteralArgument> getFunctionLiteralArguments() {
return delegate.getFunctionLiteralArguments(); return delegate.getFunctionLiteralArguments();
} }
@@ -214,7 +214,7 @@ public class ControlStructureTypingUtils {
@NotNull @NotNull
@Override @Override
public List<JetFunctionLiteralArgument> getFunctionLiteralArguments() { public List<FunctionLiteralArgument> getFunctionLiteralArguments() {
return Collections.emptyList(); return Collections.emptyList();
} }
@@ -23,6 +23,7 @@ import com.intellij.patterns.ElementPattern
import com.intellij.patterns.StandardPatterns import com.intellij.patterns.StandardPatterns
import com.intellij.psi.PsiCompiledElement import com.intellij.psi.PsiCompiledElement
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.impl.source.tree.LeafPsiElement
import com.intellij.psi.search.DelegatingGlobalSearchScope import com.intellij.psi.search.DelegatingGlobalSearchScope
import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.util.PsiTreeUtil import com.intellij.psi.util.PsiTreeUtil
@@ -50,6 +51,7 @@ import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo
import org.jetbrains.kotlin.resolve.calls.callUtil.getCall import org.jetbrains.kotlin.resolve.calls.callUtil.getCall
import org.jetbrains.kotlin.resolve.calls.smartcasts.SmartCastUtils import org.jetbrains.kotlin.resolve.calls.smartcasts.SmartCastUtils
import org.jetbrains.kotlin.resolve.calls.util.DelegatingCall
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindExclude import org.jetbrains.kotlin.resolve.scopes.DescriptorKindExclude
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
@@ -400,21 +402,7 @@ class SmartCompletionSession(configuration: CompletionSessionConfiguration, para
if (expression != null) { if (expression != null) {
val mapper = ToFromOriginalFileMapper(parameters.getOriginalFile() as JetFile, position.getContainingFile() as JetFile, parameters.getOffset()) val mapper = ToFromOriginalFileMapper(parameters.getOriginalFile() as JetFile, position.getContainingFile() as JetFile, parameters.getOffset())
// special completion for outside parenthesis lambda argument addFunctionLiteralArgumentCompletions()
if (reference != null) {
val receiverData = ReferenceVariantsHelper.getExplicitReceiverData(reference.expression)
if (receiverData != null && receiverData.second == CallType.INFIX) {
val call = receiverData.first.getCall(bindingContext)
if (call != null && call.getFunctionLiteralArguments().isEmpty()) {
val argumentIndex = call.getValueArguments().size()
val expectedInfos = ExpectedInfos(bindingContext, resolutionFacade, moduleDescriptor, true)
.calculateForArgument(call, argumentIndex, true)
if (expectedInfos != null) {
collector.addElements(LambdaItems.collect(expectedInfos))
}
}
}
}
val completion = SmartCompletion(expression, resolutionFacade, moduleDescriptor, val completion = SmartCompletion(expression, resolutionFacade, moduleDescriptor,
bindingContext, { isVisibleDescriptor(it) }, inDescriptor, prefixMatcher, originalSearchScope, bindingContext, { isVisibleDescriptor(it) }, inDescriptor, prefixMatcher, originalSearchScope,
@@ -448,6 +436,39 @@ class SmartCompletionSession(configuration: CompletionSessionConfiguration, para
} }
} }
// special completion for outside parenthesis lambda argument
private fun addFunctionLiteralArgumentCompletions() {
if (reference != null) {
val receiverData = ReferenceVariantsHelper.getExplicitReceiverData(reference.expression)
if (receiverData != null && receiverData.second == CallType.INFIX) {
val call = receiverData.first.getCall(bindingContext)
if (call != null && call.getFunctionLiteralArguments().isEmpty()) {
val dummyArgument = object : FunctionLiteralArgument {
override fun getFunctionLiteral() = throw UnsupportedOperationException()
override fun getArgumentExpression() = throw UnsupportedOperationException()
override fun getArgumentName(): JetValueArgumentName? = null
override fun isNamed() = false
override fun asElement() = throw UnsupportedOperationException()
override fun getSpreadElement(): LeafPsiElement? = null
override fun isExternal() = false
}
val dummyArguments = call!!.getValueArguments() + listOf(dummyArgument)
val dummyCall = object : DelegatingCall(call) {
override fun getValueArguments() = dummyArguments
override fun getFunctionLiteralArguments() = listOf(dummyArgument)
override fun getValueArgumentList() = throw UnsupportedOperationException()
}
val expectedInfos = ExpectedInfos(bindingContext, resolutionFacade, moduleDescriptor, true)
.calculateForArgument(dummyCall, dummyArgument)
if (expectedInfos != null) {
collector.addElements(LambdaItems.collect(expectedInfos))
}
}
}
}
}
private fun processNonImported(processor: (DeclarationDescriptor) -> Unit) { private fun processNonImported(processor: (DeclarationDescriptor) -> Unit) {
getTopLevelExtensions().forEach(processor) getTopLevelExtensions().forEach(processor)
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.di.InjectorForMacros import org.jetbrains.kotlin.di.InjectorForMacros
import org.jetbrains.kotlin.idea.caches.resolve.ResolutionFacade import org.jetbrains.kotlin.idea.caches.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.completion.smart.toList import org.jetbrains.kotlin.idea.completion.smart.toList
import org.jetbrains.kotlin.idea.core.mapArgumentsToParameters
import org.jetbrains.kotlin.idea.util.makeNotNullable import org.jetbrains.kotlin.idea.util.makeNotNullable
import org.jetbrains.kotlin.lexer.JetTokens import org.jetbrains.kotlin.lexer.JetTokens
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
@@ -29,6 +30,7 @@ import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DelegatingBindingTrace import org.jetbrains.kotlin.resolve.DelegatingBindingTrace
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo
import org.jetbrains.kotlin.resolve.bindingContextUtil.getTargetFunctionDescriptor import org.jetbrains.kotlin.resolve.bindingContextUtil.getTargetFunctionDescriptor
import org.jetbrains.kotlin.resolve.calls.callUtil.getCall
import org.jetbrains.kotlin.resolve.calls.callUtil.noErrorsInValueArguments import org.jetbrains.kotlin.resolve.calls.callUtil.noErrorsInValueArguments
import org.jetbrains.kotlin.resolve.calls.checkers.AdditionalTypeChecker import org.jetbrains.kotlin.resolve.calls.checkers.AdditionalTypeChecker
import org.jetbrains.kotlin.resolve.calls.checkers.CompositeChecker import org.jetbrains.kotlin.resolve.calls.checkers.CompositeChecker
@@ -62,18 +64,24 @@ data class ItemOptions(val starPrefix: Boolean) {
} }
} }
open data class ExpectedInfo(val type: JetType, val name: String?, val tail: Tail?, val itemOptions: ItemOptions = ItemOptions.DEFAULT) open data class ExpectedInfo(val type: JetType, val expectedName: String?, val tail: Tail?, val itemOptions: ItemOptions = ItemOptions.DEFAULT)
class PositionalArgumentExpectedInfo(type: JetType, name: String?, tail: Tail?, val function: FunctionDescriptor, val parameterIndex: Int, itemOptions: ItemOptions = ItemOptions.DEFAULT) data class ArgumentPosition(val argumentIndex: Int, val argumentName: String?, val isFunctionLiteralArgument: Boolean) {
constructor(argumentIndex: Int, isFunctionLiteralArgument: Boolean = false) : this(argumentIndex, null, isFunctionLiteralArgument)
constructor(argumentIndex: Int, argumentName: String?) : this(argumentIndex, argumentName, false)
}
class ArgumentExpectedInfo(type: JetType, name: String?, tail: Tail?, val function: FunctionDescriptor, val position: ArgumentPosition, itemOptions: ItemOptions = ItemOptions.DEFAULT)
: ExpectedInfo(type, name, tail, itemOptions) { : ExpectedInfo(type, name, tail, itemOptions) {
override fun equals(other: Any?) override fun equals(other: Any?)
= other is PositionalArgumentExpectedInfo && super.equals(other) && function == other.function && parameterIndex == other.parameterIndex = other is ArgumentExpectedInfo && super.equals(other) && function == other.function && position == other.position
override fun hashCode() override fun hashCode()
= function.hashCode() = function.hashCode()
} }
class ExpectedInfos( class ExpectedInfos(
val bindingContext: BindingContext, val bindingContext: BindingContext,
val resolutionFacade: ResolutionFacade, val resolutionFacade: ResolutionFacade,
@@ -97,67 +105,42 @@ class ExpectedInfos(
private fun calculateForArgument(expressionWithType: JetExpression): Collection<ExpectedInfo>? { private fun calculateForArgument(expressionWithType: JetExpression): Collection<ExpectedInfo>? {
val argument = expressionWithType.getParent() as? JetValueArgument ?: return null val argument = expressionWithType.getParent() as? JetValueArgument ?: return null
if (argument.isNamed()) return null //TODO - support named arguments (also do not forget to check for presence of named arguments before)
val argumentList = argument.getParent() as? JetValueArgumentList ?: return null val argumentList = argument.getParent() as? JetValueArgumentList ?: return null
val argumentIndex = argumentList.getArguments().indexOf(argument)
val callElement = argumentList.getParent() as? JetCallElement ?: return null val callElement = argumentList.getParent() as? JetCallElement ?: return null
return calculateForArgument(callElement, argumentIndex, false) return calculateForArgument(callElement, argument)
} }
private fun calculateForFunctionLiteralArgument(expressionWithType: JetExpression): Collection<ExpectedInfo>? { private fun calculateForFunctionLiteralArgument(expressionWithType: JetExpression): Collection<ExpectedInfo>? {
val functionLiteralArgument = expressionWithType.getParent() as? JetFunctionLiteralArgument val functionLiteralArgument = expressionWithType.getParent() as? JetFunctionLiteralArgument
val callExpression = functionLiteralArgument?.getParent() as? JetCallExpression val callExpression = functionLiteralArgument?.getParent() as? JetCallExpression ?: return null
if (callExpression != null) { val literalArgument = callExpression.getFunctionLiteralArguments().firstOrNull() ?: return null
if (callExpression.getFunctionLiteralArguments().firstOrNull()?.getArgumentExpression() == expressionWithType) { if (literalArgument.getArgumentExpression() != expressionWithType) return null
return calculateForArgument(callExpression, callExpression.getValueArguments().size() - 1, true) return calculateForArgument(callExpression, literalArgument)
}
}
return null
} }
private fun calculateForArgument(callElement: JetCallElement, argumentIndex: Int, isFunctionLiteralArgument: Boolean): Collection<ExpectedInfo>? { private fun calculateForArgument(callElement: JetCallElement, argument: ValueArgument): Collection<ExpectedInfo>? {
val parent = callElement.getParent() val call = callElement.getCall(bindingContext) ?: return null
val receiver: ReceiverValue return calculateForArgument(call, argument)
val callOperationNode: ASTNode?
if (parent is JetQualifiedExpression && callElement == parent.getSelectorExpression()) {
val receiverExpression = parent.getReceiverExpression()
val expressionType = bindingContext.getType(receiverExpression)
val qualifier = bindingContext[BindingContext.QUALIFIER, receiverExpression]
if (expressionType != null) {
receiver = ExpressionReceiver(receiverExpression, expressionType)
callOperationNode = parent.getOperationTokenNode()
}
else if (qualifier != null) {
receiver = qualifier
callOperationNode = null
}
else {
return null
}
}
else {
receiver = ReceiverValue.NO_RECEIVER
callOperationNode = null
}
val call = CallMaker.makeCall(receiver, callOperationNode, callElement)
return calculateForArgument(call, argumentIndex, isFunctionLiteralArgument)
} }
public fun calculateForArgument(call: Call, argumentIndex: Int, isFunctionLiteralArgument: Boolean): Collection<ExpectedInfo>? { public fun calculateForArgument(call: Call, argument: ValueArgument): Collection<ExpectedInfo>? {
val argumentIndex = call.getValueArguments().indexOf(argument)
assert(argumentIndex >= 0)
val argumentName = argument.getArgumentName()?.getReferenceExpression()?.getReferencedName()
val isFunctionLiteralArgument = argument is FunctionLiteralArgument
val argumentPosition = ArgumentPosition(argumentIndex, argumentName, isFunctionLiteralArgument)
val callElement = call.getCallElement() val callElement = call.getCallElement()
val calleeExpression = call.getCalleeExpression() val calleeExpression = call.getCalleeExpression()
val truncatedCall = if (!isFunctionLiteralArgument) { // leave only arguments before the current one // leave only arguments before the current one
object : DelegatingCall(call) { val truncatedCall = object : DelegatingCall(call) {
override fun getValueArguments() = super.getValueArguments().subList(0, argumentIndex) val arguments = call.getValueArguments().subList(0, argumentIndex)
override fun getValueArgumentList() = null
}
}
else {
call
}
override fun getValueArguments() = arguments
override fun getFunctionLiteralArguments() = emptyList<FunctionLiteralArgument>()
override fun getValueArgumentList() = null
}
val resolutionScope = bindingContext[BindingContext.RESOLUTION_SCOPE, calleeExpression] ?: return null //TODO: discuss it val resolutionScope = bindingContext[BindingContext.RESOLUTION_SCOPE, calleeExpression] ?: return null //TODO: discuss it
val expectedType = (callElement as? JetExpression)?.let { bindingContext[BindingContext.EXPECTED_EXPRESSION_TYPE, it] } ?: TypeUtils.NO_EXPECTED_TYPE val expectedType = (callElement as? JetExpression)?.let { bindingContext[BindingContext.EXPECTED_EXPRESSION_TYPE, it] } ?: TypeUtils.NO_EXPECTED_TYPE
@@ -185,36 +168,30 @@ class ExpectedInfos(
val parameters = descriptor.getValueParameters() val parameters = descriptor.getValueParameters()
if (parameters.isEmpty()) continue if (parameters.isEmpty()) continue
val parameterIndex = if (isFunctionLiteralArgument) { val argumentToParameter = call.mapArgumentsToParameters(descriptor)
parameters.lastIndex val parameter = argumentToParameter[argument] ?: continue
}
else {
val varArgIndex = parameters.indexOfFirst { it.getVarargElementType() != null }
if (varArgIndex < 0) {
if (parameters.size() <= argumentIndex) continue
argumentIndex
}
else {
if (argumentIndex < varArgIndex) argumentIndex else varArgIndex
}
}
val thisReceiver = ExpressionTypingUtils.normalizeReceiverValueForVisibility(candidate.getDispatchReceiver(), bindingContext) val thisReceiver = ExpressionTypingUtils.normalizeReceiverValueForVisibility(candidate.getDispatchReceiver(), bindingContext)
if (!Visibilities.isVisible(thisReceiver, descriptor, resolutionScope.getContainingDeclaration())) continue if (!Visibilities.isVisible(thisReceiver, descriptor, resolutionScope.getContainingDeclaration())) continue
val parameter = parameters[parameterIndex]
val varargElementType = parameter.getVarargElementType()
val expectedName = if (descriptor.hasSynthesizedParameterNames()) null else parameter.getName().asString() val expectedName = if (descriptor.hasSynthesizedParameterNames()) null else parameter.getName().asString()
val varargElementType = parameter.getVarargElementType()
if (varargElementType != null) { if (varargElementType != null) {
if (isFunctionLiteralArgument) continue if (isFunctionLiteralArgument) continue
expectedInfos.add(PositionalArgumentExpectedInfo(varargElementType, expectedName?.fromPlural(), null, descriptor, parameterIndex)) if (argumentName == null) {
expectedInfos.add(ArgumentExpectedInfo(varargElementType, expectedName?.fromPlural(), null, descriptor, argumentPosition))
if (argumentIndex == parameterIndex) { if (argumentIndex == parameters.indexOf(parameter)) {
val tail = if (parameterIndex == parameters.lastIndex) Tail.RPARENTH else null val tail = if (parameter == parameters.last()) Tail.RPARENTH else null
expectedInfos.add(PositionalArgumentExpectedInfo(parameter.getType(), expectedName, tail, descriptor, parameterIndex, ItemOptions.STAR_PREFIX)) expectedInfos.add(ArgumentExpectedInfo(parameter.getType(), expectedName, tail, descriptor, argumentPosition, ItemOptions.STAR_PREFIX))
}
}
else {
val tail = namedArgumentTail(argumentToParameter, argumentName, descriptor)
expectedInfos.add(ArgumentExpectedInfo(varargElementType, expectedName?.fromPlural(), tail, descriptor, argumentPosition))
expectedInfos.add(ArgumentExpectedInfo(parameter.getType(), expectedName, tail, descriptor, argumentPosition, ItemOptions.STAR_PREFIX))
} }
} }
else { else {
@@ -228,28 +205,47 @@ class ExpectedInfos(
return true return true
} }
val tail = if (isFunctionLiteralArgument)
null
else if (parameterIndex == parameters.lastIndex)
Tail.RPARENTH //TODO: support square brackets
else if (parameters.drop(parameterIndex + 1).none(::needCommaForParameter))
null
else
Tail.COMMA
val parameterType = if (useHeuristicSignatures) val parameterType = if (useHeuristicSignatures)
HeuristicSignatures.correctedParameterType(descriptor, argumentIndex, moduleDescriptor, callElement.getProject()) ?: parameter.getType() HeuristicSignatures.correctedParameterType(descriptor, parameter, moduleDescriptor, callElement.getProject()) ?: parameter.getType()
else else
parameter.getType() parameter.getType()
if (isFunctionLiteralArgument && !KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(parameterType)) continue if (isFunctionLiteralArgument) {
if (KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(parameterType)) {
expectedInfos.add(ArgumentExpectedInfo(parameterType, expectedName, null, descriptor, argumentPosition))
}
}
else {
val tail = if (argumentName == null) {
if (parameter == parameters.last())
Tail.RPARENTH //TODO: support square brackets
else if (parameters.dropWhile { it != parameter }.drop(1).none(::needCommaForParameter))
null
else
Tail.COMMA
}
else {
namedArgumentTail(argumentToParameter, argumentName, descriptor)
}
expectedInfos.add(PositionalArgumentExpectedInfo(parameterType, expectedName, tail, descriptor, parameterIndex)) expectedInfos.add(ArgumentExpectedInfo(parameterType, expectedName, tail, descriptor, argumentPosition))
}
} }
} }
return expectedInfos return expectedInfos
} }
private fun namedArgumentTail(argumentToParameter: Map<ValueArgument, ValueParameterDescriptor>, argumentName: String, descriptor: FunctionDescriptor): Tail? {
val usedParameterNames = (argumentToParameter.values().map { it.getName().asString() } + listOf(argumentName)).toSet()
val notUsedParameters = descriptor.getValueParameters().filter { it.getName().asString() !in usedParameterNames }
return if (notUsedParameters.isEmpty())
Tail.RPARENTH
else if (notUsedParameters.all { it.hasDefaultValue() })
null
else
Tail.COMMA
}
private fun calculateForEqAndAssignment(expressionWithType: JetExpression): Collection<ExpectedInfo>? { private fun calculateForEqAndAssignment(expressionWithType: JetExpression): Collection<ExpectedInfo>? {
val binaryExpression = expressionWithType.getParent() as? JetBinaryExpression val binaryExpression = expressionWithType.getParent() as? JetBinaryExpression
if (binaryExpression != null) { if (binaryExpression != null) {
@@ -271,7 +267,7 @@ class ExpectedInfos(
return when (expressionWithType) { return when (expressionWithType) {
ifExpression.getCondition() -> listOf(ExpectedInfo(KotlinBuiltIns.getInstance().getBooleanType(), null, Tail.RPARENTH)) ifExpression.getCondition() -> listOf(ExpectedInfo(KotlinBuiltIns.getInstance().getBooleanType(), null, Tail.RPARENTH))
ifExpression.getThen() -> calculate(ifExpression)?.map { ExpectedInfo(it.type, it.name, Tail.ELSE) } ifExpression.getThen() -> calculate(ifExpression)?.map { ExpectedInfo(it.type, it.expectedName, Tail.ELSE) }
ifExpression.getElse() -> { ifExpression.getElse() -> {
val ifExpectedInfo = calculate(ifExpression) val ifExpectedInfo = calculate(ifExpression)
@@ -312,7 +308,7 @@ class ExpectedInfos(
private fun calculateForBlockExpression(expressionWithType: JetExpression): Collection<ExpectedInfo>? { private fun calculateForBlockExpression(expressionWithType: JetExpression): Collection<ExpectedInfo>? {
val block = expressionWithType.getParent() as? JetBlockExpression ?: return null val block = expressionWithType.getParent() as? JetBlockExpression ?: return null
if (expressionWithType != block.getStatements().last()) return null if (expressionWithType != block.getStatements().last()) return null
return calculate(block)?.map { ExpectedInfo(it.type, it.name, null) } return calculate(block)?.map { ExpectedInfo(it.type, it.expectedName, null) }
} }
private fun calculateForWhenEntryValue(expressionWithType: JetExpression): Collection<ExpectedInfo>? { private fun calculateForWhenEntryValue(expressionWithType: JetExpression): Collection<ExpectedInfo>? {
@@ -16,24 +16,25 @@
package org.jetbrains.kotlin.idea.completion package org.jetbrains.kotlin.idea.completion
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import java.util.HashMap import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.di.InjectorForMacros
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.types.SubstitutionUtils
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.di.InjectorForMacros
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.resolve.JetModuleUtil
import org.jetbrains.kotlin.psi.JetPsiFactory import org.jetbrains.kotlin.psi.JetPsiFactory
import org.jetbrains.kotlin.resolve.BindingTraceContext import org.jetbrains.kotlin.resolve.BindingTraceContext
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.scopes.JetScope import org.jetbrains.kotlin.resolve.JetModuleUtil
import org.jetbrains.kotlin.resolve.scopes.ChainedScope import org.jetbrains.kotlin.resolve.scopes.ChainedScope
import org.jetbrains.kotlin.resolve.scopes.JetScope
import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.types.SubstitutionUtils
import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.Variance
import java.util.HashMap
public object HeuristicSignatures { public object HeuristicSignatures {
private val signatures = HashMap<Pair<FqName, Name>, List<String>>() private val signatures = HashMap<Pair<FqName, Name>, List<String>>()
@@ -59,7 +60,13 @@ public object HeuristicSignatures {
signatures[FqName(classFqName) to Name.identifier(name)] = parameterTypes.toList() signatures[FqName(classFqName) to Name.identifier(name)] = parameterTypes.toList()
} }
public fun correctedParameterType(function: FunctionDescriptor, parameterIndex: Int, moduleDescriptor: ModuleDescriptor, project: Project): JetType? { public fun correctedParameterType(function: FunctionDescriptor, parameter: ValueParameterDescriptor, moduleDescriptor: ModuleDescriptor, project: Project): JetType? {
val parameterIndex = function.getValueParameters().indexOf(parameter)
assert(parameterIndex >= 0)
return correctedParameterType(function, parameterIndex, moduleDescriptor, project)
}
private fun correctedParameterType(function: FunctionDescriptor, parameterIndex: Int, moduleDescriptor: ModuleDescriptor, project: Project): JetType? {
val ownerType = function.getDispatchReceiverParameter()?.getType() ?: return null val ownerType = function.getDispatchReceiverParameter()?.getType() ?: return null
val superFunctions = function.getOverriddenDescriptors() val superFunctions = function.getOverriddenDescriptors()
@@ -29,7 +29,7 @@ import org.jetbrains.kotlin.core.FirstChildInParentFilter
import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.idea.JetIcons import org.jetbrains.kotlin.idea.JetIcons
import org.jetbrains.kotlin.idea.completion.handlers.WithTailInsertHandler import org.jetbrains.kotlin.idea.completion.handlers.WithTailInsertHandler
import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil import org.jetbrains.kotlin.idea.core.mapArgumentsToParameters
import org.jetbrains.kotlin.idea.references.JetReference import org.jetbrains.kotlin.idea.references.JetReference
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.lexer.JetTokens import org.jetbrains.kotlin.lexer.JetTokens
@@ -41,6 +41,8 @@ import org.jetbrains.kotlin.psi.psiUtil.getCallNameExpression
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getCall
import java.util.*
object NamedParametersCompletion { object NamedParametersCompletion {
private val positionFilter = AndFilter( private val positionFilter = AndFilter(
@@ -83,18 +85,20 @@ object NamedParametersCompletion {
for (funDescriptor in functionDescriptors) { for (funDescriptor in functionDescriptors) {
if (!funDescriptor.hasStableParameterNames()) continue if (!funDescriptor.hasStableParameterNames()) continue
val call = callElement.getCall(bindingContext) ?: continue
val usedArguments = QuickFixUtil.getUsedParameters(callElement, valueArgument, funDescriptor) var argumentToParameter = HashMap(call.mapArgumentsToParameters(funDescriptor))
argumentToParameter.remove(valueArgument)
val usedParameters = argumentToParameter.values().toSet()
for (parameter in funDescriptor.getValueParameters()) { for (parameter in funDescriptor.getValueParameters()) {
val name = parameter.getName() if (parameter !in usedParameters) {
val nameString = name.asString() val name = parameter.getName().asString()
if (nameString !in usedArguments) { val lookupElement = LookupElementBuilder.create(name)
val lookupElement = LookupElementBuilder.create(nameString) .withPresentableText("$name =")
.withPresentableText("$nameString =")
.withTailText(" ${DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(parameter.getType())}") .withTailText(" ${DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(parameter.getType())}")
.withIcon(JetIcons.PARAMETER) .withIcon(JetIcons.PARAMETER)
.withInsertHandler(NamedParameterInsertHandler(name)) .withInsertHandler(NamedParameterInsertHandler(parameter.getName()))
.assignPriority(ItemPriority.NAMED_PARAMETER) .assignPriority(ItemPriority.NAMED_PARAMETER)
collector.addElement(lookupElement) collector.addElement(lookupElement)
} }
@@ -16,26 +16,22 @@
package org.jetbrains.kotlin.idea.completion.smart package org.jetbrains.kotlin.idea.completion.smart
import org.jetbrains.kotlin.idea.completion.ExpectedInfo
import org.jetbrains.kotlin.psi.JetExpression
import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.ui.LayeredIcon
import com.intellij.codeInsight.lookup.LookupElementBuilder
import org.jetbrains.kotlin.idea.completion.Tail
import org.jetbrains.kotlin.idea.completion.ItemPriority
import org.jetbrains.kotlin.resolve.BindingContext
import java.util.HashSet
import org.jetbrains.kotlin.resolve.scopes.JetScope
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.idea.JetDescriptorIconProvider
import org.jetbrains.kotlin.descriptors.VariableDescriptor
import org.jetbrains.kotlin.idea.completion.PositionalArgumentExpectedInfo
import java.util.ArrayList
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.idea.completion.assignPriority
import com.intellij.codeInsight.lookup.Lookup import com.intellij.codeInsight.lookup.Lookup
import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.codeInsight.lookup.LookupElementBuilder
import com.intellij.ui.LayeredIcon
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.descriptors.VariableDescriptor
import org.jetbrains.kotlin.idea.JetDescriptorIconProvider
import org.jetbrains.kotlin.idea.completion.*
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.psi.JetExpression
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.scopes.JetScope
import org.jetbrains.kotlin.types.JetType import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.types.checker.JetTypeChecker import org.jetbrains.kotlin.types.checker.JetTypeChecker
import java.util.ArrayList
import java.util.HashSet
class MultipleArgumentsItemProvider(val bindingContext: BindingContext, class MultipleArgumentsItemProvider(val bindingContext: BindingContext,
val smartCastTypes: (VariableDescriptor) -> Collection<JetType>) { val smartCastTypes: (VariableDescriptor) -> Collection<JetType>) {
@@ -47,7 +43,7 @@ class MultipleArgumentsItemProvider(val bindingContext: BindingContext,
val added = HashSet<String>() val added = HashSet<String>()
for (expectedInfo in expectedInfos) { for (expectedInfo in expectedInfos) {
if (expectedInfo is PositionalArgumentExpectedInfo && expectedInfo.parameterIndex == 0) { if (expectedInfo is ArgumentExpectedInfo && expectedInfo.position == ArgumentPosition(0)) {
val parameters = expectedInfo.function.getValueParameters() val parameters = expectedInfo.function.getValueParameters()
if (parameters.size() > 1) { if (parameters.size() > 1) {
val variables = ArrayList<VariableDescriptor>() val variables = ArrayList<VariableDescriptor>()
@@ -32,7 +32,7 @@ object NameSimilarityWeigher : LookupElementWeigher("kotlin.nameSimilarity") {
fun calcNameSimilarity(name: String, expectedInfos: Collection<ExpectedInfo>): Int { fun calcNameSimilarity(name: String, expectedInfos: Collection<ExpectedInfo>): Int {
return expectedInfos return expectedInfos
.map { it.name } .map { it.expectedName }
.filterNotNull() .filterNotNull()
.map { calcNameSimilarity(name, it) } .map { calcNameSimilarity(name, it) }
.max() ?: 0 .max() ?: 0
@@ -130,7 +130,7 @@ class SmartCompletion(
// if we complete argument of == or !=, make types in expected info's nullable to allow nullable items too // if we complete argument of == or !=, make types in expected info's nullable to allow nullable items too
val expectedInfos = if ((expressionWithType.getParent() as? JetBinaryExpression)?.getOperationToken() in COMPARISON_TOKENS) val expectedInfos = if ((expressionWithType.getParent() as? JetBinaryExpression)?.getOperationToken() in COMPARISON_TOKENS)
filteredExpectedInfos.map { ExpectedInfo(it.type.makeNullable(), it.name, it.tail) } filteredExpectedInfos.map { ExpectedInfo(it.type.makeNullable(), it.expectedName, it.tail) }
else else
filteredExpectedInfos filteredExpectedInfos
@@ -58,7 +58,7 @@ class TypesWithContainsDetector(
private fun isGoodContainsFunction(function: FunctionDescriptor, freeTypeParams: Collection<TypeParameterDescriptor>): Boolean { private fun isGoodContainsFunction(function: FunctionDescriptor, freeTypeParams: Collection<TypeParameterDescriptor>): Boolean {
if (!TypeUtils.equalTypes(function.getReturnType(), booleanType)) return false if (!TypeUtils.equalTypes(function.getReturnType(), booleanType)) return false
val parameter = function.getValueParameters().singleOrNull() ?: return false val parameter = function.getValueParameters().singleOrNull() ?: return false
val parameterType = HeuristicSignatures.correctedParameterType(function, 0, moduleDescriptor, project) ?: parameter.getType() val parameterType = HeuristicSignatures.correctedParameterType(function, parameter, moduleDescriptor, project) ?: parameter.getType()
val fuzzyParameterType = FuzzyType(parameterType, function.getTypeParameters() + freeTypeParams) val fuzzyParameterType = FuzzyType(parameterType, function.getTypeParameters() + freeTypeParams)
return fuzzyParameterType.checkIsSuperTypeOf(argumentType) != null return fuzzyParameterType.checkIsSuperTypeOf(argumentType) != null
} }
@@ -107,7 +107,7 @@ fun LookupElement.withOptions(options: ItemOptions): LookupElement {
val token = context.getFile().findElementAt(offset)!! val token = context.getFile().findElementAt(offset)!!
val argument = token.getStrictParentOfType<JetValueArgument>() val argument = token.getStrictParentOfType<JetValueArgument>()
if (argument != null) { if (argument != null) {
context.getDocument().insertString(argument.getTextRange().getStartOffset(), "*") context.getDocument().insertString(argument.getArgumentExpression()!!.getTextRange().getStartOffset(), "*")
} }
} }
} }
@@ -0,0 +1,7 @@
fun foo(vararg strings: String, option: String = ""){ }
fun bar(s: String){
foo("", "", <caret>)
}
// EXIST: { lookupString:"option", itemText:"option =" }
@@ -0,0 +1,7 @@
fun foo(vararg v: Int, s: String) { }
fun bar(s: String) {
foo(*intArrayOf(), <caret>)
}
// ELEMENT: s
@@ -0,0 +1,7 @@
fun foo(vararg v: Int, s: String) { }
fun bar(s: String) {
foo(*intArrayOf(), s)<caret>
}
// ELEMENT: s
@@ -0,0 +1,7 @@
fun foo(param1: String, param2: Int) { }
fun bar(pInt: Int) {
foo(param2 = <caret>)
}
// ELEMENT: pInt
@@ -0,0 +1,7 @@
fun foo(param1: String, param2: Int) { }
fun bar(pInt: Int) {
foo(param2 = pInt, <caret>)
}
// ELEMENT: pInt
@@ -0,0 +1,7 @@
fun foo(param1: String = "", param2: Int, param3: Int = 0) { }
fun bar(pInt: Int) {
foo(param2 = <caret>)
}
// ELEMENT: pInt
@@ -0,0 +1,7 @@
fun foo(param1: String = "", param2: Int, param3: Int = 0) { }
fun bar(pInt: Int) {
foo(param2 = pInt<caret>)
}
// ELEMENT: pInt
@@ -0,0 +1,7 @@
fun foo(param1: String, param2: Int, param3: Int) { }
fun bar(pInt: Int) {
foo("", param2 = 1, param3 = <caret>)
}
// ELEMENT: pInt
@@ -0,0 +1,7 @@
fun foo(param1: String, param2: Int, param3: Int) { }
fun bar(pInt: Int) {
foo("", param2 = 1, param3 = pInt)<caret>
}
// ELEMENT: pInt
@@ -0,0 +1,7 @@
fun foo(param1: String, vararg param2: Int) { }
fun bar(arr: IntArray) {
foo("", param2 = <caret>)
}
// ELEMENT: arr
@@ -0,0 +1,7 @@
fun foo(param1: String, vararg param2: Int) { }
fun bar(arr: IntArray) {
foo("", param2 = *arr)<caret>
}
// ELEMENT: arr
@@ -0,0 +1,7 @@
fun foo(param1: String, vararg param2: Int) { }
fun bar(p: Int) {
foo("", param2 = <caret>)
}
// ELEMENT: p
@@ -0,0 +1,7 @@
fun foo(param1: String, vararg param2: Int) { }
fun bar(p: Int) {
foo("", param2 = p)<caret>
}
// ELEMENT: p
@@ -0,0 +1,7 @@
fun foo(param1: String, vararg param2: Int) { }
fun bar(arr: IntArray) {
foo(param2 = <caret>)
}
// ELEMENT: arr
@@ -0,0 +1,7 @@
fun foo(param1: String, vararg param2: Int) { }
fun bar(arr: IntArray) {
foo(param2 = *arr, <caret>)
}
// ELEMENT: arr
@@ -0,0 +1,7 @@
fun foo(vararg v1: Int, vararg v2: Char, s: String) { }
fun bar(c: Char) {
foo(*intArrayOf(), <caret>)
}
// ELEMENT: c
@@ -0,0 +1,7 @@
fun foo(vararg v1: Int, vararg v2: Char, s: String) { }
fun bar(c: Char) {
foo(*intArrayOf(), c<caret>)
}
// ELEMENT: c
@@ -0,0 +1,8 @@
fun foo(param1: String, param2: Int) { }
fun bar(pInt: Int, pString: String) {
foo(param2 = <caret>)
}
// EXIST: pInt
// ABSENT: pString
@@ -0,0 +1,9 @@
fun foo(vararg v1: Int, vararg v2: Char, s: String) { }
fun bar(c: Char, pInt: Int) {
foo(*intArrayOf(), <caret>)
}
// EXIST: c
// ABSENT: pInt
// EXIST: { lookupString: "charArrayOf", itemText: "*charArrayOf" }
@@ -1301,6 +1301,12 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
doTest(fileName); doTest(fileName);
} }
@TestMetadata("AfterVararg.kt")
public void testAfterVararg() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/namedParameters/AfterVararg.kt");
doTest(fileName);
}
public void testAllFilesPresentInNamedParameters() throws Exception { public void testAllFilesPresentInNamedParameters() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/basic/common/namedParameters"), Pattern.compile("^(.+)\\.kt$"), true); JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/basic/common/namedParameters"), Pattern.compile("^(.+)\\.kt$"), true);
} }
@@ -1301,6 +1301,12 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
doTest(fileName); doTest(fileName);
} }
@TestMetadata("AfterVararg.kt")
public void testAfterVararg() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/namedParameters/AfterVararg.kt");
doTest(fileName);
}
public void testAllFilesPresentInNamedParameters() throws Exception { public void testAllFilesPresentInNamedParameters() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/basic/common/namedParameters"), Pattern.compile("^(.+)\\.kt$"), true); JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/basic/common/namedParameters"), Pattern.compile("^(.+)\\.kt$"), true);
} }
@@ -251,6 +251,12 @@ public class JvmSmartCompletionTestGenerated extends AbstractJvmSmartCompletionT
doTest(fileName); doTest(fileName);
} }
@TestMetadata("NamedArgument.kt")
public void testNamedArgument() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smart/NamedArgument.kt");
doTest(fileName);
}
@TestMetadata("NoExtensionMethodFromClassObject.kt") @TestMetadata("NoExtensionMethodFromClassObject.kt")
public void testNoExtensionMethodFromClassObject() throws Exception { public void testNoExtensionMethodFromClassObject() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smart/NoExtensionMethodFromClassObject.kt"); String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smart/NoExtensionMethodFromClassObject.kt");
@@ -353,6 +359,12 @@ public class JvmSmartCompletionTestGenerated extends AbstractJvmSmartCompletionT
doTest(fileName); doTest(fileName);
} }
@TestMetadata("SecondVararg.kt")
public void testSecondVararg() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smart/SecondVararg.kt");
doTest(fileName);
}
@TestMetadata("SkipDeclarationsOfType.kt") @TestMetadata("SkipDeclarationsOfType.kt")
public void testSkipDeclarationsOfType() throws Exception { public void testSkipDeclarationsOfType() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smart/SkipDeclarationsOfType.kt"); String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smart/SkipDeclarationsOfType.kt");
@@ -55,6 +55,12 @@ public class SmartCompletionHandlerTestGenerated extends AbstractSmartCompletion
doTest(fileName); doTest(fileName);
} }
@TestMetadata("AfterVararg.kt")
public void testAfterVararg() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/smart/AfterVararg.kt");
doTest(fileName);
}
public void testAllFilesPresentInSmart() throws Exception { public void testAllFilesPresentInSmart() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/handlers/smart"), Pattern.compile("^(.+)\\.kt$"), true); JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/handlers/smart"), Pattern.compile("^(.+)\\.kt$"), true);
} }
@@ -509,6 +515,42 @@ public class SmartCompletionHandlerTestGenerated extends AbstractSmartCompletion
doTest(fileName); doTest(fileName);
} }
@TestMetadata("NamedArgument1.kt")
public void testNamedArgument1() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/smart/NamedArgument1.kt");
doTest(fileName);
}
@TestMetadata("NamedArgument2.kt")
public void testNamedArgument2() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/smart/NamedArgument2.kt");
doTest(fileName);
}
@TestMetadata("NamedArgument3.kt")
public void testNamedArgument3() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/smart/NamedArgument3.kt");
doTest(fileName);
}
@TestMetadata("NamedArgumentVararg1.kt")
public void testNamedArgumentVararg1() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/smart/NamedArgumentVararg1.kt");
doTest(fileName);
}
@TestMetadata("NamedArgumentVararg2.kt")
public void testNamedArgumentVararg2() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/smart/NamedArgumentVararg2.kt");
doTest(fileName);
}
@TestMetadata("NamedArgumentVararg3.kt")
public void testNamedArgumentVararg3() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/smart/NamedArgumentVararg3.kt");
doTest(fileName);
}
@TestMetadata("NestedDataClass.kt") @TestMetadata("NestedDataClass.kt")
public void testNestedDataClass() throws Exception { public void testNestedDataClass() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/smart/NestedDataClass.kt"); String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/smart/NestedDataClass.kt");
@@ -587,6 +629,12 @@ public class SmartCompletionHandlerTestGenerated extends AbstractSmartCompletion
doTest(fileName); doTest(fileName);
} }
@TestMetadata("SecondVararg.kt")
public void testSecondVararg() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/smart/SecondVararg.kt");
doTest(fileName);
}
@TestMetadata("TabReplaceComma1.kt") @TestMetadata("TabReplaceComma1.kt")
public void testTabReplaceComma1() throws Exception { public void testTabReplaceComma1() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/smart/TabReplaceComma1.kt"); String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/smart/TabReplaceComma1.kt");
@@ -0,0 +1,65 @@
/*
* 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.core
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.psi.Call
import org.jetbrains.kotlin.psi.FunctionLiteralArgument
import org.jetbrains.kotlin.psi.ValueArgument
import java.util.HashMap
public fun Call.mapArgumentsToParameters(targetDescriptor: CallableDescriptor): Map<ValueArgument, ValueParameterDescriptor> {
val parameters = targetDescriptor.getValueParameters()
if (parameters.isEmpty()) return emptyMap()
val map = HashMap<ValueArgument, ValueParameterDescriptor>()
val parametersByName = parameters.toMap { it.getName().asString() }
var positionalArgumentIndex: Int? = 0
for (argument in getValueArguments()) {
if (argument is FunctionLiteralArgument) {
map[argument] = parameters.last()
}
else {
val argumentName = argument.getArgumentName()?.getReferenceExpression()?.getReferencedName()
if (argumentName != null) {
if (targetDescriptor.hasStableParameterNames()) {
val parameter = parametersByName[argumentName]
if (parameter != null) {
map[argument] = parameter
}
}
positionalArgumentIndex = null
}
else {
if (positionalArgumentIndex != null && positionalArgumentIndex < parameters.size()) {
val parameter = parameters[positionalArgumentIndex]
map[argument] = parameter
if (parameter.getVarargElementType() == null || argument.getSpreadElement() != null) {
positionalArgumentIndex++
}
}
}
}
}
return map
}
@@ -16,7 +16,6 @@
package org.jetbrains.kotlin.idea.core.quickfix; package org.jetbrains.kotlin.idea.core.quickfix;
import com.google.common.collect.Sets;
import com.intellij.extapi.psi.ASTDelegatePsiElement; import com.intellij.extapi.psi.ASTDelegatePsiElement;
import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFile;
@@ -24,10 +23,8 @@ import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.ReadOnly;
import org.jetbrains.kotlin.descriptors.CallableDescriptor; import org.jetbrains.kotlin.descriptors.CallableDescriptor;
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor; import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor;
import org.jetbrains.kotlin.diagnostics.Diagnostic; import org.jetbrains.kotlin.diagnostics.Diagnostic;
import org.jetbrains.kotlin.idea.caches.resolve.ResolvePackage; import org.jetbrains.kotlin.idea.caches.resolve.ResolvePackage;
import org.jetbrains.kotlin.idea.references.BuiltInsReferenceResolver; import org.jetbrains.kotlin.idea.references.BuiltInsReferenceResolver;
@@ -44,8 +41,6 @@ import org.jetbrains.kotlin.types.DeferredType;
import org.jetbrains.kotlin.types.JetType; import org.jetbrains.kotlin.types.JetType;
import org.jetbrains.kotlin.types.checker.JetTypeChecker; import org.jetbrains.kotlin.types.checker.JetTypeChecker;
import java.util.Set;
public class QuickFixUtil { public class QuickFixUtil {
private QuickFixUtil() { private QuickFixUtil() {
} }
@@ -167,39 +162,6 @@ public class QuickFixUtil {
} }
} }
@ReadOnly
@NotNull
public static Set<String> getUsedParameters(
@NotNull JetCallElement callElement,
@Nullable JetValueArgument ignoreArgument,
@NotNull CallableDescriptor callableDescriptor
) {
Set<String> usedParameters = Sets.newHashSet();
boolean isPositionalArgument = true;
int idx = 0;
for (ValueArgument argument : callElement.getValueArguments()) {
if (argument.isNamed()) {
JetValueArgumentName name = argument.getArgumentName();
assert name != null : "Named argument's name cannot be null";
if (argument != ignoreArgument) {
usedParameters.add(name.getText());
}
isPositionalArgument = false;
}
else if (isPositionalArgument) {
if (callableDescriptor.getValueParameters().size() > idx) {
ValueParameterDescriptor parameter = callableDescriptor.getValueParameters().get(idx);
if (argument != ignoreArgument) {
usedParameters.add(parameter.getName().asString());
}
idx++;
}
}
}
return usedParameters;
}
public static String renderTypeWithFqNameOnClash(JetType type, String nameToCheckAgainst) { public static String renderTypeWithFqNameOnClash(JetType type, String nameToCheckAgainst) {
FqName typeFqName = DescriptorUtils.getFqNameSafe(DescriptorUtils.getClassDescriptorForType(type)); FqName typeFqName = DescriptorUtils.getFqNameSafe(DescriptorUtils.getClassDescriptorForType(type));
FqName fqNameToCheckAgainst = new FqName(nameToCheckAgainst); FqName fqNameToCheckAgainst = new FqName(nameToCheckAgainst);
@@ -28,6 +28,7 @@ import com.intellij.openapi.ui.popup.PopupStep;
import com.intellij.openapi.ui.popup.util.BaseListPopupStep; import com.intellij.openapi.ui.popup.util.BaseListPopupStep;
import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiTreeUtil;
import kotlin.KotlinPackage;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.CallableDescriptor; import org.jetbrains.kotlin.descriptors.CallableDescriptor;
@@ -36,6 +37,7 @@ import org.jetbrains.kotlin.diagnostics.Diagnostic;
import org.jetbrains.kotlin.idea.JetBundle; import org.jetbrains.kotlin.idea.JetBundle;
import org.jetbrains.kotlin.idea.JetIcons; import org.jetbrains.kotlin.idea.JetIcons;
import org.jetbrains.kotlin.idea.caches.resolve.ResolvePackage; import org.jetbrains.kotlin.idea.caches.resolve.ResolvePackage;
import org.jetbrains.kotlin.idea.core.CorePackage;
import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil; import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil;
import org.jetbrains.kotlin.psi.JetCallElement; import org.jetbrains.kotlin.psi.JetCallElement;
import org.jetbrains.kotlin.psi.JetExpression; import org.jetbrains.kotlin.psi.JetExpression;
@@ -76,13 +78,11 @@ public class AddNameToArgumentFix extends JetIntentionAction<JetValueArgument> {
CallableDescriptor callableDescriptor = resolvedCall.getResultingDescriptor(); CallableDescriptor callableDescriptor = resolvedCall.getResultingDescriptor();
JetExpression argExpression = argument.getArgumentExpression(); JetExpression argExpression = argument.getArgumentExpression();
JetType type = argExpression != null ? context.getType(argExpression) : null; JetType type = argExpression != null ? context.getType(argExpression) : null;
Set<String> usedParameters = QuickFixUtil.getUsedParameters(callElement, null, callableDescriptor); Set<ValueParameterDescriptor> usedParameters = KotlinPackage.toSet( CorePackage.mapArgumentsToParameters(resolvedCall.getCall(), callableDescriptor).values());
List<String> names = Lists.newArrayList(); List<String> names = Lists.newArrayList();
for (ValueParameterDescriptor parameter: callableDescriptor.getValueParameters()) { for (ValueParameterDescriptor parameter: callableDescriptor.getValueParameters()) {
String name = parameter.getName().asString(); if (!usedParameters.contains(parameter) && (type == null || JetTypeChecker.DEFAULT.isSubtypeOf(type, parameter.getType()))) {
if (usedParameters.contains(name)) continue; names.add(parameter.getName().asString());
if (type == null || JetTypeChecker.DEFAULT.isSubtypeOf(type, parameter.getType())) {
names.add(name);
} }
} }
return names; return names;