diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingContext.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingContext.java index eb5dad38798..7b5aed81191 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingContext.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingContext.java @@ -34,9 +34,20 @@ public class ExpressionTypingContext extends ResolutionContext() { diff --git a/idea/ide-common/src/org/jetbrains/kotlin/idea/analysis/AnalyzerUtil.kt b/idea/ide-common/src/org/jetbrains/kotlin/idea/analysis/AnalyzerUtil.kt index be56a4d615f..8b79122f033 100644 --- a/idea/ide-common/src/org/jetbrains/kotlin/idea/analysis/AnalyzerUtil.kt +++ b/idea/ide-common/src/org/jetbrains/kotlin/idea/analysis/AnalyzerUtil.kt @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingTrace import org.jetbrains.kotlin.resolve.BindingTraceContext +import org.jetbrains.kotlin.resolve.calls.context.ContextDependency import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo import org.jetbrains.kotlin.resolve.scopes.LexicalScope import org.jetbrains.kotlin.types.KotlinType @@ -36,11 +37,12 @@ import org.jetbrains.kotlin.types.expressions.PreliminaryDeclarationVisitor trace: BindingTrace = BindingTraceContext(), dataFlowInfo: DataFlowInfo = DataFlowInfo.EMPTY, expectedType: KotlinType = TypeUtils.NO_EXPECTED_TYPE, - isStatement: Boolean = false + isStatement: Boolean = false, + contextDependency: ContextDependency = ContextDependency.INDEPENDENT ): KotlinTypeInfo { PreliminaryDeclarationVisitor.createForExpression(this, trace) return contextExpression.getResolutionFacade().frontendService() - .getTypeInfo(scope, this, expectedType, dataFlowInfo, trace, isStatement, contextExpression) + .getTypeInfo(scope, this, expectedType, dataFlowInfo, trace, isStatement, contextExpression, contextDependency) } @JvmOverloads fun KtExpression.analyzeInContext( @@ -49,9 +51,10 @@ import org.jetbrains.kotlin.types.expressions.PreliminaryDeclarationVisitor trace: BindingTrace = BindingTraceContext(), dataFlowInfo: DataFlowInfo = DataFlowInfo.EMPTY, expectedType: KotlinType = TypeUtils.NO_EXPECTED_TYPE, - isStatement: Boolean = false + isStatement: Boolean = false, + contextDependency: ContextDependency = ContextDependency.INDEPENDENT ): BindingContext { - computeTypeInfoInContext(scope, contextExpression, trace, dataFlowInfo, expectedType, isStatement) + computeTypeInfoInContext(scope, contextExpression, trace, dataFlowInfo, expectedType, isStatement, contextDependency) return trace.bindingContext } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/ImportFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/ImportFix.kt index e8a3842f909..e40c0825f42 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/ImportFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/ImportFix.kt @@ -25,18 +25,23 @@ import com.intellij.openapi.command.CommandProcessor import com.intellij.openapi.editor.Editor import com.intellij.openapi.project.Project import com.intellij.packageDependencies.DependencyValidationManager +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiErrorElement import com.intellij.psi.PsiFile import com.intellij.psi.util.PsiModificationTracker +import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithVisibility import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.diagnostics.DiagnosticFactory import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.diagnostics.Severity import org.jetbrains.kotlin.idea.KotlinBundle import org.jetbrains.kotlin.idea.actions.KotlinAddImportAction import org.jetbrains.kotlin.idea.actions.createGroupedImportsAction import org.jetbrains.kotlin.idea.actions.createSingleImportAction +import org.jetbrains.kotlin.idea.analysis.analyzeInContext import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade import org.jetbrains.kotlin.idea.caches.resolve.getResolveScope @@ -46,12 +51,25 @@ import org.jetbrains.kotlin.idea.core.isVisible import org.jetbrains.kotlin.idea.imports.canBeReferencedViaImport import org.jetbrains.kotlin.idea.project.ProjectStructureUtil import org.jetbrains.kotlin.idea.util.CallTypeAndReceiver +import org.jetbrains.kotlin.idea.util.getResolutionScope +import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.KtPsiUtil.getConventionName +import org.jetbrains.kotlin.psi.KtPsiUtil.isSelectorInQualified +import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType +import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelectorOrThis +import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.isImportDirectiveExpression import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.DescriptorUtils.isTopLevelDeclaration +import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo +import org.jetbrains.kotlin.resolve.calls.callUtil.getParentCall +import org.jetbrains.kotlin.resolve.calls.context.ContextDependency import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode +import org.jetbrains.kotlin.resolve.scopes.ExplicitImportsScope +import org.jetbrains.kotlin.resolve.scopes.utils.addImportingScope +import org.jetbrains.kotlin.resolve.scopes.utils.collectFunctions import org.jetbrains.kotlin.types.expressions.OperatorConventions import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.utils.CachedValueProperty @@ -123,22 +141,18 @@ internal abstract class ImportFixBase(expression: T) : } } - private fun computeSuggestionsForName(name: Name, callTypeAndReceiver: CallTypeAndReceiver<*, *>): - Collection { + private fun computeSuggestionsForName(name: Name, callTypeAndReceiver: CallTypeAndReceiver<*, *>): Collection { val nameStr = name.asString() if (nameStr.isEmpty()) return emptyList() - val file = element.containingFile as KtFile + val file = element.getContainingKtFile() + + val bindingContext = element.analyze(BodyResolveMode.PARTIAL) + if (!checkErrorStillPresent(bindingContext)) return emptyList() val searchScope = getResolveScope(file) - val bindingContext = element.analyze(BodyResolveMode.PARTIAL) - - val diagnostics = bindingContext.diagnostics.forElement(element) - - if (!diagnostics.any { it.factory in getSupportedErrors() }) return emptyList() - - val resolutionFacade = element.getResolutionFacade() + val resolutionFacade = file.getResolutionFacade() fun isVisible(descriptor: DeclarationDescriptor): Boolean { if (descriptor is DeclarationDescriptorWithVisibility) { @@ -166,6 +180,14 @@ internal abstract class ImportFixBase(expression: T) : result } + private fun checkErrorStillPresent(bindingContext: BindingContext): Boolean { + return elementsToCheckDiagnostics() + .flatMap { bindingContext.diagnostics.forElement(it) } + .any { diagnostic -> diagnostic.factory in getSupportedErrors() } + } + + protected open fun elementsToCheckDiagnostics(): Collection = listOf(element) + abstract fun fillCandidates( name: String, callTypeAndReceiver: CallTypeAndReceiver<*, *>, @@ -196,7 +218,7 @@ internal abstract class OrdinaryImportFixBase(expression: T) : val expression = element if (expression is KtSimpleNameExpression) { - if (!expression.isImportDirectiveExpression() && !KtPsiUtil.isSelectorInQualified(expression)) { + if (!expression.isImportDirectiveExpression() && !isSelectorInQualified(expression)) { val filterByCallType = { descriptor: DeclarationDescriptor -> callTypeAndReceiver.callType.descriptorKindFilter.accepts(descriptor) } if (ProjectStructureUtil.isJsKotlinModule(expression.getContainingKtFile())) { @@ -221,31 +243,7 @@ internal abstract class OrdinaryImportFixBase(expression: T) : internal class ImportFix(expression: KtSimpleNameExpression) : OrdinaryImportFixBase(expression) { override fun getCallTypeAndReceiver() = CallTypeAndReceiver.detect(element) - override val importNames: Collection = run { - if (element.getIdentifier() == null) { - val conventionName = KtPsiUtil.getConventionName(element) - if (conventionName != null) { - if (element is KtOperationReferenceExpression) { - val elementType = element.firstChild.node.elementType - if (elementType in OperatorConventions.ASSIGNMENT_OPERATIONS) { - val counterpart = OperatorConventions.ASSIGNMENT_OPERATION_COUNTERPARTS[elementType] - val counterpartName = OperatorConventions.BINARY_OPERATION_NAMES[counterpart] - if (counterpartName != null) { - return@run listOf(conventionName, counterpartName) - } - } - } - - return@run conventionName.singletonOrEmptyList() - } - } - else if (Name.isValidIdentifier(element.getReferencedName())) { - return@run Name.identifier(element.getReferencedName()).singletonOrEmptyList() - } - - emptyList() - } - + override val importNames = element.extractImportNames() override fun getSupportedErrors() = ERRORS @@ -259,6 +257,31 @@ internal class ImportFix(expression: KtSimpleNameExpression) : OrdinaryImportFix } } +private fun KtSimpleNameExpression.extractImportNames(): Collection { + if (getIdentifier() == null) { + val conventionName = getConventionName(this) + if (conventionName != null) { + if (this is KtOperationReferenceExpression) { + val elementType = firstChild.node.elementType + if (elementType in OperatorConventions.ASSIGNMENT_OPERATIONS) { + val counterpart = OperatorConventions.ASSIGNMENT_OPERATION_COUNTERPARTS[elementType] + val counterpartName = OperatorConventions.BINARY_OPERATION_NAMES[counterpart] + if (counterpartName != null) { + return listOf(conventionName, counterpartName) + } + } + } + + return conventionName.singletonOrEmptyList() + } + } + else if (Name.isValidIdentifier(getReferencedName())) { + return Name.identifier(getReferencedName()).singletonOrEmptyList() + } + + return emptyList() +} + internal class InvokeImportFix(expression: KtExpression) : OrdinaryImportFixBase(expression) { override val importNames = OperatorNameConventions.INVOKE.singletonList() @@ -275,8 +298,12 @@ internal class InvokeImportFix(expression: KtExpression) : OrdinaryImportFixBase } } -internal open class ArrayAccessorImportFix(element: KtArrayAccessExpression, override val importNames: Collection, private val showHint: Boolean) : - OrdinaryImportFixBase(element) { +internal open class ArrayAccessorImportFix( + element: KtArrayAccessExpression, + override val importNames: Collection, + private val showHint: Boolean +) : OrdinaryImportFixBase(element) { + override fun getCallTypeAndReceiver() = CallTypeAndReceiver.OPERATOR(element.arrayExpression!!) @@ -310,8 +337,11 @@ internal open class ArrayAccessorImportFix(element: KtArrayAccessExpression, ove } internal class DelegateAccessorsImportFix( - element: KtExpression, override val importNames: Collection, private val solveSeveralProblems: Boolean) : - OrdinaryImportFixBase(element) { + element: KtExpression, + override val importNames: Collection, + private val solveSeveralProblems: Boolean +) : OrdinaryImportFixBase(element) { + override fun getCallTypeAndReceiver() = CallTypeAndReceiver.DELEGATE(element) override fun createAction(project: Project, editor: Editor): KotlinAddImportAction { @@ -351,8 +381,12 @@ internal class DelegateAccessorsImportFix( } } -internal class ComponentsImportFix(element: KtExpression, override val importNames: Collection, private val solveSeveralProblems: Boolean) : - OrdinaryImportFixBase(element) { +internal class ComponentsImportFix( + element: KtExpression, + override val importNames: Collection, + private val solveSeveralProblems: Boolean +) : OrdinaryImportFixBase(element) { + override fun getCallTypeAndReceiver() = CallTypeAndReceiver.OPERATOR(element) override fun createAction(project: Project, editor: Editor): KotlinAddImportAction { @@ -397,15 +431,14 @@ internal class ImportMemberFix(expression: KtSimpleNameExpression) : ImportFixBa indicesHelper: KotlinIndicesHelper ): List { val result = ArrayList() - val expression = element - if (!expression.isImportDirectiveExpression() && !KtPsiUtil.isSelectorInQualified(expression)) { + if (!element.isImportDirectiveExpression() && !isSelectorInQualified(element)) { val filterByCallType = { descriptor: DeclarationDescriptor -> callTypeAndReceiver.callType.descriptorKindFilter.accepts(descriptor) } indicesHelper.getKotlinCallablesByName(name) .filter { it.canBeReferencedViaImport() && !isTopLevelDeclaration(it) } .filterTo(result, filterByCallType) - if (!ProjectStructureUtil.isJsKotlinModule(expression.getContainingKtFile())) { + if (!ProjectStructureUtil.isJsKotlinModule(element.getContainingKtFile())) { indicesHelper.getJvmCallablesByName(name) .filter { it.canBeReferencedViaImport() } .filterTo(result, filterByCallType) @@ -436,7 +469,90 @@ internal class ImportMemberFix(expression: KtSimpleNameExpression) : ImportFixBa private val ERRORS: Collection> by lazy { QuickFixes.getInstance().getDiagnostics(this) } } +} +internal class ImportForMismatchingArgumentsFix(expression: KtSimpleNameExpression) : ImportFixBase(expression) { + override fun getCallTypeAndReceiver() = CallTypeAndReceiver.detect(element) + + override val importNames = element.extractImportNames() + + override fun getSupportedErrors() = ERRORS + + override fun elementsToCheckDiagnostics(): Collection { + val callExpression = element.parent as? KtCallExpression ?: return emptyList() + return callExpression.valueArguments + + callExpression.valueArguments.mapNotNull { it.getArgumentExpression() } + + callExpression.valueArguments.mapNotNull { it.getArgumentName()?.referenceExpression } + + callExpression.valueArgumentList.singletonOrEmptyList() + } + + override fun fillCandidates( + name: String, + callTypeAndReceiver: CallTypeAndReceiver<*, *>, + bindingContext: BindingContext, + indicesHelper: KotlinIndicesHelper + ): List { + if (!Name.isValidIdentifier(name)) return emptyList() + val identifier = Name.identifier(name) + + val call = element.getParentCall(bindingContext) ?: return emptyList() + val callElement = call.callElement as? KtExpression ?: return emptyList() + if (callElement.anyDescendantOfType()) return emptyList() // incomplete call + val elementToAnalyze = callElement.getQualifiedExpressionForSelectorOrThis() + + val file = element.getContainingKtFile() + val resolutionFacade = file.getResolutionFacade() + val resolutionScope = elementToAnalyze.getResolutionScope(bindingContext, resolutionFacade) + + fun filterFunction(descriptor: FunctionDescriptor): Boolean { + if (!callTypeAndReceiver.callType.descriptorKindFilter.accepts(descriptor)) return false + + if (descriptor in resolutionScope.collectFunctions(identifier, NoLookupLocation.FROM_IDE)) return false // already imported + + // check that this function matches all arguments + val resolutionScopeWithAddedImport = resolutionScope.addImportingScope(ExplicitImportsScope(listOf(descriptor))) + val dataFlowInfo = bindingContext.getDataFlowInfo(elementToAnalyze) + val newBindingContext = elementToAnalyze.analyzeInContext( + resolutionScopeWithAddedImport, + dataFlowInfo = dataFlowInfo, + contextDependency = ContextDependency.DEPENDENT // to not check complete inference + ) + return newBindingContext.diagnostics.none { it.severity == Severity.ERROR } + } + + val result = ArrayList() + + fun processDescriptor(descriptor: CallableDescriptor) { + if (descriptor is FunctionDescriptor && filterFunction(descriptor)) { + result.add(descriptor) + } + } + + indicesHelper + .getCallableTopLevelExtensions(callTypeAndReceiver, element, bindingContext) { it == name } + .forEach(::processDescriptor) + + if (!isSelectorInQualified(element)) { + indicesHelper + .getTopLevelCallablesByName(name) + .forEach(::processDescriptor) + } + + return result + } + + companion object : KotlinSingleIntentionActionFactory() { + override fun createAction(diagnostic: Diagnostic): ImportForMismatchingArgumentsFix? { + //TODO: not only KtCallExpression + val callExpression = diagnostic.psiElement.getStrictParentOfType() ?: return null + val nameExpression = callExpression.calleeExpression as? KtNameReferenceExpression ?: return null + return ImportForMismatchingArgumentsFix(nameExpression) + } + + override fun isApplicableForCodeFragment() = true + + private val ERRORS: Collection> by lazy { QuickFixes.getInstance().getDiagnostics(this) } + } } object ImportForMissingOperatorFactory : KotlinSingleIntentionActionFactory() { diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt index eb5d4766147..749799a3ecc 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt @@ -134,6 +134,12 @@ class QuickFixRegistrar : QuickFixContributor { UNRESOLVED_REFERENCE.registerFactory(ImportMemberFix) UNRESOLVED_REFERENCE.registerFactory(ImportFix) + TOO_MANY_ARGUMENTS.registerFactory(ImportForMismatchingArgumentsFix) + NO_VALUE_FOR_PARAMETER.registerFactory(ImportForMismatchingArgumentsFix) + TYPE_MISMATCH.registerFactory(ImportForMismatchingArgumentsFix) + CONSTANT_EXPECTED_TYPE_MISMATCH.registerFactory(ImportForMismatchingArgumentsFix) + NAMED_PARAMETER_NOT_FOUND.registerFactory(ImportForMismatchingArgumentsFix) + UNRESOLVED_REFERENCE.registerFactory(AddTestLibQuickFix) UNRESOLVED_REFERENCE_WRONG_RECEIVER.registerFactory(ImportFix) diff --git a/idea/testData/quickfix/autoImports/mismatchingArgs/checkArgumentTypes.test b/idea/testData/quickfix/autoImports/mismatchingArgs/checkArgumentTypes.test new file mode 100644 index 00000000000..987a89abf72 --- /dev/null +++ b/idea/testData/quickfix/autoImports/mismatchingArgs/checkArgumentTypes.test @@ -0,0 +1,26 @@ +// FILE: first.before.kt +// "Import" "false" +// ERROR: Too many arguments for public final fun foo(): Unit defined in main.X +// ACTION: Add parameter to function 'foo' +// ACTION: Convert to expression body +// ACTION: Create extension function 'X.foo' +// ACTION: Create member function 'X.foo' + +package main + +class X { + fun foo() { + } + + fun f() { + this.foo(1) + } +} + +// FILE: second.kt +package other + +import main.X + +fun X.foo(p: String) { +} diff --git a/idea/testData/quickfix/autoImports/mismatchingArgs/constantExpectedTypeMismatch.test b/idea/testData/quickfix/autoImports/mismatchingArgs/constantExpectedTypeMismatch.test new file mode 100644 index 00000000000..77a83554d69 --- /dev/null +++ b/idea/testData/quickfix/autoImports/mismatchingArgs/constantExpectedTypeMismatch.test @@ -0,0 +1,40 @@ +// FILE: first.before.kt +// "Import" "true" +// ERROR: The integer literal does not conform to the expected type String + +package main + +class X { + fun foo(p: String) { + } + + fun f() { + foo(1) + } +} + +// FILE: second.kt +package other + +import main.X + +fun X.foo(p: Int) { +} + +// FILE: first.after.kt +// "Import" "true" +// ERROR: The integer literal does not conform to the expected type String + +package main + +import other.foo + +class X { + fun foo(p: String) { + } + + fun f() { + foo(1) + } +} + diff --git a/idea/testData/quickfix/autoImports/mismatchingArgs/expectedTypeRequired.test b/idea/testData/quickfix/autoImports/mismatchingArgs/expectedTypeRequired.test new file mode 100644 index 00000000000..8a6985ee228 --- /dev/null +++ b/idea/testData/quickfix/autoImports/mismatchingArgs/expectedTypeRequired.test @@ -0,0 +1,41 @@ +// FILE: first.before.kt +// "Import" "true" +// ERROR: No value passed for parameter p +// ERROR: Type mismatch: inferred type is Unit but String was expected + +package main + +class X { + fun foo(p: Int) { + } + + fun f(): String { + return foo() + } +} + +// FILE: second.kt +package other + +import main.X + +fun X.foo(): T = TODO() + +// FILE: first.after.kt +// "Import" "true" +// ERROR: No value passed for parameter p +// ERROR: Type mismatch: inferred type is Unit but String was expected + +package main + +import other.foo + +class X { + fun foo(p: Int) { + } + + fun f(): String { + return foo() + } +} + diff --git a/idea/testData/quickfix/autoImports/mismatchingArgs/extensionExplicitReceiver.test b/idea/testData/quickfix/autoImports/mismatchingArgs/extensionExplicitReceiver.test new file mode 100644 index 00000000000..84e16896c26 --- /dev/null +++ b/idea/testData/quickfix/autoImports/mismatchingArgs/extensionExplicitReceiver.test @@ -0,0 +1,40 @@ +// FILE: first.before.kt +// "Import" "true" +// ERROR: Too many arguments for public final fun foo(): Unit defined in main.X + +package main + +class X { + fun foo() { + } +} + +fun f(x: X) { + x.foo(1) +} + + +// FILE: second.kt +package other + +import main.X + +fun X.foo(p: Int) { +} + +// FILE: first.after.kt +// "Import" "true" +// ERROR: Too many arguments for public final fun foo(): Unit defined in main.X + +package main + +import other.foo + +class X { + fun foo() { + } +} + +fun f(x: X) { + x.foo(1) +} diff --git a/idea/testData/quickfix/autoImports/mismatchingArgs/extensionImplicitReceiver.test b/idea/testData/quickfix/autoImports/mismatchingArgs/extensionImplicitReceiver.test new file mode 100644 index 00000000000..7f2b9c21d67 --- /dev/null +++ b/idea/testData/quickfix/autoImports/mismatchingArgs/extensionImplicitReceiver.test @@ -0,0 +1,40 @@ +// FILE: first.before.kt +// "Import" "true" +// ERROR: Too many arguments for public final fun foo(): Unit defined in main.X + +package main + +class X { + fun foo() { + } + + fun f() { + foo(1) + } +} + +// FILE: second.kt +package other + +import main.X + +fun X.foo(p: Int) { +} + +// FILE: first.after.kt +// "Import" "true" +// ERROR: Too many arguments for public final fun foo(): Unit defined in main.X + +package main + +import other.foo + +class X { + fun foo() { + } + + fun f() { + foo(1) + } +} + diff --git a/idea/testData/quickfix/autoImports/mismatchingArgs/extensionWrongReceiver.test b/idea/testData/quickfix/autoImports/mismatchingArgs/extensionWrongReceiver.test new file mode 100644 index 00000000000..0d1e45cec32 --- /dev/null +++ b/idea/testData/quickfix/autoImports/mismatchingArgs/extensionWrongReceiver.test @@ -0,0 +1,27 @@ +// FILE: first.before.kt +// "Import" "false" +// ERROR: Too many arguments for public final fun foo(): Unit defined in main.X +// ACTION: Add parameter to function 'foo' +// ACTION: Convert to expression body +// ACTION: Create extension function 'X.foo' +// ACTION: Create member function 'X.foo' + +package main + +class X { + fun foo() { + } +} + +fun f(x: X) { + x.foo(1) +} + + +// FILE: second.kt +package other + +import main.X + +fun String.foo(p: Int) { +} diff --git a/idea/testData/quickfix/autoImports/mismatchingArgs/ignoreErrorsOutsideCall.test b/idea/testData/quickfix/autoImports/mismatchingArgs/ignoreErrorsOutsideCall.test new file mode 100644 index 00000000000..b3920fcfac7 --- /dev/null +++ b/idea/testData/quickfix/autoImports/mismatchingArgs/ignoreErrorsOutsideCall.test @@ -0,0 +1,46 @@ +// FILE: first.before.kt +// "Import" "true" +// ERROR: Too many arguments for public final fun foo(): Unit defined in main.X +// ERROR: Unresolved reference. None of the following candidates is applicable because of receiver type mismatch:
public operator fun String?.plus(other: Any?): String defined in kotlin +// ERROR: Unresolved reference: XXX +// ERROR: Unresolved reference: xxx + +package main + +class X : XXX { + fun foo() { + } + + fun f() { + foo(1) + xxx() + } +} + +// FILE: second.kt +package other + +import main.X + +fun X.foo(p: Int) { +} + +// FILE: first.after.kt +// "Import" "true" +// ERROR: Too many arguments for public final fun foo(): Unit defined in main.X +// ERROR: Unresolved reference. None of the following candidates is applicable because of receiver type mismatch:
public operator fun String?.plus(other: Any?): String defined in kotlin +// ERROR: Unresolved reference: XXX +// ERROR: Unresolved reference: xxx + +package main + +import other.foo + +class X : XXX { + fun foo() { + } + + fun f() { + foo(1) + xxx() + } +} + diff --git a/idea/testData/quickfix/autoImports/mismatchingArgs/lambdaArgument.test b/idea/testData/quickfix/autoImports/mismatchingArgs/lambdaArgument.test new file mode 100644 index 00000000000..c9e4358c06f --- /dev/null +++ b/idea/testData/quickfix/autoImports/mismatchingArgs/lambdaArgument.test @@ -0,0 +1,39 @@ +// FILE: first.before.kt +// "Import" "true" +// ERROR: Type mismatch: inferred type is () -> Unit but Int was expected + +package main + +class X { + fun foo(p: Int) { + } + + fun f() { + foo {} + } +} + +// FILE: second.kt +package other + +import main.X + +fun X.foo(filter: (String) -> Boolean){} + +// FILE: first.after.kt +// "Import" "true" +// ERROR: Type mismatch: inferred type is () -> Unit but Int was expected + +package main + +import other.foo + +class X { + fun foo(p: Int) { + } + + fun f() { + foo {} + } +} + diff --git a/idea/testData/quickfix/autoImports/mismatchingArgs/namedArgument.test b/idea/testData/quickfix/autoImports/mismatchingArgs/namedArgument.test new file mode 100644 index 00000000000..7fbd73dd885 --- /dev/null +++ b/idea/testData/quickfix/autoImports/mismatchingArgs/namedArgument.test @@ -0,0 +1,39 @@ +// FILE: first.before.kt +// "Import" "true" +// ERROR: Cannot find a parameter with this name: p2 + +package main + +class X { + fun foo() { + } + + fun f(p: Int) { + foo(p2 = 0) + } +} + +// FILE: second.kt +package other + +import main.X + +fun X.foo(p1: Int = 1, p2: Int = 2, p3: Int = 3){} + +// FILE: first.after.kt +// "Import" "true" +// ERROR: Cannot find a parameter with this name: p2 + +package main + +import other.foo + +class X { + fun foo() { + } + + fun f(p: Int) { + foo(p2 = 0) + } +} + diff --git a/idea/testData/quickfix/autoImports/mismatchingArgs/notForIncompleteCall.test b/idea/testData/quickfix/autoImports/mismatchingArgs/notForIncompleteCall.test new file mode 100644 index 00000000000..369d3489600 --- /dev/null +++ b/idea/testData/quickfix/autoImports/mismatchingArgs/notForIncompleteCall.test @@ -0,0 +1,26 @@ +// FILE: first.before.kt +// "Import" "false" +// ERROR: Type mismatch: inferred type is Int but String was expected +// ACTION: Add 'toString()' call +// ACTION: Change parameter 'p' type of function 'main.X.foo' to 'Int' +// ACTION: Convert to expression body +// ACTION: Create function 'foo' + +package main + +class X { + fun foo(p: String) { + } + + fun f(p: Int) { + foo(p, ) + } +} + +// FILE: second.kt +package other + +import main.X + +fun X.foo(p: Int) { +} diff --git a/idea/testData/quickfix/autoImports/mismatchingArgs/smartCast.test b/idea/testData/quickfix/autoImports/mismatchingArgs/smartCast.test new file mode 100644 index 00000000000..c6edc01fdf1 --- /dev/null +++ b/idea/testData/quickfix/autoImports/mismatchingArgs/smartCast.test @@ -0,0 +1,44 @@ +// FILE: first.before.kt +// "Import" "true" +// ERROR: Too many arguments for public final fun foo(): Unit defined in main.X + +package main + +class X { + fun foo() { + } + + fun f(p: Any) { + if (p is String) { + foo(p) + } + } +} + +// FILE: second.kt +package other + +import main.X + +fun X.foo(p: String) { +} + +// FILE: first.after.kt +// "Import" "true" +// ERROR: Too many arguments for public final fun foo(): Unit defined in main.X + +package main + +import other.foo + +class X { + fun foo() { + } + + fun f(p: Any) { + if (p is String) { + foo(p) + } + } +} + diff --git a/idea/testData/quickfix/autoImports/mismatchingArgs/topLevelFun.test b/idea/testData/quickfix/autoImports/mismatchingArgs/topLevelFun.test new file mode 100644 index 00000000000..c9f2a2db275 --- /dev/null +++ b/idea/testData/quickfix/autoImports/mismatchingArgs/topLevelFun.test @@ -0,0 +1,38 @@ +// FILE: first.before.kt +// "Import" "true" +// ERROR: Too many arguments for public final fun foo(): Unit defined in main.X + +package main + +class X { + fun foo() { + } + + fun f() { + foo(1) + } +} + +// FILE: second.kt +package other + +fun foo(p: Int) { +} + +// FILE: first.after.kt +// "Import" "true" +// ERROR: Too many arguments for public final fun foo(): Unit defined in main.X + +package main + +import other.foo + +class X { + fun foo() { + } + + fun f() { + foo(1) + } +} + diff --git a/idea/testData/quickfix/autoImports/mismatchingArgs/topLevelFun_notWithReceiver.test b/idea/testData/quickfix/autoImports/mismatchingArgs/topLevelFun_notWithReceiver.test new file mode 100644 index 00000000000..57db1837669 --- /dev/null +++ b/idea/testData/quickfix/autoImports/mismatchingArgs/topLevelFun_notWithReceiver.test @@ -0,0 +1,24 @@ +// FILE: first.before.kt +// "Import" "false" +// ERROR: Too many arguments for public final fun foo(): Unit defined in main.X +// ACTION: Add parameter to function 'foo' +// ACTION: Convert to expression body +// ACTION: Create extension function 'X.foo' +// ACTION: Create member function 'X.foo' + +package main + +class X { + fun foo() { + } + + fun f() { + this.foo(1) + } +} + +// FILE: second.kt +package other + +fun foo(p: Int) { +} diff --git a/idea/testData/quickfix/autoImports/mismatchingArgs/typeMismatch.test b/idea/testData/quickfix/autoImports/mismatchingArgs/typeMismatch.test new file mode 100644 index 00000000000..e9cb018d058 --- /dev/null +++ b/idea/testData/quickfix/autoImports/mismatchingArgs/typeMismatch.test @@ -0,0 +1,40 @@ +// FILE: first.before.kt +// "Import" "true" +// ERROR: Type mismatch: inferred type is Int but String was expected + +package main + +class X { + fun foo(p: String) { + } + + fun f(p: Int) { + foo(p) + } +} + +// FILE: second.kt +package other + +import main.X + +fun X.foo(p: Int) { +} + +// FILE: first.after.kt +// "Import" "true" +// ERROR: Type mismatch: inferred type is Int but String was expected + +package main + +import other.foo + +class X { + fun foo(p: String) { + } + + fun f(p: Int) { + foo(p) + } +} + diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java index ba96c764a1a..abb6534502e 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java @@ -580,6 +580,99 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/withSmartCastQualifier.before.Main.kt"); doTestWithExtraFile(fileName); } + + @TestMetadata("idea/testData/quickfix/autoImports/mismatchingArgs") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class MismatchingArgs extends AbstractQuickFixMultiFileTest { + public void testAllFilesPresentInMismatchingArgs() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/autoImports/mismatchingArgs"), Pattern.compile("^(\\w+)\\.((before\\.Main\\.\\w+)|(test))$"), true); + } + + @TestMetadata("checkArgumentTypes.test") + public void testCheckArgumentTypes() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/mismatchingArgs/checkArgumentTypes.test"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("constantExpectedTypeMismatch.test") + public void testConstantExpectedTypeMismatch() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/mismatchingArgs/constantExpectedTypeMismatch.test"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("expectedTypeRequired.test") + public void testExpectedTypeRequired() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/mismatchingArgs/expectedTypeRequired.test"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("extensionExplicitReceiver.test") + public void testExtensionExplicitReceiver() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/mismatchingArgs/extensionExplicitReceiver.test"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("extensionImplicitReceiver.test") + public void testExtensionImplicitReceiver() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/mismatchingArgs/extensionImplicitReceiver.test"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("extensionWrongReceiver.test") + public void testExtensionWrongReceiver() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/mismatchingArgs/extensionWrongReceiver.test"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("ignoreErrorsOutsideCall.test") + public void testIgnoreErrorsOutsideCall() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/mismatchingArgs/ignoreErrorsOutsideCall.test"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("lambdaArgument.test") + public void testLambdaArgument() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/mismatchingArgs/lambdaArgument.test"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("namedArgument.test") + public void testNamedArgument() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/mismatchingArgs/namedArgument.test"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("notForIncompleteCall.test") + public void testNotForIncompleteCall() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/mismatchingArgs/notForIncompleteCall.test"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("smartCast.test") + public void testSmartCast() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/mismatchingArgs/smartCast.test"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("topLevelFun.test") + public void testTopLevelFun() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/mismatchingArgs/topLevelFun.test"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("topLevelFun_notWithReceiver.test") + public void testTopLevelFun_notWithReceiver() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/mismatchingArgs/topLevelFun_notWithReceiver.test"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("typeMismatch.test") + public void testTypeMismatch() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/mismatchingArgs/typeMismatch.test"); + doTestWithExtraFile(fileName); + } + } } @TestMetadata("idea/testData/quickfix/changeSignature") diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java index d346d229e7d..2240a0b2231 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -771,6 +771,7 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/unresolvedReferenceInCall.kt"); doTest(fileName); } + } @TestMetadata("idea/testData/quickfix/canBeParameter")