KT-22274 report error/warning on incorrect return target label

This commit is contained in:
Dmitry Petrov
2018-07-24 13:41:37 +03:00
parent 5241b37ad9
commit 6fb913a463
15 changed files with 222 additions and 14 deletions
@@ -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(
@@ -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
View File
@@ -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
View File
@@ -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
@@ -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<!>
}
}
@@ -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
}
@@ -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<!>
}
}
@@ -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");
@@ -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
@@ -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?