check definite return in local functions
This commit is contained in:
@@ -49,6 +49,7 @@ import static org.jetbrains.jet.lang.cfg.PseudocodeTraverser.TraversalOrder.FORW
|
||||
import static org.jetbrains.jet.lang.cfg.PseudocodeVariablesData.VariableUseState.*;
|
||||
import static org.jetbrains.jet.lang.diagnostics.Errors.*;
|
||||
import static org.jetbrains.jet.lang.resolve.BindingContext.CAPTURED_IN_CLOSURE;
|
||||
import static org.jetbrains.jet.lang.types.TypeUtils.NO_EXPECTED_TYPE;
|
||||
import static org.jetbrains.jet.lang.types.TypeUtils.noExpectedType;
|
||||
|
||||
public class JetFlowInformationProvider {
|
||||
@@ -58,13 +59,21 @@ public class JetFlowInformationProvider {
|
||||
private final BindingTrace trace;
|
||||
private PseudocodeVariablesData pseudocodeVariablesData;
|
||||
|
||||
public JetFlowInformationProvider(
|
||||
private JetFlowInformationProvider(
|
||||
@NotNull JetElement declaration,
|
||||
@NotNull BindingTrace trace) {
|
||||
|
||||
@NotNull BindingTrace trace,
|
||||
@NotNull Pseudocode pseudocode
|
||||
) {
|
||||
subroutine = declaration;
|
||||
this.trace = trace;
|
||||
pseudocode = new JetControlFlowProcessor(trace).generatePseudocode(declaration);
|
||||
this.pseudocode = pseudocode;
|
||||
}
|
||||
|
||||
public JetFlowInformationProvider(
|
||||
@NotNull JetElement declaration,
|
||||
@NotNull BindingTrace trace
|
||||
) {
|
||||
this(declaration, trace, new JetControlFlowProcessor(trace).generatePseudocode(declaration));
|
||||
}
|
||||
|
||||
public PseudocodeVariablesData getPseudocodeVariablesData() {
|
||||
@@ -84,9 +93,10 @@ public class JetFlowInformationProvider {
|
||||
recordInitializedVariables();
|
||||
}
|
||||
|
||||
if (isLocalObject) return;
|
||||
|
||||
checkDefiniteReturn(expectedReturnType);
|
||||
checkDefiniteReturnInLocalFunctions();
|
||||
|
||||
if (isLocalObject) return;
|
||||
|
||||
if (!isPropertyAccessor) {
|
||||
// Property accessor is checked through initialization of a class/object or package properties (at 'checkDeclarationContainer')
|
||||
@@ -158,6 +168,21 @@ public class JetFlowInformationProvider {
|
||||
}
|
||||
}
|
||||
|
||||
private void checkDefiniteReturnInLocalFunctions() {
|
||||
for (LocalFunctionDeclarationInstruction localDeclarationInstruction : pseudocode.getLocalDeclarations()) {
|
||||
JetElement element = localDeclarationInstruction.getElement();
|
||||
if (element instanceof JetNamedFunction) {
|
||||
JetNamedFunction localFunction = (JetNamedFunction) element;
|
||||
SimpleFunctionDescriptor functionDescriptor = trace.getBindingContext().get(BindingContext.FUNCTION, localFunction);
|
||||
JetType expectedType = functionDescriptor != null ? functionDescriptor.getReturnType() : null;
|
||||
|
||||
JetFlowInformationProvider providerForLocalDeclaration =
|
||||
new JetFlowInformationProvider(localFunction, trace, localDeclarationInstruction.getBody());
|
||||
providerForLocalDeclaration.checkDefiniteReturn(expectedType != null ? expectedType : NO_EXPECTED_TYPE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void checkDefiniteReturn(final @NotNull JetType expectedReturnType) {
|
||||
assert subroutine instanceof JetDeclarationWithBody;
|
||||
JetDeclarationWithBody function = (JetDeclarationWithBody) subroutine;
|
||||
|
||||
@@ -12,7 +12,7 @@ fun Array<String>.test1Nested(): Array<String> {
|
||||
fun Array<String>.test2() : Array<String> {
|
||||
class Z2() {
|
||||
fun run(): Array<String> {
|
||||
this@test2
|
||||
return this@test2
|
||||
}
|
||||
}
|
||||
return Z2().run()
|
||||
@@ -34,7 +34,7 @@ fun Array<String>.test2Nested() : Array<String> {
|
||||
|
||||
fun Array<String>.test3(): Array<String> {
|
||||
fun local(): Array<String> {
|
||||
this@test3
|
||||
return this@test3
|
||||
}
|
||||
return local()
|
||||
}
|
||||
@@ -42,7 +42,7 @@ fun Array<String>.test3(): Array<String> {
|
||||
fun Array<String>.test3Nested(): Array<String> {
|
||||
fun local(): Array<String> {
|
||||
fun local2(): Array<String> {
|
||||
this@test3Nested
|
||||
return this@test3Nested
|
||||
}
|
||||
return local2()
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ fun<TItem> Iterable<TItem>.where(predicate : (TItem)->Boolean) : Iterable<TItem>
|
||||
if (predicate(next))
|
||||
return next
|
||||
}
|
||||
null
|
||||
return null
|
||||
}
|
||||
{ yielder() }
|
||||
}
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
trait X {
|
||||
fun f(): Boolean
|
||||
}
|
||||
|
||||
val m = object : X {
|
||||
override fun f(): Boolean {
|
||||
<!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>}<!>
|
||||
|
||||
fun foo() {
|
||||
fun local(): Int {
|
||||
<!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>}<!>
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package f
|
||||
|
||||
|
||||
//KT-3444 Front-end doesn't check if local function or function of anonymous class returns value
|
||||
|
||||
fun box(): Int {
|
||||
|
||||
fun local(): Int {
|
||||
<!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>}<!>
|
||||
|
||||
return local()
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
box()
|
||||
}
|
||||
|
||||
@@ -1392,6 +1392,7 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis")
|
||||
@InnerTestClasses({ControlFlowAnalysis.DefiniteReturn.class})
|
||||
public static class ControlFlowAnalysis extends AbstractDiagnosticsTestWithEagerResolve {
|
||||
public void testAllFilesPresentInControlFlowAnalysis() throws Exception {
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/diagnostics/tests/controlFlowAnalysis"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
@@ -1572,6 +1573,35 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
|
||||
doTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/unreachableCode.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/definiteReturn")
|
||||
public static class DefiniteReturn extends AbstractDiagnosticsTestWithEagerResolve {
|
||||
public void testAllFilesPresentInDefiniteReturn() throws Exception {
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/diagnostics/tests/controlFlowAnalysis/definiteReturn"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("kt3444_ReturnFromLocalFunctions.kt")
|
||||
public void testKt3444_ReturnFromLocalFunctions() throws Exception {
|
||||
doTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/definiteReturn/kt3444_ReturnFromLocalFunctions.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("kt4034.kt")
|
||||
public void testKt4034() throws Exception {
|
||||
doTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/definiteReturn/kt4034.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ReturnFromFunctionInObject.kt")
|
||||
public void testReturnFromFunctionInObject() throws Exception {
|
||||
doTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/definiteReturn/ReturnFromFunctionInObject.kt");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static Test innerSuite() {
|
||||
TestSuite suite = new TestSuite("ControlFlowAnalysis");
|
||||
suite.addTestSuite(ControlFlowAnalysis.class);
|
||||
suite.addTestSuite(DefiniteReturn.class);
|
||||
return suite;
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/diagnostics/tests/controlStructures")
|
||||
@@ -6619,7 +6649,7 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
|
||||
suite.addTest(Cast.innerSuite());
|
||||
suite.addTestSuite(CheckArguments.class);
|
||||
suite.addTestSuite(ClassObjects.class);
|
||||
suite.addTestSuite(ControlFlowAnalysis.class);
|
||||
suite.addTest(ControlFlowAnalysis.innerSuite());
|
||||
suite.addTestSuite(ControlStructures.class);
|
||||
suite.addTestSuite(DataClasses.class);
|
||||
suite.addTest(DataFlow.innerSuite());
|
||||
|
||||
Reference in New Issue
Block a user