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:
@@ -69,7 +69,7 @@ public class Concat : IntrinsicMethod() {
|
||||
codegen: ExpressionCodegen
|
||||
): StackValue {
|
||||
return StackValue.operation(returnType) {
|
||||
val arguments = resolvedCall.getCall().getValueArguments().map { it.getArgumentExpression() }
|
||||
val arguments = resolvedCall.getCall().getValueArguments().map { it.getArgumentExpression()!! }
|
||||
val actualType = generateImpl(
|
||||
codegen, it, returnType,
|
||||
resolvedCall.getCall().getCallElement(),
|
||||
|
||||
@@ -48,7 +48,7 @@ public interface Call {
|
||||
|
||||
@ReadOnly
|
||||
@NotNull
|
||||
List<JetFunctionLiteralArgument> getFunctionLiteralArguments();
|
||||
List<? extends FunctionLiteralArgument> getFunctionLiteralArguments();
|
||||
|
||||
@ReadOnly
|
||||
@NotNull
|
||||
|
||||
@@ -19,14 +19,14 @@ package org.jetbrains.kotlin.psi
|
||||
import com.intellij.lang.ASTNode
|
||||
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: " +
|
||||
"${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 =
|
||||
when (expression) {
|
||||
|
||||
+16
-16
@@ -14,29 +14,29 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.psi;
|
||||
package org.jetbrains.kotlin.psi
|
||||
|
||||
import com.intellij.psi.impl.source.tree.LeafPsiElement;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import com.intellij.psi.impl.source.tree.LeafPsiElement
|
||||
|
||||
public interface ValueArgument {
|
||||
@Nullable
|
||||
@IfNotParsed
|
||||
JetExpression getArgumentExpression();
|
||||
public trait ValueArgument {
|
||||
IfNotParsed
|
||||
public fun getArgumentExpression(): JetExpression?
|
||||
|
||||
@Nullable
|
||||
JetValueArgumentName getArgumentName();
|
||||
public fun getArgumentName(): JetValueArgumentName?
|
||||
|
||||
boolean isNamed();
|
||||
public fun isNamed(): Boolean
|
||||
|
||||
@NotNull
|
||||
JetElement asElement();
|
||||
public fun asElement(): JetElement
|
||||
|
||||
/* The '*' in something like foo(*arr) i.e. pass an array as a number of vararg arguments */
|
||||
@Nullable
|
||||
LeafPsiElement getSpreadElement();
|
||||
public fun getSpreadElement(): LeafPsiElement?
|
||||
|
||||
/* 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
|
||||
|
||||
val expression = valueArgument.getArgumentExpression()
|
||||
val expression = valueArgument.getArgumentExpression() ?: return
|
||||
val deparenthesized = ArgumentTypeResolver.getLastElementDeparenthesized(expression, context)
|
||||
if (deparenthesized == null) return
|
||||
|
||||
val recordedType = expression?.let { context.trace.getType(it) }
|
||||
val recordedType = expression.let { context.trace.getType(it) }
|
||||
var updatedType: JetType? = recordedType
|
||||
|
||||
val results = completeCallForArgument(deparenthesized, context)
|
||||
|
||||
@@ -164,7 +164,7 @@ public class CallTransformer<D extends CallableDescriptor, F extends D> {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<JetFunctionLiteralArgument> getFunctionLiteralArguments() {
|
||||
public List<FunctionLiteralArgument> getFunctionLiteralArguments() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -213,9 +213,9 @@ import static org.jetbrains.kotlin.resolve.calls.ValueArgumentsToParametersMappe
|
||||
D candidate = candidateCall.getCandidateDescriptor();
|
||||
List<ValueParameterDescriptor> valueParameters = candidate.getValueParameters();
|
||||
|
||||
List<JetFunctionLiteralArgument> functionLiteralArguments = call.getFunctionLiteralArguments();
|
||||
List<? extends FunctionLiteralArgument> functionLiteralArguments = call.getFunctionLiteralArguments();
|
||||
if (!functionLiteralArguments.isEmpty()) {
|
||||
JetFunctionLiteralArgument functionLiteralArgument = functionLiteralArguments.get(0);
|
||||
FunctionLiteralArgument functionLiteralArgument = functionLiteralArguments.get(0);
|
||||
JetExpression possiblyLabeledFunctionLiteral = functionLiteralArgument.getArgumentExpression();
|
||||
|
||||
if (valueParameters.isEmpty()) {
|
||||
|
||||
@@ -162,7 +162,7 @@ public class CallMaker {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<JetFunctionLiteralArgument> getFunctionLiteralArguments() {
|
||||
public List<FunctionLiteralArgument> getFunctionLiteralArguments() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
@NotNull
|
||||
@@ -301,7 +301,7 @@ public class CallMaker {
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public List<JetFunctionLiteralArgument> getFunctionLiteralArguments() {
|
||||
public List<? extends FunctionLiteralArgument> getFunctionLiteralArguments() {
|
||||
return callElement.getFunctionLiteralArguments();
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ public class DelegatingCall implements Call {
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public List<JetFunctionLiteralArgument> getFunctionLiteralArguments() {
|
||||
public List<? extends FunctionLiteralArgument> getFunctionLiteralArguments() {
|
||||
return delegate.getFunctionLiteralArguments();
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -214,7 +214,7 @@ public class ControlStructureTypingUtils {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<JetFunctionLiteralArgument> getFunctionLiteralArguments() {
|
||||
public List<FunctionLiteralArgument> getFunctionLiteralArguments() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
|
||||
+36
-15
@@ -23,6 +23,7 @@ import com.intellij.patterns.ElementPattern
|
||||
import com.intellij.patterns.StandardPatterns
|
||||
import com.intellij.psi.PsiCompiledElement
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.impl.source.tree.LeafPsiElement
|
||||
import com.intellij.psi.search.DelegatingGlobalSearchScope
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
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.calls.callUtil.getCall
|
||||
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.scopes.DescriptorKindExclude
|
||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
||||
@@ -400,21 +402,7 @@ class SmartCompletionSession(configuration: CompletionSessionConfiguration, para
|
||||
if (expression != null) {
|
||||
val mapper = ToFromOriginalFileMapper(parameters.getOriginalFile() as JetFile, position.getContainingFile() as JetFile, parameters.getOffset())
|
||||
|
||||
// special completion for outside parenthesis lambda argument
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
addFunctionLiteralArgumentCompletions()
|
||||
|
||||
val completion = SmartCompletion(expression, resolutionFacade, moduleDescriptor,
|
||||
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) {
|
||||
getTopLevelExtensions().forEach(processor)
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.di.InjectorForMacros
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.ResolutionFacade
|
||||
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.lexer.JetTokens
|
||||
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.bindingContextUtil.getDataFlowInfo
|
||||
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.checkers.AdditionalTypeChecker
|
||||
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) {
|
||||
|
||||
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()
|
||||
= function.hashCode()
|
||||
}
|
||||
|
||||
|
||||
class ExpectedInfos(
|
||||
val bindingContext: BindingContext,
|
||||
val resolutionFacade: ResolutionFacade,
|
||||
@@ -97,67 +105,42 @@ class ExpectedInfos(
|
||||
|
||||
private fun calculateForArgument(expressionWithType: JetExpression): Collection<ExpectedInfo>? {
|
||||
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 argumentIndex = argumentList.getArguments().indexOf(argument)
|
||||
val callElement = argumentList.getParent() as? JetCallElement ?: return null
|
||||
return calculateForArgument(callElement, argumentIndex, false)
|
||||
return calculateForArgument(callElement, argument)
|
||||
}
|
||||
|
||||
private fun calculateForFunctionLiteralArgument(expressionWithType: JetExpression): Collection<ExpectedInfo>? {
|
||||
val functionLiteralArgument = expressionWithType.getParent() as? JetFunctionLiteralArgument
|
||||
val callExpression = functionLiteralArgument?.getParent() as? JetCallExpression
|
||||
if (callExpression != null) {
|
||||
if (callExpression.getFunctionLiteralArguments().firstOrNull()?.getArgumentExpression() == expressionWithType) {
|
||||
return calculateForArgument(callExpression, callExpression.getValueArguments().size() - 1, true)
|
||||
}
|
||||
}
|
||||
return null
|
||||
val callExpression = functionLiteralArgument?.getParent() as? JetCallExpression ?: return null
|
||||
val literalArgument = callExpression.getFunctionLiteralArguments().firstOrNull() ?: return null
|
||||
if (literalArgument.getArgumentExpression() != expressionWithType) return null
|
||||
return calculateForArgument(callExpression, literalArgument)
|
||||
}
|
||||
|
||||
private fun calculateForArgument(callElement: JetCallElement, argumentIndex: Int, isFunctionLiteralArgument: Boolean): Collection<ExpectedInfo>? {
|
||||
val parent = callElement.getParent()
|
||||
val receiver: ReceiverValue
|
||||
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)
|
||||
private fun calculateForArgument(callElement: JetCallElement, argument: ValueArgument): Collection<ExpectedInfo>? {
|
||||
val call = callElement.getCall(bindingContext) ?: return null
|
||||
return calculateForArgument(call, argument)
|
||||
}
|
||||
|
||||
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 calleeExpression = call.getCalleeExpression()
|
||||
|
||||
val truncatedCall = if (!isFunctionLiteralArgument) { // leave only arguments before the current one
|
||||
object : DelegatingCall(call) {
|
||||
override fun getValueArguments() = super.getValueArguments().subList(0, argumentIndex)
|
||||
override fun getValueArgumentList() = null
|
||||
}
|
||||
}
|
||||
else {
|
||||
call
|
||||
}
|
||||
// leave only arguments before the current one
|
||||
val truncatedCall = object : DelegatingCall(call) {
|
||||
val arguments = call.getValueArguments().subList(0, argumentIndex)
|
||||
|
||||
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 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()
|
||||
if (parameters.isEmpty()) continue
|
||||
|
||||
val parameterIndex = if (isFunctionLiteralArgument) {
|
||||
parameters.lastIndex
|
||||
}
|
||||
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 argumentToParameter = call.mapArgumentsToParameters(descriptor)
|
||||
val parameter = argumentToParameter[argument] ?: continue
|
||||
|
||||
val thisReceiver = ExpressionTypingUtils.normalizeReceiverValueForVisibility(candidate.getDispatchReceiver(), bindingContext)
|
||||
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 varargElementType = parameter.getVarargElementType()
|
||||
if (varargElementType != null) {
|
||||
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) {
|
||||
val tail = if (parameterIndex == parameters.lastIndex) Tail.RPARENTH else null
|
||||
expectedInfos.add(PositionalArgumentExpectedInfo(parameter.getType(), expectedName, tail, descriptor, parameterIndex, ItemOptions.STAR_PREFIX))
|
||||
if (argumentIndex == parameters.indexOf(parameter)) {
|
||||
val tail = if (parameter == parameters.last()) Tail.RPARENTH else null
|
||||
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 {
|
||||
@@ -228,28 +205,47 @@ class ExpectedInfos(
|
||||
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)
|
||||
HeuristicSignatures.correctedParameterType(descriptor, argumentIndex, moduleDescriptor, callElement.getProject()) ?: parameter.getType()
|
||||
HeuristicSignatures.correctedParameterType(descriptor, parameter, moduleDescriptor, callElement.getProject()) ?: parameter.getType()
|
||||
else
|
||||
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
|
||||
}
|
||||
|
||||
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>? {
|
||||
val binaryExpression = expressionWithType.getParent() as? JetBinaryExpression
|
||||
if (binaryExpression != null) {
|
||||
@@ -271,7 +267,7 @@ class ExpectedInfos(
|
||||
return when (expressionWithType) {
|
||||
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() -> {
|
||||
val ifExpectedInfo = calculate(ifExpression)
|
||||
@@ -312,7 +308,7 @@ class ExpectedInfos(
|
||||
private fun calculateForBlockExpression(expressionWithType: JetExpression): Collection<ExpectedInfo>? {
|
||||
val block = expressionWithType.getParent() as? JetBlockExpression ?: 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>? {
|
||||
|
||||
+19
-12
@@ -16,24 +16,25 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.completion
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
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.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.resolve.BindingTraceContext
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.resolve.scopes.JetScope
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.JetModuleUtil
|
||||
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.Variance
|
||||
import java.util.HashMap
|
||||
|
||||
public object HeuristicSignatures {
|
||||
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()
|
||||
}
|
||||
|
||||
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 superFunctions = function.getOverriddenDescriptors()
|
||||
|
||||
+12
-8
@@ -29,7 +29,7 @@ import org.jetbrains.kotlin.core.FirstChildInParentFilter
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.idea.JetIcons
|
||||
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.util.IdeDescriptorRenderers
|
||||
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.renderer.DescriptorRenderer
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getCall
|
||||
import java.util.*
|
||||
|
||||
object NamedParametersCompletion {
|
||||
private val positionFilter = AndFilter(
|
||||
@@ -83,18 +85,20 @@ object NamedParametersCompletion {
|
||||
|
||||
for (funDescriptor in functionDescriptors) {
|
||||
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()) {
|
||||
val name = parameter.getName()
|
||||
val nameString = name.asString()
|
||||
if (nameString !in usedArguments) {
|
||||
val lookupElement = LookupElementBuilder.create(nameString)
|
||||
.withPresentableText("$nameString =")
|
||||
if (parameter !in usedParameters) {
|
||||
val name = parameter.getName().asString()
|
||||
val lookupElement = LookupElementBuilder.create(name)
|
||||
.withPresentableText("$name =")
|
||||
.withTailText(" ${DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(parameter.getType())}")
|
||||
.withIcon(JetIcons.PARAMETER)
|
||||
.withInsertHandler(NamedParameterInsertHandler(name))
|
||||
.withInsertHandler(NamedParameterInsertHandler(parameter.getName()))
|
||||
.assignPriority(ItemPriority.NAMED_PARAMETER)
|
||||
collector.addElement(lookupElement)
|
||||
}
|
||||
|
||||
+14
-18
@@ -16,26 +16,22 @@
|
||||
|
||||
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.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.checker.JetTypeChecker
|
||||
import java.util.ArrayList
|
||||
import java.util.HashSet
|
||||
|
||||
class MultipleArgumentsItemProvider(val bindingContext: BindingContext,
|
||||
val smartCastTypes: (VariableDescriptor) -> Collection<JetType>) {
|
||||
@@ -47,7 +43,7 @@ class MultipleArgumentsItemProvider(val bindingContext: BindingContext,
|
||||
|
||||
val added = HashSet<String>()
|
||||
for (expectedInfo in expectedInfos) {
|
||||
if (expectedInfo is PositionalArgumentExpectedInfo && expectedInfo.parameterIndex == 0) {
|
||||
if (expectedInfo is ArgumentExpectedInfo && expectedInfo.position == ArgumentPosition(0)) {
|
||||
val parameters = expectedInfo.function.getValueParameters()
|
||||
if (parameters.size() > 1) {
|
||||
val variables = ArrayList<VariableDescriptor>()
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ object NameSimilarityWeigher : LookupElementWeigher("kotlin.nameSimilarity") {
|
||||
|
||||
fun calcNameSimilarity(name: String, expectedInfos: Collection<ExpectedInfo>): Int {
|
||||
return expectedInfos
|
||||
.map { it.name }
|
||||
.map { it.expectedName }
|
||||
.filterNotNull()
|
||||
.map { calcNameSimilarity(name, it) }
|
||||
.max() ?: 0
|
||||
|
||||
+1
-1
@@ -130,7 +130,7 @@ class SmartCompletion(
|
||||
|
||||
// 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)
|
||||
filteredExpectedInfos.map { ExpectedInfo(it.type.makeNullable(), it.name, it.tail) }
|
||||
filteredExpectedInfos.map { ExpectedInfo(it.type.makeNullable(), it.expectedName, it.tail) }
|
||||
else
|
||||
filteredExpectedInfos
|
||||
|
||||
|
||||
+1
-1
@@ -58,7 +58,7 @@ class TypesWithContainsDetector(
|
||||
private fun isGoodContainsFunction(function: FunctionDescriptor, freeTypeParams: Collection<TypeParameterDescriptor>): Boolean {
|
||||
if (!TypeUtils.equalTypes(function.getReturnType(), booleanType)) 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)
|
||||
return fuzzyParameterType.checkIsSuperTypeOf(argumentType) != null
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ fun LookupElement.withOptions(options: ItemOptions): LookupElement {
|
||||
val token = context.getFile().findElementAt(offset)!!
|
||||
val argument = token.getStrictParentOfType<JetValueArgument>()
|
||||
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" }
|
||||
+6
@@ -1301,6 +1301,12 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
|
||||
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 {
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/basic/common/namedParameters"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
+6
@@ -1301,6 +1301,12 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
|
||||
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 {
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/basic/common/namedParameters"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
+12
@@ -251,6 +251,12 @@ public class JvmSmartCompletionTestGenerated extends AbstractJvmSmartCompletionT
|
||||
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")
|
||||
public void testNoExtensionMethodFromClassObject() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smart/NoExtensionMethodFromClassObject.kt");
|
||||
@@ -353,6 +359,12 @@ public class JvmSmartCompletionTestGenerated extends AbstractJvmSmartCompletionT
|
||||
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")
|
||||
public void testSkipDeclarationsOfType() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smart/SkipDeclarationsOfType.kt");
|
||||
|
||||
+48
@@ -55,6 +55,12 @@ public class SmartCompletionHandlerTestGenerated extends AbstractSmartCompletion
|
||||
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 {
|
||||
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);
|
||||
}
|
||||
|
||||
@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")
|
||||
public void testNestedDataClass() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/smart/NestedDataClass.kt");
|
||||
@@ -587,6 +629,12 @@ public class SmartCompletionHandlerTestGenerated extends AbstractSmartCompletion
|
||||
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")
|
||||
public void testTabReplaceComma1() throws Exception {
|
||||
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;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import com.intellij.extapi.psi.ASTDelegatePsiElement;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
@@ -24,10 +23,8 @@ import com.intellij.psi.PsiWhiteSpace;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.ReadOnly;
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor;
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic;
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.ResolvePackage;
|
||||
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.checker.JetTypeChecker;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class 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) {
|
||||
FqName typeFqName = DescriptorUtils.getFqNameSafe(DescriptorUtils.getClassDescriptorForType(type));
|
||||
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.psi.PsiDocumentManager;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import kotlin.KotlinPackage;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
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.JetIcons;
|
||||
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.psi.JetCallElement;
|
||||
import org.jetbrains.kotlin.psi.JetExpression;
|
||||
@@ -76,13 +78,11 @@ public class AddNameToArgumentFix extends JetIntentionAction<JetValueArgument> {
|
||||
CallableDescriptor callableDescriptor = resolvedCall.getResultingDescriptor();
|
||||
JetExpression argExpression = argument.getArgumentExpression();
|
||||
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();
|
||||
for (ValueParameterDescriptor parameter: callableDescriptor.getValueParameters()) {
|
||||
String name = parameter.getName().asString();
|
||||
if (usedParameters.contains(name)) continue;
|
||||
if (type == null || JetTypeChecker.DEFAULT.isSubtypeOf(type, parameter.getType())) {
|
||||
names.add(name);
|
||||
if (!usedParameters.contains(parameter) && (type == null || JetTypeChecker.DEFAULT.isSubtypeOf(type, parameter.getType()))) {
|
||||
names.add(parameter.getName().asString());
|
||||
}
|
||||
}
|
||||
return names;
|
||||
|
||||
Reference in New Issue
Block a user