KT-4405 Control-flow analysis is not performed for some local declarations #KT-4405 Fixed

This commit is contained in:
Svetlana Isakova
2014-01-24 19:27:30 +04:00
parent aa713ef1f6
commit 791fa22abb
20 changed files with 93 additions and 48 deletions
@@ -91,23 +91,29 @@ public class JetFlowInformationProvider {
return pseudocodeVariablesData; return pseudocodeVariablesData;
} }
public void checkFunction( public void checkForLocalClassOrObjectMode() {
@NotNull JetDeclarationWithBody function, // Local classes and objects are analyzed twice: when TopDownAnalyzer processes it and as a part of its container.
@NotNull JetType expectedReturnType, // Almost all checks can be done when the container is analyzed
boolean isLocalObject // except recording initialized variables (this information is needed for DeclarationChecker).
) { recordInitializedVariables();
}
public void checkDeclaration() {
recordInitializedVariables(); recordInitializedVariables();
checkDefiniteReturn(expectedReturnType);
checkLocalFunctions(); checkLocalFunctions();
if (isLocalObject) return;
markUninitializedVariables(); markUninitializedVariables();
markUnusedVariables(); markUnusedVariables();
markUnusedLiteralsInBlock(); markUnusedLiteralsInBlock();
}
public void checkFunction(@Nullable JetType expectedReturnType) {
checkDefiniteReturn(expectedReturnType != null ? expectedReturnType : NO_EXPECTED_TYPE);
markTailCalls(); markTailCalls();
} }
@@ -175,16 +181,17 @@ public class JetFlowInformationProvider {
private void checkLocalFunctions() { private void checkLocalFunctions() {
for (LocalFunctionDeclarationInstruction localDeclarationInstruction : pseudocode.getLocalDeclarations()) { for (LocalFunctionDeclarationInstruction localDeclarationInstruction : pseudocode.getLocalDeclarations()) {
JetElement element = localDeclarationInstruction.getElement(); JetElement element = localDeclarationInstruction.getElement();
if (element instanceof JetNamedFunction) { if (element instanceof JetDeclarationWithBody) {
JetNamedFunction localFunction = (JetNamedFunction) element; JetDeclarationWithBody localDeclaration = (JetDeclarationWithBody) element;
SimpleFunctionDescriptor functionDescriptor = trace.getBindingContext().get(BindingContext.FUNCTION, localFunction); if (localDeclaration instanceof JetFunctionLiteral) continue;
CallableDescriptor functionDescriptor =
(CallableDescriptor) trace.getBindingContext().get(BindingContext.DECLARATION_TO_DESCRIPTOR, localDeclaration);
JetType expectedType = functionDescriptor != null ? functionDescriptor.getReturnType() : null; JetType expectedType = functionDescriptor != null ? functionDescriptor.getReturnType() : null;
JetFlowInformationProvider providerForLocalDeclaration = JetFlowInformationProvider providerForLocalDeclaration =
new JetFlowInformationProvider(localFunction, trace, localDeclarationInstruction.getBody()); new JetFlowInformationProvider(localDeclaration, trace, localDeclarationInstruction.getBody());
providerForLocalDeclaration.checkDefiniteReturn(expectedType != null ? expectedType : NO_EXPECTED_TYPE); providerForLocalDeclaration.checkFunction(expectedType);
providerForLocalDeclaration.markTailCalls();
} }
} }
} }
@@ -17,6 +17,7 @@
package org.jetbrains.jet.lang.resolve; package org.jetbrains.jet.lang.resolve;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.cfg.JetFlowInformationProvider; import org.jetbrains.jet.lang.cfg.JetFlowInformationProvider;
import org.jetbrains.jet.lang.descriptors.PropertyAccessorDescriptor; import org.jetbrains.jet.lang.descriptors.PropertyAccessorDescriptor;
import org.jetbrains.jet.lang.descriptors.PropertyDescriptor; import org.jetbrains.jet.lang.descriptors.PropertyDescriptor;
@@ -59,8 +60,6 @@ public class ControlFlowAnalyzer {
JetType expectedReturnType = !function.hasBlockBody() && !function.hasDeclaredReturnType() JetType expectedReturnType = !function.hasBlockBody() && !function.hasDeclaredReturnType()
? NO_EXPECTED_TYPE ? NO_EXPECTED_TYPE
: functionDescriptor.getReturnType(); : functionDescriptor.getReturnType();
assert expectedReturnType != null
: "functionDescriptor is not yet fully initialized or broken so return type is null " + functionDescriptor;
checkFunction(function, expectedReturnType); checkFunction(function, expectedReturnType);
} }
for (Map.Entry<JetProperty, PropertyDescriptor> entry : bodiesResolveContext.getProperties().entrySet()) { for (Map.Entry<JetProperty, PropertyDescriptor> entry : bodiesResolveContext.getProperties().entrySet()) {
@@ -75,11 +74,11 @@ public class ControlFlowAnalyzer {
// A pseudocode of class/object initialization corresponds to a class/object // A pseudocode of class/object initialization corresponds to a class/object
// or initialization of properties corresponds to a package declared in a file // or initialization of properties corresponds to a package declared in a file
JetFlowInformationProvider flowInformationProvider = new JetFlowInformationProvider((JetElement) declarationContainer, trace); JetFlowInformationProvider flowInformationProvider = new JetFlowInformationProvider((JetElement) declarationContainer, trace);
flowInformationProvider.recordInitializedVariables(); if (topDownAnalysisParameters.isDeclaredLocally()) {
flowInformationProvider.checkForLocalClassOrObjectMode();
if (topDownAnalysisParameters.isDeclaredLocally()) return; return;
}
flowInformationProvider.markUninitializedVariables(); flowInformationProvider.checkDeclaration();
} }
private void checkProperty(JetProperty property, PropertyDescriptor propertyDescriptor) { private void checkProperty(JetProperty property, PropertyDescriptor propertyDescriptor) {
@@ -89,15 +88,19 @@ public class ControlFlowAnalyzer {
: propertyDescriptor.getSetter(); : propertyDescriptor.getSetter();
assert accessorDescriptor != null : "no property accessor descriptor " + accessor.getText(); assert accessorDescriptor != null : "no property accessor descriptor " + accessor.getText();
JetType returnType = accessorDescriptor.getReturnType(); JetType returnType = accessorDescriptor.getReturnType();
assert returnType != null : "property accessor has no return type " + accessorDescriptor;
checkFunction(accessor, returnType); checkFunction(accessor, returnType);
} }
} }
private void checkFunction(@NotNull JetDeclarationWithBody function, @NotNull JetType expectedReturnType) { private void checkFunction(@NotNull JetDeclarationWithBody function, @Nullable JetType expectedReturnType) {
JetExpression bodyExpression = function.getBodyExpression(); JetExpression bodyExpression = function.getBodyExpression();
if (bodyExpression == null) return; if (bodyExpression == null) return;
JetFlowInformationProvider flowInformationProvider = new JetFlowInformationProvider(function, trace); JetFlowInformationProvider flowInformationProvider = new JetFlowInformationProvider(function, trace);
flowInformationProvider.checkFunction(function, expectedReturnType, topDownAnalysisParameters.isDeclaredLocally()); if (topDownAnalysisParameters.isDeclaredLocally()) {
flowInformationProvider.checkForLocalClassOrObjectMode();
return;
}
flowInformationProvider.checkDeclaration();
flowInformationProvider.checkFunction(expectedReturnType);
} }
} }
@@ -15,15 +15,15 @@ class WithC() {
{ {
$x = 1 $x = 1
<!UNRESOLVED_REFERENCE!>$y<!> = 2 <!UNRESOLVED_REFERENCE!>$y<!> = 2
val b = x val <!UNUSED_VARIABLE!>b<!> = x
} }
val a : Int get() = 1 val a : Int get() = 1
{ {
val z = <!UNRESOLVED_REFERENCE!>b<!> val <!UNUSED_VARIABLE!>z<!> = <!UNRESOLVED_REFERENCE!>b<!>
val zz = x val <!UNUSED_VARIABLE!>zz<!> = x
val zzz = <!NO_BACKING_FIELD_CUSTOM_ACCESSORS!>$a<!> val <!UNUSED_VARIABLE!>zzz<!> = <!NO_BACKING_FIELD_CUSTOM_ACCESSORS!>$a<!>
} }
} }
@@ -199,7 +199,7 @@ class LocalValsVsProperties(val a: Int, w: Int) : Open(a, w) {
<!MUST_BE_INITIALIZED_OR_BE_ABSTRACT!>val y : Int<!> <!MUST_BE_INITIALIZED_OR_BE_ABSTRACT!>val y : Int<!>
{ {
$x = 1 $x = 1
val b = x val <!UNUSED_VARIABLE!>b<!> = x
} }
val b = a val b = a
@@ -1,6 +1,6 @@
class ReadForward() { class ReadForward() {
{ {
val x = <!UNINITIALIZED_VARIABLE!>$a<!> val <!UNUSED_VARIABLE!>x<!> = <!UNINITIALIZED_VARIABLE!>$a<!>
} }
val a = 1 val a = 1
@@ -1,6 +1,6 @@
class ReadByAnotherPropertyInitializer() { class ReadByAnotherPropertyInitializer() {
val a = 1 val a = 1
{ {
val x = $a val <!UNUSED_VARIABLE!>x<!> = $a
} }
} }
@@ -2,6 +2,6 @@ abstract class ReadNonexistent() {
abstract val aa: Int abstract val aa: Int
{ {
val x = <!NO_BACKING_FIELD_ABSTRACT_PROPERTY!>$aa<!> val <!UNUSED_VARIABLE!>x<!> = <!NO_BACKING_FIELD_ABSTRACT_PROPERTY!>$aa<!>
} }
} }
@@ -3,6 +3,6 @@ class ReadNonexistent() {
get() = 1 get() = 1
{ {
val x = <!NO_BACKING_FIELD_CUSTOM_ACCESSORS!>$a<!> val <!UNUSED_VARIABLE!>x<!> = <!NO_BACKING_FIELD_CUSTOM_ACCESSORS!>$a<!>
} }
} }
@@ -1,5 +1,5 @@
class Cl() { class Cl() {
{ {
val x = <!UNRESOLVED_REFERENCE!>$a<!> val <!UNUSED_VARIABLE!>x<!> = <!UNRESOLVED_REFERENCE!>$a<!>
} }
} }
@@ -0,0 +1,30 @@
//KT-4405 Control-flow analysis is not performed for some local declarations
package d
val closure = {
val <!UNUSED_VARIABLE!>x4<!> = "" // error: should be UNUSED_VARIABLE
fun g() {
val <!UNUSED_VARIABLE!>x6<!> = "" // error: should be UNUSED_VARIABLE
}
fun h(): Int { // error: should be NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY
<!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>}<!>
}
class A {
{
fun foo(): Int {
<!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>}<!>
val <!UNUSED_VARIABLE!>closure<!> = {
val <!UNUSED_VARIABLE!>x<!> = ""
fun local(): Int {
<!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>}<!>
}
val <!UNUSED_VARIABLE!>y<!> = ""
}
}
@@ -15,7 +15,7 @@ fun f() {
fun loc2(): Int { fun loc2(): Int {
val <!UNUSED_VARIABLE!>x2_<!> = "" // error: should be UNUSED_VARIABLE val <!UNUSED_VARIABLE!>x2_<!> = "" // error: should be UNUSED_VARIABLE
<!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY, NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>}<!> <!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>}<!>
} }
val v: String val v: String
@@ -2,20 +2,20 @@ package f
object A { object A {
class LoginFormPage() : Request({ class LoginFormPage() : Request({
val failed = session.get("LOGIN_FAILED") val <!UNUSED_VARIABLE!>failed<!> = session.get("LOGIN_FAILED")
}) })
} }
class B { class B {
class object { class object {
class LoginFormPage() : Request({ class LoginFormPage() : Request({
val failed = session.get("LOGIN_FAILED") val <!UNUSED_VARIABLE!>failed<!> = session.get("LOGIN_FAILED")
}) })
} }
class C { class C {
class LoginFormPage() : Request({ class LoginFormPage() : Request({
val failed = session.get("LOGIN_FAILED") val <!UNUSED_VARIABLE!>failed<!> = session.get("LOGIN_FAILED")
}) })
} }
} }
@@ -23,7 +23,7 @@ fun f() {
val closure = { val closure = {
class MyClass { class MyClass {
{ {
val x: MyClass = MyClass() val <!UNUSED_VARIABLE!>x<!>: MyClass = MyClass()
} }
} }
} }
@@ -16,7 +16,7 @@ class A(a: String?) {
val b = if (a != null) <!DEBUG_INFO_AUTOCAST!>a<!>.length else 1 val b = if (a != null) <!DEBUG_INFO_AUTOCAST!>a<!>.length else 1
{ {
if (a != null) { if (a != null) {
val c = <!DEBUG_INFO_AUTOCAST!>a<!>.length val <!UNUSED_VARIABLE!>c<!> = <!DEBUG_INFO_AUTOCAST!>a<!>.length
} }
} }
@@ -4,6 +4,6 @@ import javax.swing.JFrame
class KFrame() : JFrame() { class KFrame() : JFrame() {
{ {
val x = this.rootPaneCheckingEnabled // make sure field is visible val <!UNUSED_VARIABLE!>x<!> = this.rootPaneCheckingEnabled // make sure field is visible
} }
} }
@@ -1,5 +1,5 @@
class A(vararg t : Int) { class A(vararg t : Int) {
{ {
val t1 : IntArray = t val <!UNUSED_VARIABLE!>t1<!> : IntArray = t
} }
} }
@@ -1,5 +1,5 @@
class A(vararg val t : Int) { class A(vararg val t : Int) {
{ {
val t1 : IntArray = t val <!UNUSED_VARIABLE!>t1<!> : IntArray = t
} }
} }
@@ -1618,6 +1618,11 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
doTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/kt3501.kt"); doTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/kt3501.kt");
} }
@TestMetadata("kt4405.kt")
public void testKt4405() throws Exception {
doTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/kt4405.kt");
}
@TestMetadata("kt510.kt") @TestMetadata("kt510.kt")
public void testKt510() throws Exception { public void testKt510() throws Exception {
doTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/kt510.kt"); doTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/kt510.kt");
@@ -15,16 +15,16 @@ class WithC() {
{ {
$x = 1 $x = 1
<error>$y</error> = 2 <error>$y</error> = 2
val b = x val <warning>b</warning> = x
} }
val a : Int get() = 1 val a : Int get() = 1
{ {
val z = <error>b</error> val <warning>z</warning> = <error>b</error>
val zz = x val <warning>zz</warning> = x
val zzz = <error>$a</error> val <warning>zzz</warning> = <error>$a</error>
} }
} }
@@ -11,7 +11,7 @@
bar = 1 bar = 1
this.bar this.bar
1 : Int 1 : Int
val a : Int =1 val <warning>a</warning> : Int =1
this : Foo this : Foo
} }
} }