diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt index ee6ab790978..db717354c52 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt @@ -45,7 +45,8 @@ private val DEFAULT_CALL_CHECKERS = listOf( CallableReferenceCompatibilityChecker(), LateinitIntrinsicApplicabilityChecker, UnderscoreUsageChecker, AssigningNamedArgumentToVarargChecker(), PrimitiveNumericComparisonCallChecker, LambdaWithSuspendModifierCallChecker, - UselessElvisCallChecker(), ResultTypeWithNullableOperatorsChecker(), NullableVarargArgumentCallChecker + UselessElvisCallChecker(), ResultTypeWithNullableOperatorsChecker(), NullableVarargArgumentCallChecker, + NamedFunAsExpressionChecker ) private val DEFAULT_TYPE_CHECKERS = emptyList() private val DEFAULT_CLASSIFIER_USAGE_CHECKERS = listOf( diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/NamedFunAsExpressionChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/NamedFunAsExpressionChecker.kt new file mode 100644 index 00000000000..afb0372e72c --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/NamedFunAsExpressionChecker.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.resolve.calls.checkers + +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.config.LanguageFeature +import org.jetbrains.kotlin.diagnostics.Errors.ANONYMOUS_FUNCTION_WITH_NAME +import org.jetbrains.kotlin.diagnostics.reportDiagnosticOnce +import org.jetbrains.kotlin.psi.KtNamedFunction +import org.jetbrains.kotlin.psi.KtPsiUtil +import org.jetbrains.kotlin.psi.psiUtil.isFunctionalExpression +import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.utils.addToStdlib.safeAs + + +/** + * That checker used to check that only anonymous functions are used as function arguments + * + * Examples: + * + * fun foo(obj: Any) {} + * foo(fun named() {}) // bad + * + * // here `if` is synthetic function call + * val x = if (b) fun named1() {} else fun named2() {} + */ +object NamedFunAsExpressionChecker : CallChecker { + override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) { + if (!context.languageVersionSettings.supportsFeature(LanguageFeature.NewInference)) return + for (argument in resolvedCall.valueArguments.values.filterIsInstance(ExpressionValueArgument::class.java)) { + val expression = KtPsiUtil.deparenthesize(argument.valueArgument?.getArgumentExpression()) as? KtNamedFunction ?: continue + if (!expression.isFunctionalExpression()) { + /* + * There is one another place, where ANONYMOUS_FUNCTION_WITH_NAME is reported + * ([org.jetbrains.kotlin.types.expressions.FunctionsTypingVisitor]), so in some cases there is + * can be duplicated diagnostic. It is a little problem that should go when we will resolve assignments + * as calls, so that checker will report all cases of ANONYMOUS_FUNCTION_WITH_NAME diagnostic. + */ + context.trace.reportDiagnosticOnce(ANONYMOUS_FUNCTION_WITH_NAME.on(expression.nameIdentifier!!)) + } + } + } +} \ No newline at end of file diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/NewCallArguments.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/NewCallArguments.kt index ee2e4d4fc40..7c78c461309 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/NewCallArguments.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/NewCallArguments.kt @@ -13,6 +13,7 @@ import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getParentOfType +import org.jetbrains.kotlin.psi.psiUtil.isFunctionalExpression import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.StatementFilter import org.jetbrains.kotlin.resolve.TypeResolver @@ -216,6 +217,8 @@ fun processFunctionalExpression( ) is KtNamedFunction -> { + // if function is a not anonymous function, resolve it as simple expression + if (!postponedExpression.isFunctionalExpression()) return null val receiverType = resolveType(outerCallContext, postponedExpression.receiverTypeReference, typeResolver) val parametersTypes = resolveParametersTypes(outerCallContext, postponedExpression, typeResolver) ?: emptyArray() val returnType = resolveType(outerCallContext, postponedExpression.typeReference, typeResolver) diff --git a/compiler/testData/diagnostics/tests/declarationChecks/namedFunAsLastExpressionInBlock.kt b/compiler/testData/diagnostics/tests/declarationChecks/namedFunAsLastExpressionInBlock.kt index 232dc9ca5be..455f40aac32 100644 --- a/compiler/testData/diagnostics/tests/declarationChecks/namedFunAsLastExpressionInBlock.kt +++ b/compiler/testData/diagnostics/tests/declarationChecks/namedFunAsLastExpressionInBlock.kt @@ -27,15 +27,15 @@ fun test() { fun named7() = 1 val x3 = when (1) { - 0 -> fun named8(): Int {return 1} - else -> fun named9() = 1 + 0 -> fun named8(): Int {return 1} + else -> fun named9() = 1 } val x31 = when (1) { 0 -> { - fun named10(): Int {return 1} + fun named10(): Int {return 1} } - else -> fun named11() = 1 + else -> fun named11() = 1 } val x4 = { @@ -45,15 +45,31 @@ fun test() { x4 checkType { _>() } { y: Int -> fun named14(): Int {return 1} } + val b = (fun named15(): Boolean { return true })() + + baz(fun named16(){}) } +fun bar() = fun named() {} + fun run(block: () -> T): T = null!! fun run2(block: () -> Unit): Unit = null!! +fun baz(obj: Any?) {} fun success() { run { fun named1() = 1 } run2 { fun named2() = 1 } val x = run { fun named3() = 1 } - x checkType { _() } + x checkType { _() } + + val y = when (1) { + 0 -> { + fun named4(): Int {return 1} + } + else -> { + fun named5(): Int {return 1} + } + } + y checkType { _() } } diff --git a/compiler/testData/diagnostics/tests/declarationChecks/namedFunAsLastExpressionInBlock.txt b/compiler/testData/diagnostics/tests/declarationChecks/namedFunAsLastExpressionInBlock.txt index 95dc03d37ac..1d97fa60d92 100644 --- a/compiler/testData/diagnostics/tests/declarationChecks/namedFunAsLastExpressionInBlock.txt +++ b/compiler/testData/diagnostics/tests/declarationChecks/namedFunAsLastExpressionInBlock.txt @@ -1,5 +1,7 @@ package +public fun bar(): () -> kotlin.Unit +public fun baz(/*0*/ obj: kotlin.Any?): kotlin.Unit public fun foo(/*0*/ block: () -> () -> kotlin.Int): kotlin.Unit public fun run(/*0*/ block: () -> T): T public fun run2(/*0*/ block: () -> kotlin.Unit): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/inference/localFunctionInsideIfBlock.kt b/compiler/testData/diagnostics/tests/inference/localFunctionInsideIfBlock.kt index 424704c3e7a..a713982a29e 100644 --- a/compiler/testData/diagnostics/tests/inference/localFunctionInsideIfBlock.kt +++ b/compiler/testData/diagnostics/tests/inference/localFunctionInsideIfBlock.kt @@ -1,10 +1,10 @@ // !LANGUAGE: +NewInference fun bar() { - if (true) { - fun local() { - } - } else { + if (true) { + fun local() { + } + } else { } }