KT-22274 report error/warning on incorrect return target label
This commit is contained in:
@@ -70,7 +70,7 @@ class ControlFlowInformationProvider private constructor(
|
||||
) : this(
|
||||
declaration,
|
||||
trace,
|
||||
ControlFlowProcessor(trace).generatePseudocode(declaration),
|
||||
ControlFlowProcessor(trace, languageVersionSettings).generatePseudocode(declaration),
|
||||
languageVersionSettings,
|
||||
diagnosticSuppressor
|
||||
)
|
||||
|
||||
@@ -30,11 +30,14 @@ import org.jetbrains.kotlin.cfg.pseudocode.PseudocodeImpl
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.AccessTarget
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.InstructionWithValue
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.MagicKind
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.contracts.description.InvocationKind
|
||||
import org.jetbrains.kotlin.contracts.description.canBeRevisited
|
||||
import org.jetbrains.kotlin.contracts.description.isDefinitelyVisited
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.diagnostics.Errors.*
|
||||
import org.jetbrains.kotlin.lexer.KtToken
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
@@ -63,7 +66,10 @@ import java.util.*
|
||||
|
||||
typealias DeferredGenerator = (ControlFlowBuilder) -> Unit
|
||||
|
||||
class ControlFlowProcessor(private val trace: BindingTrace) {
|
||||
class ControlFlowProcessor(
|
||||
private val trace: BindingTrace,
|
||||
private val languageVersionSettings: LanguageVersionSettings?
|
||||
) {
|
||||
|
||||
private val builder: ControlFlowBuilder = ControlFlowInstructionsGenerator()
|
||||
|
||||
@@ -927,12 +933,10 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
|
||||
val subroutine: KtElement?
|
||||
val labelName = expression.getLabelName()
|
||||
subroutine = if (labelElement != null && labelName != null) {
|
||||
val labeledElement = trace.get(BindingContext.LABEL_TARGET, labelElement)
|
||||
if (labeledElement != null) {
|
||||
assert(labeledElement is KtElement)
|
||||
labeledElement as KtElement?
|
||||
} else {
|
||||
null
|
||||
trace.get(BindingContext.LABEL_TARGET, labelElement)?.let { labeledElement ->
|
||||
val labeledKtElement = labeledElement as KtElement
|
||||
checkReturnLabelTarget(expression, labeledKtElement)
|
||||
labeledKtElement
|
||||
}
|
||||
} else {
|
||||
builder.returnSubroutine
|
||||
@@ -951,6 +955,17 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkReturnLabelTarget(returnExpression: KtReturnExpression, labeledElement: KtElement) {
|
||||
if (languageVersionSettings == null) return
|
||||
if (labeledElement !is KtFunctionLiteral && labeledElement !is KtNamedFunction) {
|
||||
if (languageVersionSettings.supportsFeature(LanguageFeature.RestrictReturnStatementTarget)) {
|
||||
trace.report(Errors.NOT_A_FUNCTION_LABEL.on(returnExpression))
|
||||
} else {
|
||||
trace.report(Errors.NOT_A_FUNCTION_LABEL_WARNING.on(returnExpression))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitParameter(parameter: KtParameter) {
|
||||
builder.declareParameter(parameter)
|
||||
val defaultValue = parameter.defaultValue
|
||||
|
||||
@@ -89,7 +89,7 @@ public class PseudocodeUtil {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
return new ControlFlowProcessor(mockTrace).generatePseudocode(declaration);
|
||||
return new ControlFlowProcessor(mockTrace, null).generatePseudocode(declaration);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -819,6 +819,9 @@ public interface Errors {
|
||||
DiagnosticFactory0<KtExpressionWithLabel> BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory1<KtExpressionWithLabel, String> NOT_A_LOOP_LABEL = DiagnosticFactory1.create(ERROR);
|
||||
|
||||
DiagnosticFactory0<KtExpressionWithLabel> NOT_A_FUNCTION_LABEL = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<KtExpressionWithLabel> NOT_A_FUNCTION_LABEL_WARNING = DiagnosticFactory0.create(WARNING);
|
||||
|
||||
// Control flow / Data flow
|
||||
|
||||
DiagnosticFactory1<KtElement, List<TextRange>> UNREACHABLE_CODE = DiagnosticFactory1.create(
|
||||
|
||||
+3
@@ -599,6 +599,9 @@ public class DefaultErrorMessages {
|
||||
MAP.put(BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY, "'break' or 'continue' jumps across a function or a class boundary");
|
||||
MAP.put(NOT_A_LOOP_LABEL, "The label ''{0}'' does not denote a loop", STRING);
|
||||
|
||||
MAP.put(NOT_A_FUNCTION_LABEL, "Target label does not denote a function");
|
||||
MAP.put(NOT_A_FUNCTION_LABEL_WARNING, "Target label does not denote a function");
|
||||
|
||||
MAP.put(ANONYMOUS_INITIALIZER_IN_INTERFACE, "Anonymous initializers are not allowed in interfaces");
|
||||
MAP.put(NULLABLE_SUPERTYPE, "A supertype cannot be nullable");
|
||||
MAP.put(DYNAMIC_SUPERTYPE, "A supertype cannot be dynamic");
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
error: unknown API version: 239.42
|
||||
Supported API versions: 1.0, 1.1, 1.2, 1.3 (EXPERIMENTAL)
|
||||
Supported API versions: 1.0, 1.1, 1.2, 1.3 (EXPERIMENTAL), 1.4 (EXPERIMENTAL)
|
||||
COMPILATION_ERROR
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
error: unknown language version: 239.42
|
||||
Supported language versions: 1.0, 1.1, 1.2, 1.3 (EXPERIMENTAL)
|
||||
Supported language versions: 1.0, 1.1, 1.2, 1.3 (EXPERIMENTAL), 1.4 (EXPERIMENTAL)
|
||||
COMPILATION_ERROR
|
||||
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
// !LANGUAGE: +RestrictReturnStatementTarget
|
||||
|
||||
@Target(AnnotationTarget.EXPRESSION)
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
annotation class Ann
|
||||
|
||||
fun testFunctionName() {
|
||||
return@testFunctionName
|
||||
}
|
||||
|
||||
fun testHighOrderFunctionName() {
|
||||
run {
|
||||
return@run
|
||||
}
|
||||
}
|
||||
|
||||
fun testLambdaLabel() =
|
||||
lambda@ {
|
||||
return@lambda
|
||||
}
|
||||
|
||||
fun testParenthesizedLambdaLabel() =
|
||||
lambda@ ( {
|
||||
return@lambda
|
||||
} )
|
||||
|
||||
fun testAnnotatedLambdaLabel() =
|
||||
lambda@ @Ann {
|
||||
return@lambda
|
||||
}
|
||||
|
||||
fun testLambdaMultipleLabels1() =
|
||||
lambda1@ lambda2@ {
|
||||
return@lambda1
|
||||
}
|
||||
|
||||
fun testLambdaMultipleLabels2() =
|
||||
lambda1@ lambda2@ {
|
||||
return@lambda2
|
||||
}
|
||||
|
||||
fun testAnonymousFunctionLabel() =
|
||||
anonFun@ fun() {
|
||||
return@anonFun
|
||||
}
|
||||
|
||||
fun testLoopLabelInReturn(xs: List<Int>) {
|
||||
L@ for (x in xs) {
|
||||
if (x > 0) <!NOT_A_FUNCTION_LABEL!>return@L<!>
|
||||
}
|
||||
}
|
||||
|
||||
fun testValLabelInReturn() {
|
||||
L@ val fn = { <!NOT_A_FUNCTION_LABEL!>return@L<!> }
|
||||
fn()
|
||||
}
|
||||
|
||||
fun testHighOrderFunctionCallLabelInReturn() {
|
||||
L@ run {
|
||||
<!NOT_A_FUNCTION_LABEL!>return@L<!>
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package
|
||||
|
||||
public fun testAnnotatedLambdaLabel(): () -> kotlin.Unit
|
||||
public fun testAnonymousFunctionLabel(): () -> kotlin.Unit
|
||||
public fun testFunctionName(): kotlin.Unit
|
||||
public fun testHighOrderFunctionCallLabelInReturn(): kotlin.Unit
|
||||
public fun testHighOrderFunctionName(): kotlin.Unit
|
||||
public fun testLambdaLabel(): () -> kotlin.Unit
|
||||
public fun testLambdaMultipleLabels1(): () -> kotlin.Unit
|
||||
public fun testLambdaMultipleLabels2(): () -> kotlin.Unit
|
||||
public fun testLoopLabelInReturn(/*0*/ xs: kotlin.collections.List<kotlin.Int>): kotlin.Unit
|
||||
public fun testParenthesizedLambdaLabel(): () -> kotlin.Unit
|
||||
public fun testValLabelInReturn(): kotlin.Unit
|
||||
|
||||
@kotlin.annotation.Target(allowedTargets = {AnnotationTarget.EXPRESSION}) @kotlin.annotation.Retention(value = AnnotationRetention.SOURCE) public final annotation class Ann : kotlin.Annotation {
|
||||
public constructor Ann()
|
||||
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
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
// !LANGUAGE: -RestrictReturnStatementTarget
|
||||
|
||||
@Target(AnnotationTarget.EXPRESSION)
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
annotation class Ann
|
||||
|
||||
fun testFunctionName() {
|
||||
return@testFunctionName
|
||||
}
|
||||
|
||||
fun testHighOrderFunctionName() {
|
||||
run {
|
||||
return@run
|
||||
}
|
||||
}
|
||||
|
||||
fun testLambdaLabel() =
|
||||
lambda@ {
|
||||
return@lambda
|
||||
}
|
||||
|
||||
fun testParenthesizedLambdaLabel() =
|
||||
lambda@ ( {
|
||||
return@lambda
|
||||
} )
|
||||
|
||||
fun testAnnotatedLambdaLabel() =
|
||||
lambda@ @Ann {
|
||||
return@lambda
|
||||
}
|
||||
|
||||
fun testLambdaMultipleLabels1() =
|
||||
lambda1@ lambda2@ {
|
||||
return@lambda1
|
||||
}
|
||||
|
||||
fun testLambdaMultipleLabels2() =
|
||||
lambda1@ lambda2@ {
|
||||
return@lambda2
|
||||
}
|
||||
|
||||
fun testAnonymousFunctionLabel() =
|
||||
anonFun@ fun() {
|
||||
return@anonFun
|
||||
}
|
||||
|
||||
fun testLoopLabelInReturn(xs: List<Int>) {
|
||||
L@ for (x in xs) {
|
||||
if (x > 0) <!NOT_A_FUNCTION_LABEL_WARNING!>return@L<!>
|
||||
}
|
||||
}
|
||||
|
||||
fun testValLabelInReturn() {
|
||||
L@ val fn = { <!NOT_A_FUNCTION_LABEL_WARNING!>return@L<!> }
|
||||
fn()
|
||||
}
|
||||
|
||||
fun testHighOrderFunctionCallLabelInReturn() {
|
||||
L@ run {
|
||||
<!NOT_A_FUNCTION_LABEL_WARNING!>return@L<!>
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package
|
||||
|
||||
public fun testAnnotatedLambdaLabel(): () -> kotlin.Unit
|
||||
public fun testAnonymousFunctionLabel(): () -> kotlin.Unit
|
||||
public fun testFunctionName(): kotlin.Unit
|
||||
public fun testHighOrderFunctionCallLabelInReturn(): kotlin.Unit
|
||||
public fun testHighOrderFunctionName(): kotlin.Unit
|
||||
public fun testLambdaLabel(): () -> kotlin.Unit
|
||||
public fun testLambdaMultipleLabels1(): () -> kotlin.Unit
|
||||
public fun testLambdaMultipleLabels2(): () -> kotlin.Unit
|
||||
public fun testLoopLabelInReturn(/*0*/ xs: kotlin.collections.List<kotlin.Int>): kotlin.Unit
|
||||
public fun testParenthesizedLambdaLabel(): () -> kotlin.Unit
|
||||
public fun testValLabelInReturn(): kotlin.Unit
|
||||
|
||||
@kotlin.annotation.Target(allowedTargets = {AnnotationTarget.EXPRESSION}) @kotlin.annotation.Retention(value = AnnotationRetention.SOURCE) public final annotation class Ann : kotlin.Annotation {
|
||||
public constructor Ann()
|
||||
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
|
||||
}
|
||||
@@ -4130,6 +4130,16 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
||||
runTest("compiler/testData/diagnostics/tests/controlStructures/localReturnInsidePropertyAccessor.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("notAFunctionLabel_after.kt")
|
||||
public void testNotAFunctionLabel_after() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/controlStructures/notAFunctionLabel_after.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("notAFunctionLabel_before.kt")
|
||||
public void testNotAFunctionLabel_before() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/controlStructures/notAFunctionLabel_before.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("specialConstructsAndPlatformTypes.kt")
|
||||
public void testSpecialConstructsAndPlatformTypes() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/controlStructures/specialConstructsAndPlatformTypes.kt");
|
||||
|
||||
Generated
+10
@@ -4130,6 +4130,16 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing
|
||||
runTest("compiler/testData/diagnostics/tests/controlStructures/localReturnInsidePropertyAccessor.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("notAFunctionLabel_after.kt")
|
||||
public void testNotAFunctionLabel_after() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/controlStructures/notAFunctionLabel_after.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("notAFunctionLabel_before.kt")
|
||||
public void testNotAFunctionLabel_before() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/controlStructures/notAFunctionLabel_before.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("specialConstructsAndPlatformTypes.kt")
|
||||
public void testSpecialConstructsAndPlatformTypes() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/controlStructures/specialConstructsAndPlatformTypes.kt");
|
||||
|
||||
@@ -83,6 +83,8 @@ enum class LanguageFeature(
|
||||
RestrictRetentionForExpressionAnnotations(KOTLIN_1_3, kind = BUG_FIX),
|
||||
NoConstantValueAttributeForNonConstVals(KOTLIN_1_3, kind = BUG_FIX),
|
||||
|
||||
RestrictReturnStatementTarget(KOTLIN_1_4, kind = BUG_FIX),
|
||||
|
||||
StrictJavaNullabilityAssertions(sinceVersion = null, defaultState = State.DISABLED),
|
||||
ProperIeee754Comparisons(sinceVersion = null, defaultState = State.DISABLED, kind = BUG_FIX),
|
||||
|
||||
@@ -185,7 +187,8 @@ enum class LanguageVersion(val major: Int, val minor: Int) : DescriptionAware {
|
||||
KOTLIN_1_0(1, 0),
|
||||
KOTLIN_1_1(1, 1),
|
||||
KOTLIN_1_2(1, 2),
|
||||
KOTLIN_1_3(1, 3);
|
||||
KOTLIN_1_3(1, 3),
|
||||
KOTLIN_1_4(1, 4);
|
||||
|
||||
val isStable: Boolean
|
||||
get() = this <= LATEST_STABLE
|
||||
|
||||
+2
-2
@@ -6,14 +6,14 @@ interface KotlinCommonOptions : org.jetbrains.kotlin.gradle.dsl.KotlinCommonToo
|
||||
|
||||
/**
|
||||
* Allow to use declarations only from the specified version of bundled libraries
|
||||
* Possible values: "1.0", "1.1", "1.2", "1.3 (EXPERIMENTAL)"
|
||||
* Possible values: "1.0", "1.1", "1.2", "1.3 (EXPERIMENTAL)", "1.4 (EXPERIMENTAL)"
|
||||
* Default value: null
|
||||
*/
|
||||
var apiVersion: kotlin.String?
|
||||
|
||||
/**
|
||||
* Provide source compatibility with specified language version
|
||||
* Possible values: "1.0", "1.1", "1.2", "1.3 (EXPERIMENTAL)"
|
||||
* Possible values: "1.0", "1.1", "1.2", "1.3 (EXPERIMENTAL)", "1.4 (EXPERIMENTAL)"
|
||||
* Default value: null
|
||||
*/
|
||||
var languageVersion: kotlin.String?
|
||||
|
||||
Reference in New Issue
Block a user