[FIR] Implement FirReturnAllowedChecker

Supported diagnostics:
- RETURN_NOT_ALLOWED
- RETURN_IN_FUNCTION_WITH_EXPRESSION_BODY
This commit is contained in:
Dmitriy Novozhilov
2021-04-05 10:15:21 +03:00
committed by TeamCityServer
parent 254ff77977
commit 3cb17ac2f0
59 changed files with 263 additions and 343 deletions
@@ -8,12 +8,12 @@ fun unitEmpty() : Unit {}
fun unitEmptyReturn() : Unit {return}
fun unitIntReturn() : Unit {return 1}
fun unitUnitReturn() : Unit {return Unit}
fun test1() : Any = {return}
fun test1() : Any = {<!RETURN_NOT_ALLOWED!>return<!>}
fun test2() : Any = a@ {return@a 1}
fun test3() : Any { return }
fun test4(): ()-> Unit = { return@test4 }
fun test4(): ()-> Unit = { <!RETURN_NOT_ALLOWED!>return@test4<!> }
fun test5(): Any = l@{ return@l }
fun test6(): Any = {return 1}
fun test6(): Any = {<!RETURN_NOT_ALLOWED!>return<!> 1}
fun bbb() {
return 1
@@ -137,7 +137,7 @@ fun blockNoReturnIfUnitInOneBranch(): Int {
fun nonBlockReturnIfEmptyIf(): Int = if (1 < 2) {} else {}
fun nonBlockNoReturnIfUnitInOneBranch(): Int = if (1 < 2) {} else 2
val a = return 1
val a = <!RETURN_NOT_ALLOWED!>return<!> 1
class A() {
}
+3 -3
View File
@@ -6,12 +6,12 @@ class A {
if (1 < 2)
return@inner
else
return@outer
<!RETURN_NOT_ALLOWED!>return@outer<!>
}
if (1 < 2)
return@A
<!RETURN_NOT_ALLOWED!>return@A<!>
else if (2 < 3)
return@inner
<!RETURN_NOT_ALLOWED!>return@inner<!>
return@outer
}
}
@@ -1,3 +1,3 @@
fun foo1(): () -> String = return { "some long expression "}
fun foo2(): () -> String = return@label { "some long expression "}
fun foo3(): () -> String = return<!SYNTAX!>@<!> { "some long expression "}
fun foo1(): () -> String = <!RETURN_IN_FUNCTION_WITH_EXPRESSION_BODY!>return<!> { "some long expression "}
fun foo2(): () -> String = <!RETURN_IN_FUNCTION_WITH_EXPRESSION_BODY, RETURN_NOT_ALLOWED!>return@label<!> { "some long expression "}
fun foo3(): () -> String = <!RETURN_IN_FUNCTION_WITH_EXPRESSION_BODY!>return<!><!SYNTAX!>@<!> { "some long expression "}
@@ -1,6 +1,6 @@
fun find2(): Any? {
fun visit(element: Any) {
return@find2 element
<!RETURN_NOT_ALLOWED!>return@find2<!> element
}
return null
}
@@ -10,7 +10,7 @@ fun find2(): Any? {
fun find(): Any? {
object : Any() {
fun visit(element: Any) {
return@find element
<!RETURN_NOT_ALLOWED!>return@find<!> element
}
}
return null
@@ -18,7 +18,7 @@ fun find(): Any? {
fun find4(): Any? {
inline fun visit(element: Any) {
return@find4 element
<!RETURN_NOT_ALLOWED!>return@find4<!> element
}
return null
}
@@ -26,7 +26,7 @@ fun find4(): Any? {
fun find3(): Any? {
object : Any() {
inline fun visit(element: Any) {
return@find3 element
<!RETURN_NOT_ALLOWED!>return@find3<!> element
}
}
return null
@@ -12,11 +12,11 @@ fun test() {
doSmth(if (true) 3 else return, <!TOO_MANY_ARGUMENTS!>1<!>)
}
val a : Nothing = return 1
val a : Nothing = <!RETURN_NOT_ALLOWED!>return<!> 1
val b = return 1
val b = <!RETURN_NOT_ALLOWED!>return<!> 1
val c = doSmth(if (true) 3 else return)
val c = doSmth(if (true) 3 else <!RETURN_NOT_ALLOWED!>return<!>)
fun f(mi: Int = if (true) 0 else return) {}
@@ -10,11 +10,11 @@ fun f() = object : ClassData {
fun g() = object : ClassData {
init {
if (true) {
return 0
<!RETURN_NOT_ALLOWED!>return<!> 0
}
}
fun some(): Int {
return 6
}
}
}
@@ -31,7 +31,7 @@ fun testAnnotatedLambdaLabel() =
fun testLambdaMultipleLabels1() =
lambda1@ lambda2@ {
return@lambda1
<!RETURN_NOT_ALLOWED!>return@lambda1<!>
}
fun testLambdaMultipleLabels2() =
@@ -46,7 +46,7 @@ fun testAnonymousFunctionLabel() =
fun testLoopLabelInReturn(xs: List<Int>) {
L@ for (x in xs) {
if (x > 0) return@L
if (x > 0) <!RETURN_NOT_ALLOWED!>return@L<!>
}
}
@@ -59,4 +59,4 @@ fun testHighOrderFunctionCallLabelInReturn() {
L@ run {
return@L
}
}
}
@@ -31,7 +31,7 @@ fun testAnnotatedLambdaLabel() =
fun testLambdaMultipleLabels1() =
lambda1@ lambda2@ {
return@lambda1
<!RETURN_NOT_ALLOWED!>return@lambda1<!>
}
fun testLambdaMultipleLabels2() =
@@ -46,7 +46,7 @@ fun testAnonymousFunctionLabel() =
fun testLoopLabelInReturn(xs: List<Int>) {
L@ for (x in xs) {
if (x > 0) return@L
if (x > 0) <!RETURN_NOT_ALLOWED!>return@L<!>
}
}
@@ -59,4 +59,4 @@ fun testHighOrderFunctionCallLabelInReturn() {
L@ run {
return@L
}
}
}
@@ -1,34 +1,34 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE
// KT-5068 Add special error for scala-like syntax 'fun foo(): Int = { 1 }'
fun test1(): Int = { return 1 }
fun test1(): Int = { <!RETURN_NOT_ALLOWED!>return<!> 1 }
fun test2(): Int = { 1 }
val test3: () -> Int = fun (): Int = { return 1 }
val test3: () -> Int = fun (): Int = { <!RETURN_NOT_ALLOWED!>return<!> 1 }
val test4: () -> Int = fun (): Int = { 1 }
fun test5(): Int { return { 1 } }
fun test6(): Int = fun (): Int = 1
fun outer() {
fun test1(): Int = { return 1 }
fun test1(): Int = { <!RETURN_NOT_ALLOWED!>return<!> 1 }
fun test2(): Int = { 1 }
val test3: () -> Int = fun (): Int = { return 1 }
val test3: () -> Int = fun (): Int = { <!RETURN_NOT_ALLOWED!>return<!> 1 }
val test4: () -> Int = fun (): Int = { 1 }
fun test5(): Int { return { 1 } }
fun test6(): Int = fun (): Int = 1
}
class Outer {
fun test1(): Int = { return 1 }
fun test1(): Int = { <!RETURN_NOT_ALLOWED!>return<!> 1 }
fun test2(): Int = { 1 }
val test3: () -> Int = fun (): Int = { return 1 }
val test3: () -> Int = fun (): Int = { <!RETURN_NOT_ALLOWED!>return<!> 1 }
val test4: () -> Int = fun (): Int = { 1 }
fun test5(): Int { return { 1 } }
fun test6(): Int = fun (): Int = 1
class Nested {
fun test1(): Int = { return 1 }
fun test1(): Int = { <!RETURN_NOT_ALLOWED!>return<!> 1 }
fun test2(): Int = { 1 }
val test3: () -> Int = fun (): Int = { return 1 }
val test3: () -> Int = fun (): Int = { <!RETURN_NOT_ALLOWED!>return<!> 1 }
val test4: () -> Int = fun (): Int = { 1 }
fun test5(): Int { return { 1 } }
fun test6(): Int = fun (): Int = 1
@@ -7,5 +7,5 @@ fun autolabel(l: List<Int>) = l.map (fun (i: Int): Int {
})
fun unresolvedMapLabel(l: List<Int>) = l.map (l@ fun(i: Int): Int {
return@map 4
<!RETURN_NOT_ALLOWED!>return@map<!> 4
})
@@ -1,22 +0,0 @@
// !DIAGNOSTICS: -UNUSED_VARIABLE
fun test() {
fun bar() {
val bas = fun() {
return@bar
}
}
val bar = fun() {
return@test
}
}
fun foo() {
val bal = bag@ fun () {
val bar = fun() {
return@bag
}
return@bag
}
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_VARIABLE
fun test() {
@@ -1,11 +0,0 @@
fun f() {
foo {
bar {
return@foo 1
}
return@foo 1
}
}
fun foo(a: Any) {}
fun bar(a: Any) {}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
fun f() {
foo {
bar {
@@ -1,19 +0,0 @@
// !WITH_NEW_INFERENCE
fun test() {
run {return}
run {}
}
fun test2() {
run {return@test2}
run {}
}
fun test3() {
fun test4() {
run {return@test3}
run {}
}
}
fun <T> run(f: () -> T): T { return f() }
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !WITH_NEW_INFERENCE
fun test() {
run {<!RETURN_NOT_ALLOWED!>return<!>}
@@ -1,11 +0,0 @@
// !CHECK_TYPE
fun test2(a: Int) {
val x = run f@{
if (a > 0) return
return@f 1
}
checkSubtype<Int>(x)
}
fun <T> run(f: () -> T): T { return f() }
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !CHECK_TYPE
fun test2(a: Int) {
@@ -1,31 +0,0 @@
fun inlineCall(): String {
inlineFun {
if (true) {
return@inlineCall ""
}
1
}
return "x"
}
inline fun inlineFun(s: ()->Int) {
s()
}
fun noInlineCall(): String {
noInline {
if (true) {
return@noInlineCall ""
}
1
}
return "x"
}
fun noInline(s: ()->Int) {
s()
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
fun inlineCall(): String {
inlineFun {
if (true) {
@@ -1,10 +0,0 @@
// !DIAGNOSTICS: -NOTHING_TO_INLINE
fun main() {
test {
return
}
}
inline fun test(noinline lambda: () -> Unit) {
lambda()
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -NOTHING_TO_INLINE
fun main() {
test {
@@ -1,43 +0,0 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION -UNUSED_PARAMETER -UNUSED_VARIABLE -NOTHING_TO_INLINE
fun <R> fun1(p: () -> R) {
inlineFun {
p()
}
}
fun <R> fun1ValueArgument(p: () -> R) {
inlineFun ({
p()
})
}
fun <R> fun3(p: () -> R) {
inlineFun {
return;
}
}
fun <R> fun3ValueArgument(p: () -> R) {
inlineFun ({
return;
})
}
fun <R> fun4(p: () -> R) {
inlineFun lambda@ {
return@lambda p();
}
}
fun <R> fun4ValueArgument(p: () -> R) {
inlineFun (lambda@ {
return@lambda p();
})
}
inline fun <R> inlineFun(crossinline p: () -> R) {
p()
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_EXPRESSION -UNUSED_PARAMETER -UNUSED_VARIABLE -NOTHING_TO_INLINE
fun <R> fun1(p: () -> R) {
@@ -1,25 +0,0 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION -UNUSED_PARAMETER -UNUSED_VARIABLE -NOTHING_TO_INLINE
class Z {
inline infix fun <R> inlineFun(crossinline p: () -> R) {
p()
}
}
fun <R> fun1(p: () -> R) {
Z() inlineFun {
p()
}
}
fun <R> fun3(p: () -> R) {
Z() inlineFun {
return;
}
}
fun <R> fun4(p: () -> R) {
Z() inlineFun lambda@ {
return@lambda p();
}
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_EXPRESSION -UNUSED_PARAMETER -UNUSED_VARIABLE -NOTHING_TO_INLINE
class Z {
@@ -1,24 +0,0 @@
inline fun <R> doCall(p: () -> R) {
p()
}
inline fun <R> doCallInt(p: () -> R): R {
return p()
}
class A {
var result: Int = doCallInt { return this };
var field: Int
get() {
doCall { return 1 }
return 2
}
set(v: Int) {
doCall {
result = v / 2
return
}
result = v
}
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
inline fun <R> doCall(p: () -> R) {
p()
}
-11
View File
@@ -1,11 +0,0 @@
fun nonlocals(b : Boolean) {
a@{
fun foo() {
if (b) {
return@a 1 // The label must be resolved, but an error should be reported for a non-local return
}
}
return@a 5
}
}
+1
View File
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
fun nonlocals(b : Boolean) {
a@{
fun foo() {
@@ -23,7 +23,7 @@ fun t2() : String {
if (true) {
return@l 1
}
return "s"
<!RETURN_NOT_ALLOWED!>return<!> "s"
}
return "s"
}
@@ -32,10 +32,10 @@ fun t3() : String {
invoker(
l@{
if (true) {
return@t3 "1"
<!RETURN_NOT_ALLOWED!>return@t3<!> "1"
}
else {
return 2
<!RETURN_NOT_ALLOWED!>return<!> 2
}
return@l 0
}
@@ -47,7 +47,7 @@ fun t3() : String {
)
invoker(
{
return "0"
<!RETURN_NOT_ALLOWED!>return<!> "0"
}
)
return "2"
@@ -66,4 +66,4 @@ fun t4() : Int {
}
return 12
}
}
@@ -1,8 +1,8 @@
// !WITH_NEW_INFERENCE
class A {
init {
return
return 1
<!RETURN_NOT_ALLOWED!>return<!>
<!RETURN_NOT_ALLOWED!>return<!> 1
}
constructor() {
if (1 == 1) {
@@ -16,15 +16,15 @@ suspend fun baz() {
}
foo2 {
return@baz
<!RETURN_NOT_ALLOWED!>return@baz<!>
}
foo3 {
return@baz
<!RETURN_NOT_ALLOWED!>return@baz<!>
}
foo4 {
return@baz
<!RETURN_NOT_ALLOWED!>return@baz<!>
}
bar1 {
@@ -32,14 +32,14 @@ suspend fun baz() {
}
bar2 {
return@baz
<!RETURN_NOT_ALLOWED!>return@baz<!>
}
bar3 {
return@baz
<!RETURN_NOT_ALLOWED!>return@baz<!>
}
bar4 {
return@baz
<!RETURN_NOT_ALLOWED!>return@baz<!>
}
}
@@ -1,51 +0,0 @@
// !LANGUAGE: +NewInference +OverloadResolutionByLambdaReturnType
// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE -UNUSED_EXPRESSION -EXPERIMENTAL_API_USAGE -EXPERIMENTAL_UNSIGNED_LITERALS
// ISSUE: KT-11265
// FILE: OverloadResolutionByLambdaReturnType.kt
package kotlin
annotation class OverloadResolutionByLambdaReturnType
// FILE: main.kt
import kotlin.OverloadResolutionByLambdaReturnType
inline fun <T, R> Array<out T>.myFlatMap(transform: (T) -> Iterable<R>): List<R> {
TODO()
}
@OverloadResolutionByLambdaReturnType
@JvmName("seqFlatMap")
inline fun <T, R> Array<out T>.myFlatMap(transform: (T) -> Sequence<R>): List<R> {
TODO()
}
fun String.toList(): List<String> = null!!
fun test_1(a: Array<String>, b: Boolean) {
a.myFlatMap { it.toList().ifEmpty { return } }
a.myFlatMap {
if (b) return
it.toList()
}
}
fun <T, R> Array<out T>.noInlineFlatMap(transform: (T) -> Iterable<R>): List<R> {
TODO()
}
@OverloadResolutionByLambdaReturnType
@JvmName("noInlineSeqFlatMap")
fun <T, R> Array<out T>.noInlineFlatMap(transform: (T) -> Sequence<R>): List<R> {
TODO()
}
fun test_2(a: Array<String>, b: Boolean) {
a.noInlineFlatMap { it.toList().ifEmpty { return } }
a.noInlineFlatMap {
if (b) return
it.toList()
}
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !LANGUAGE: +NewInference +OverloadResolutionByLambdaReturnType
// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE -UNUSED_EXPRESSION -EXPERIMENTAL_API_USAGE -EXPERIMENTAL_UNSIGNED_LITERALS
// ISSUE: KT-11265
@@ -0,0 +1,20 @@
// FIR_IDENTICAL
fun testArray(b: Boolean) {
Array(5) { i ->
if (b) return
i
}
throw AssertionError()
}
fun testMyArray(b: Boolean) {
MyArray(5) { i ->
if (b) <!RETURN_NOT_ALLOWED!>return<!>
i
}
throw AssertionError()
}
class MyArray<T> {
constructor(size: Int, init: (Int) -> T)
}
@@ -0,0 +1,11 @@
package
public fun testArray(/*0*/ b: kotlin.Boolean): kotlin.Unit
public fun testMyArray(/*0*/ b: kotlin.Boolean): kotlin.Unit
public final class MyArray</*0*/ T> {
public constructor MyArray</*0*/ T>(/*0*/ size: kotlin.Int, /*1*/ init: (kotlin.Int) -> T)
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}