From 16736e3a0e3aa809302a6e1b925aee059db1d7ea Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Fri, 18 Mar 2016 18:34:37 +0300 Subject: [PATCH] Resolve anonymous functions in block as expression #KT-7265 Fixed --- .../resolve/FunctionDescriptorResolver.kt | 2 +- .../expressions/ExpressionTypingServices.java | 4 +- .../ExpressionTypingVisitorForStatements.java | 2 +- .../expressions/FunctionsTypingVisitor.kt | 12 ++-- .../FunctionWithMissingNames.kt | 8 +-- .../anonymousFunAsLastExpressionInBlock.kt | 43 ++++++++++++++ .../anonymousFunAsLastExpressionInBlock.txt | 4 ++ ...anonymousFunUnusedLastExpressionInBlock.kt | 21 +++++++ ...nonymousFunUnusedLastExpressionInBlock.txt | 3 + .../namedFunAsLastExpressionInBlock.kt | 58 +++++++++++++++++++ .../namedFunAsLastExpressionInBlock.txt | 7 +++ .../duplicateJvmSignature/missingNames.kt | 4 +- .../checkers/DiagnosticsTestGenerated.java | 18 ++++++ 13 files changed, 171 insertions(+), 15 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/declarationChecks/anonymousFunAsLastExpressionInBlock.kt create mode 100644 compiler/testData/diagnostics/tests/declarationChecks/anonymousFunAsLastExpressionInBlock.txt create mode 100644 compiler/testData/diagnostics/tests/declarationChecks/anonymousFunUnusedLastExpressionInBlock.kt create mode 100644 compiler/testData/diagnostics/tests/declarationChecks/anonymousFunUnusedLastExpressionInBlock.txt create mode 100644 compiler/testData/diagnostics/tests/declarationChecks/namedFunAsLastExpressionInBlock.kt create mode 100644 compiler/testData/diagnostics/tests/declarationChecks/namedFunAsLastExpressionInBlock.txt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionDescriptorResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionDescriptorResolver.kt index 26dd2abd0d3..8b80e0bf626 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionDescriptorResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionDescriptorResolver.kt @@ -70,7 +70,7 @@ class FunctionDescriptorResolver( trace: BindingTrace, dataFlowInfo: DataFlowInfo ): SimpleFunctionDescriptor { - if (function.getName() == null) trace.report(FUNCTION_DECLARATION_WITH_NO_NAME.on(function)) + if (function.name == null) trace.report(FUNCTION_DECLARATION_WITH_NO_NAME.on(function)) return resolveFunctionDescriptor( SimpleFunctionDescriptorImpl::create, containingDescriptor, scope, function, trace, dataFlowInfo, TypeUtils.NO_EXPECTED_TYPE) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingServices.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingServices.java index 36e0aadd052..f09cf372dce 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingServices.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingServices.java @@ -293,7 +293,9 @@ public class ExpressionTypingServices { if (coercionStrategyForLastExpression == COERCION_TO_UNIT) { boolean mightBeUnit = false; if (statementExpression instanceof KtDeclaration) { - mightBeUnit = true; + if (!(statementExpression instanceof KtNamedFunction) || statementExpression.getName() != null) { + mightBeUnit = true; + } } if (statementExpression instanceof KtBinaryExpression) { KtBinaryExpression binaryExpression = (KtBinaryExpression) statementExpression; diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingVisitorForStatements.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingVisitorForStatements.java index e53ebf21ec5..2f378a27bd5 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingVisitorForStatements.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingVisitorForStatements.java @@ -138,7 +138,7 @@ public class ExpressionTypingVisitorForStatements extends ExpressionTypingVisito @Override public KotlinTypeInfo visitNamedFunction(@NotNull KtNamedFunction function, ExpressionTypingContext context) { - return functions.visitNamedFunction(function, context, true, scope); + return functions.visitNamedFunction(function, context, /* isDeclaration = */ function.getName() != null, scope); } @Override diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/FunctionsTypingVisitor.kt b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/FunctionsTypingVisitor.kt index 7ba276f0809..8ab95ecc096 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/FunctionsTypingVisitor.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/FunctionsTypingVisitor.kt @@ -48,17 +48,17 @@ import org.jetbrains.kotlin.utils.addIfNotNull internal class FunctionsTypingVisitor(facade: ExpressionTypingInternals) : ExpressionTypingVisitor(facade) { override fun visitNamedFunction(function: KtNamedFunction, data: ExpressionTypingContext): KotlinTypeInfo { - return visitNamedFunction(function, data, false, null) + return visitNamedFunction(function, data, isDeclaration = false, statementScope = null) } fun visitNamedFunction( function: KtNamedFunction, context: ExpressionTypingContext, - isStatement: Boolean, - statementScope: LexicalWritableScope? // must be not null if isStatement + isDeclaration: Boolean, + statementScope: LexicalWritableScope? // must be not null if isDeclaration ): KotlinTypeInfo { checkReservedAsync(context, function) - if (!isStatement) { + if (!isDeclaration) { // function expression if (!function.getTypeParameters().isEmpty()) { context.trace.report(TYPE_PARAMETERS_NOT_ALLOWED.on(function)) @@ -79,7 +79,7 @@ internal class FunctionsTypingVisitor(facade: ExpressionTypingInternals) : Expre } val functionDescriptor: SimpleFunctionDescriptor - if (isStatement) { + if (isDeclaration) { functionDescriptor = components.functionDescriptorResolver.resolveFunctionDescriptor( context.scope.ownerDescriptor, context.scope, function, context.trace, context.dataFlowInfo) assert(statementScope != null) { @@ -115,7 +115,7 @@ internal class FunctionsTypingVisitor(facade: ExpressionTypingInternals) : Expre components.identifierChecker.checkDeclaration(function, context.trace) components.declarationsCheckerBuilder.withTrace(context.trace).checkFunction(function, functionDescriptor) - if (isStatement) { + if (isDeclaration) { return createTypeInfo(components.dataFlowAnalyzer.checkStatementType(function, context), context) } else { diff --git a/compiler/testData/diagnostics/tests/declarationChecks/FunctionWithMissingNames.kt b/compiler/testData/diagnostics/tests/declarationChecks/FunctionWithMissingNames.kt index 978845bf5ea..525e844de5c 100644 --- a/compiler/testData/diagnostics/tests/declarationChecks/FunctionWithMissingNames.kt +++ b/compiler/testData/diagnostics/tests/declarationChecks/FunctionWithMissingNames.kt @@ -18,9 +18,9 @@ class Outer { } fun outerFun() { - fun () {} - fun B.() {} + fun () {} + fun B.() {} - @a fun () {} - fun @a A.() {} + @a fun () {} + fun @a A.() {} } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/declarationChecks/anonymousFunAsLastExpressionInBlock.kt b/compiler/testData/diagnostics/tests/declarationChecks/anonymousFunAsLastExpressionInBlock.kt new file mode 100644 index 00000000000..182a0b8a7ca --- /dev/null +++ b/compiler/testData/diagnostics/tests/declarationChecks/anonymousFunAsLastExpressionInBlock.kt @@ -0,0 +1,43 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE +// !CHECK_TYPE +fun foo(block: () -> (() -> Int)) {} + +fun test() { + foo { fun(): Int {return 1} } + foo({ fun() = 1 }) + + val x1 = + if (1 == 1) + fun(): Int {return 1} + else + fun() = 1 + + val x2 = + if (1 == 1) { + fun(): Int { + return 1 + } + } + else + fun() = 1 + + val x3 = when (1) { + 0 -> fun(): Int {return 1} + else -> fun() = 1 + } + + val x31 = when (1) { + 0 -> { + fun(): Int {return 1} + } + else -> fun() = 1 + } + + val x4 = { + y: Int -> fun(): Int {return 1} + } + + x4 checkType { _>>() } + + { y: Int -> fun(): Int {return 1} } +} diff --git a/compiler/testData/diagnostics/tests/declarationChecks/anonymousFunAsLastExpressionInBlock.txt b/compiler/testData/diagnostics/tests/declarationChecks/anonymousFunAsLastExpressionInBlock.txt new file mode 100644 index 00000000000..08384cb3db4 --- /dev/null +++ b/compiler/testData/diagnostics/tests/declarationChecks/anonymousFunAsLastExpressionInBlock.txt @@ -0,0 +1,4 @@ +package + +public fun foo(/*0*/ block: () -> () -> kotlin.Int): kotlin.Unit +public fun test(): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/declarationChecks/anonymousFunUnusedLastExpressionInBlock.kt b/compiler/testData/diagnostics/tests/declarationChecks/anonymousFunUnusedLastExpressionInBlock.kt new file mode 100644 index 00000000000..e8ba7c71cb2 --- /dev/null +++ b/compiler/testData/diagnostics/tests/declarationChecks/anonymousFunUnusedLastExpressionInBlock.kt @@ -0,0 +1,21 @@ +fun unusedExpressions() { + if (1 == 1) + fun(): Int {return 1} + else + fun() = 1 + + if (1 == 1) { + fun(): Int { + return 1 + } + } + else + fun() = 1 + + when (1) { + 0 -> fun(): Int {return 1} + else -> fun() = 1 + } + + fun() = 1 +} diff --git a/compiler/testData/diagnostics/tests/declarationChecks/anonymousFunUnusedLastExpressionInBlock.txt b/compiler/testData/diagnostics/tests/declarationChecks/anonymousFunUnusedLastExpressionInBlock.txt new file mode 100644 index 00000000000..22e14013087 --- /dev/null +++ b/compiler/testData/diagnostics/tests/declarationChecks/anonymousFunUnusedLastExpressionInBlock.txt @@ -0,0 +1,3 @@ +package + +public fun unusedExpressions(): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/declarationChecks/namedFunAsLastExpressionInBlock.kt b/compiler/testData/diagnostics/tests/declarationChecks/namedFunAsLastExpressionInBlock.kt new file mode 100644 index 00000000000..e72fe43bc1f --- /dev/null +++ b/compiler/testData/diagnostics/tests/declarationChecks/namedFunAsLastExpressionInBlock.kt @@ -0,0 +1,58 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE +// !CHECK_TYPE +fun foo(block: () -> (() -> Int)) {} + +fun test() { + val x = fun named1(x: Int): Int { return 1 } + x checkType { _>() } + + foo { Int)!>fun named2(): Int {return 1} } + foo({ fun named3() = 1 }) + + val x1 = + if (1 == 1) + // TODO: Diagnostic content could be better + Int)!>fun named4(): Int {return 1} + else + fun named5() = 1 + + val x2 = + if (1 == 1) { + fun named6(): Int { + return 1 + } + } + else + fun named7() = 1 + + val x3 = when (1) { + 0 -> fun named8(): Int {return 1} + else -> fun named9() = 1 + } + + val x31 = when (1) { + 0 -> { + fun named10(): Int {return 1} + } + else -> fun named11() = 1 + } + + val x4 = { + y: Int -> fun named12(): Int {return 1} + } + + x4 checkType { _>() } + + { y: Int -> fun named14(): Int {return 1} } +} + +fun run(block: () -> T): T = null!! +fun run2(block: () -> Unit): Unit = null!! + +fun success() { + run { fun named1() = 1 } + run2 { fun named2() = 1 } + + val x = run { fun named3() = 1 } + x checkType { _() } +} diff --git a/compiler/testData/diagnostics/tests/declarationChecks/namedFunAsLastExpressionInBlock.txt b/compiler/testData/diagnostics/tests/declarationChecks/namedFunAsLastExpressionInBlock.txt new file mode 100644 index 00000000000..95dc03d37ac --- /dev/null +++ b/compiler/testData/diagnostics/tests/declarationChecks/namedFunAsLastExpressionInBlock.txt @@ -0,0 +1,7 @@ +package + +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 +public fun success(): kotlin.Unit +public fun test(): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/duplicateJvmSignature/missingNames.kt b/compiler/testData/diagnostics/tests/duplicateJvmSignature/missingNames.kt index cc975df88e6..fe4d64999cf 100644 --- a/compiler/testData/diagnostics/tests/duplicateJvmSignature/missingNames.kt +++ b/compiler/testData/diagnostics/tests/duplicateJvmSignature/missingNames.kt @@ -58,10 +58,10 @@ class Outer { } fun outerFun() { - fun () { + fun () { } - fun () { + fun () { } } diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 7a7e8dd13f8..ffba92d04a0 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -4320,6 +4320,18 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("anonymousFunAsLastExpressionInBlock.kt") + public void testAnonymousFunAsLastExpressionInBlock() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/declarationChecks/anonymousFunAsLastExpressionInBlock.kt"); + doTest(fileName); + } + + @TestMetadata("anonymousFunUnusedLastExpressionInBlock.kt") + public void testAnonymousFunUnusedLastExpressionInBlock() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/declarationChecks/anonymousFunUnusedLastExpressionInBlock.kt"); + doTest(fileName); + } + @TestMetadata("ComponentFunctionReturnTypeMismatch.kt") public void testComponentFunctionReturnTypeMismatch() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/declarationChecks/ComponentFunctionReturnTypeMismatch.kt"); @@ -4434,6 +4446,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("namedFunAsLastExpressionInBlock.kt") + public void testNamedFunAsLastExpressionInBlock() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/declarationChecks/namedFunAsLastExpressionInBlock.kt"); + doTest(fileName); + } + @TestMetadata("packageDeclarationModifiers.kt") public void testPackageDeclarationModifiers() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/declarationChecks/packageDeclarationModifiers.kt");