KT-1002 If there's no return in function highlight only last token but not last statement

This commit is contained in:
Svetlana Isakova
2012-03-09 12:20:10 +04:00
parent 1054a4da9a
commit c857785910
11 changed files with 93 additions and 67 deletions
@@ -149,7 +149,7 @@ public class JetFlowInformationProvider {
} }
} }
public void checkDefiniteReturn(@NotNull JetDeclarationWithBody function, final @NotNull JetType expectedReturnType) { public void checkDefiniteReturn(@NotNull final JetDeclarationWithBody function, final @NotNull JetType expectedReturnType) {
assert function instanceof JetDeclaration; assert function instanceof JetDeclaration;
JetExpression bodyExpression = function.getBodyExpression(); JetExpression bodyExpression = function.getBodyExpression();
@@ -172,6 +172,7 @@ public class JetFlowInformationProvider {
trace.report(UNREACHABLE_CODE.on(element)); trace.report(UNREACHABLE_CODE.on(element));
} }
final boolean[] noReturnError = new boolean[] { false };
for (JetElement returnedExpression : returnedExpressions) { for (JetElement returnedExpression : returnedExpressions) {
returnedExpression.accept(new JetVisitorVoid() { returnedExpression.accept(new JetVisitorVoid() {
@Override @Override
@@ -184,11 +185,14 @@ public class JetFlowInformationProvider {
@Override @Override
public void visitExpression(JetExpression expression) { public void visitExpression(JetExpression expression) {
if (blockBody && expectedReturnType != NO_EXPECTED_TYPE && !JetStandardClasses.isUnit(expectedReturnType) && !rootUnreachableElements.contains(expression)) { if (blockBody && expectedReturnType != NO_EXPECTED_TYPE && !JetStandardClasses.isUnit(expectedReturnType) && !rootUnreachableElements.contains(expression)) {
trace.report(NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY.on(expression)); noReturnError[0] = true;
} }
} }
}); });
} }
if (noReturnError[0]) {
trace.report(NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY.on(function));
}
} }
private Set<JetElement> collectUnreachableCode(@NotNull JetElement subroutine) { private Set<JetElement> collectUnreachableCode(@NotNull JetElement subroutine) {
@@ -232,7 +232,18 @@ public interface Errors {
DiagnosticFactory1<JetExpression, JetType> CALLEE_NOT_A_FUNCTION = DiagnosticFactory1.create(ERROR, "Expecting a function type, but found {0}"); DiagnosticFactory1<JetExpression, JetType> CALLEE_NOT_A_FUNCTION = DiagnosticFactory1.create(ERROR, "Expecting a function type, but found {0}");
DiagnosticFactory<JetReturnExpression> RETURN_IN_FUNCTION_WITH_EXPRESSION_BODY = DiagnosticFactory.create(ERROR, "Returns are not allowed for functions with expression body. Use block body in '{...}'"); DiagnosticFactory<JetReturnExpression> RETURN_IN_FUNCTION_WITH_EXPRESSION_BODY = DiagnosticFactory.create(ERROR, "Returns are not allowed for functions with expression body. Use block body in '{...}'");
DiagnosticFactory<JetExpression> NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY = DiagnosticFactory.create(ERROR, "A 'return' expression required in a function with a block body ('{...}')"); DiagnosticFactory<JetDeclarationWithBody> NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY = DiagnosticFactory.create(ERROR, "A 'return' expression required in a function with a block body ('{...}')", new PositioningStrategy<JetDeclarationWithBody>() {
@NotNull
@Override
public List<TextRange> mark(@NotNull JetDeclarationWithBody element) {
JetExpression bodyExpression = element.getBodyExpression();
if (!(bodyExpression instanceof JetBlockExpression)) return Collections.emptyList();
JetBlockExpression blockExpression = (JetBlockExpression) bodyExpression;
TextRange lastBracketRange = blockExpression.getLastBracketRange();
if (lastBracketRange == null) return Collections.emptyList();
return markRange(lastBracketRange);
}
});
DiagnosticFactory1<JetExpression, JetType> RETURN_TYPE_MISMATCH = DiagnosticFactory1.create(ERROR, "This function must return a value of type {0}"); DiagnosticFactory1<JetExpression, JetType> RETURN_TYPE_MISMATCH = DiagnosticFactory1.create(ERROR, "This function must return a value of type {0}");
DiagnosticFactory1<JetExpression, JetType> EXPECTED_TYPE_MISMATCH = DiagnosticFactory1.create(ERROR, "Expected a value of type {0}"); DiagnosticFactory1<JetExpression, JetType> EXPECTED_TYPE_MISMATCH = DiagnosticFactory1.create(ERROR, "Expected a value of type {0}");
DiagnosticFactory1<JetBinaryExpression, JetType> ASSIGNMENT_TYPE_MISMATCH = DiagnosticFactory1.create(ERROR, "Expected a value of type {0}. Assignment operation is not an expression, so it does not return any value"); DiagnosticFactory1<JetBinaryExpression, JetType> ASSIGNMENT_TYPE_MISMATCH = DiagnosticFactory1.create(ERROR, "Expected a value of type {0}. Assignment operation is not an expression, so it does not return any value");
@@ -16,8 +16,13 @@
package org.jetbrains.jet.lang.psi; package org.jetbrains.jet.lang.psi;
import com.google.common.collect.Lists;
import com.intellij.lang.ASTNode; import com.intellij.lang.ASTNode;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lexer.JetTokens;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@@ -44,4 +49,10 @@ public class JetBlockExpression extends JetExpression implements JetStatementExp
public List<JetElement> getStatements() { public List<JetElement> getStatements() {
return Arrays.asList(findChildrenByClass(JetElement.class)); return Arrays.asList(findChildrenByClass(JetElement.class));
} }
@Nullable
public TextRange getLastBracketRange() {
PsiElement rBrace = findChildByType(JetTokens.RBRACE);
return rBrace != null ? rBrace.getTextRange() : null;
}
} }
@@ -31,12 +31,12 @@ fun unitShort() : Unit = #()
fun unitShortConv() : Unit = <!TYPE_MISMATCH!>1<!> fun unitShortConv() : Unit = <!TYPE_MISMATCH!>1<!>
fun unitShortNull() : Unit = <!TYPE_MISMATCH!>null<!> fun unitShortNull() : Unit = <!TYPE_MISMATCH!>null<!>
fun intEmpty() : Int <!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>{}<!> fun intEmpty() : Int {<!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>}<!>
fun intShortInfer() = 1 fun intShortInfer() = 1
fun intShort() : Int = 1 fun intShort() : Int = 1
//fun intBlockInfer() {1} //fun intBlockInfer() {1}
fun intBlock() : Int {return 1} fun intBlock() : Int {return 1}
fun intBlock1() : Int {<!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY, UNUSED_EXPRESSION!>1<!>} fun intBlock1() : Int {<!UNUSED_EXPRESSION!>1<!><!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>}<!>
fun intString(): Int = <!TYPE_MISMATCH!>"s"<!> fun intString(): Int = <!TYPE_MISMATCH!>"s"<!>
fun intFunctionLiteral(): Int = <!TYPE_MISMATCH!>{ 10 }<!> fun intFunctionLiteral(): Int = <!TYPE_MISMATCH!>{ 10 }<!>
@@ -47,8 +47,8 @@ fun blockReturnValueTypeMatch() : Int {return 1}
fun blockReturnValueTypeMismatchUnit() : Int {return <!TYPE_MISMATCH!>#()<!>} fun blockReturnValueTypeMismatchUnit() : Int {return <!TYPE_MISMATCH!>#()<!>}
fun blockAndAndMismatch() : Int { fun blockAndAndMismatch() : Int {
<!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>true && false<!> true && false
} <!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>}<!>
fun blockAndAndMismatch1() : Int { fun blockAndAndMismatch1() : Int {
return <!TYPE_MISMATCH!>true && false<!> return <!TYPE_MISMATCH!>true && false<!>
} }
@@ -57,8 +57,8 @@ fun blockAndAndMismatch2() : Int {
} }
fun blockAndAndMismatch3() : Int { fun blockAndAndMismatch3() : Int {
<!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>true || false<!> true || false
} <!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>}<!>
fun blockAndAndMismatch4() : Int { fun blockAndAndMismatch4() : Int {
return <!TYPE_MISMATCH!>true || false<!> return <!TYPE_MISMATCH!>true || false<!>
} }
@@ -91,9 +91,9 @@ fun blockReturnValueTypeMatch6() : Int {
} }
fun blockReturnValueTypeMatch7() : Int { fun blockReturnValueTypeMatch7() : Int {
if (1 > 2) if (1 > 2)
<!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>1.0<!> 1.0
else <!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>2.0<!> else 2.0
} <!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>}<!>
fun blockReturnValueTypeMatch8() : Int { fun blockReturnValueTypeMatch8() : Int {
if (1 > 2) if (1 > 2)
1.0 1.0
@@ -101,38 +101,38 @@ fun blockReturnValueTypeMatch8() : Int {
return 1 return 1
} }
fun blockReturnValueTypeMatch9() : Int { fun blockReturnValueTypeMatch9() : Int {
<!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>if (1 > 2) if (1 > 2)
<!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>1.0<!><!> 1.0
} <!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>}<!>
fun blockReturnValueTypeMatch10() : Int { fun blockReturnValueTypeMatch10() : Int {
return <!TYPE_MISMATCH!>if (1 > 2) return <!TYPE_MISMATCH!>if (1 > 2)
1<!> 1<!>
} }
fun blockReturnValueTypeMatch11() : Int { fun blockReturnValueTypeMatch11() : Int {
<!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>if (1 > 2) if (1 > 2)
else <!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>1.0<!><!> else 1.0
} <!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>}<!>
fun blockReturnValueTypeMatch12() : Int { fun blockReturnValueTypeMatch12() : Int {
if (1 > 2) if (1 > 2)
return 1 return 1
else return <!ERROR_COMPILE_TIME_VALUE!>1.0<!> else return <!ERROR_COMPILE_TIME_VALUE!>1.0<!>
} }
fun blockNoReturnIfValDeclaration(): Int { fun blockNoReturnIfValDeclaration(): Int {
<!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>val <!UNUSED_VARIABLE!>x<!> = 1<!> val <!UNUSED_VARIABLE!>x<!> = 1
} <!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>}<!>
fun blockNoReturnIfEmptyIf(): Int { fun blockNoReturnIfEmptyIf(): Int {
if (1 < 2) <!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>{}<!> else <!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>{}<!> if (1 < 2) {} else {}
} <!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>}<!>
fun blockNoReturnIfUnitInOneBranch(): Int { fun blockNoReturnIfUnitInOneBranch(): Int {
if (1 < 2) { if (1 < 2) {
return 1 return 1
} else { } else {
if (3 < 4) <!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>{ if (3 < 4) {
}<!> else { } else {
return 2 return 2
} }
} }
} <!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>}<!>
fun nonBlockReturnIfEmptyIf(): Int = if (1 < 2) <!TYPE_MISMATCH!>{}<!> else <!TYPE_MISMATCH!>{}<!> fun nonBlockReturnIfEmptyIf(): Int = if (1 < 2) <!TYPE_MISMATCH!>{}<!> else <!TYPE_MISMATCH!>{}<!>
fun nonBlockNoReturnIfUnitInOneBranch(): Int = if (1 < 2) <!TYPE_MISMATCH!>{}<!> else 2 fun nonBlockNoReturnIfUnitInOneBranch(): Int = if (1 < 2) <!TYPE_MISMATCH!>{}<!> else 2
@@ -1,9 +1,9 @@
class C<T>() { class C<T>() {
fun foo() : T <!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>{}<!> fun foo() : T {<!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>}<!>
} }
fun foo(<!UNUSED_PARAMETER!>c<!>: C<Int>) {} fun foo(<!UNUSED_PARAMETER!>c<!>: C<Int>) {}
fun bar<T>() : C<T> <!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>{}<!> fun bar<T>() : C<T> {<!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>}<!>
fun main(args : Array<String>) { fun main(args : Array<String>) {
val <!UNUSED_VARIABLE!>a<!> : C<Int> = C(); val <!UNUSED_VARIABLE!>a<!> : C<Int> = C();
@@ -5,11 +5,11 @@ fun foo() : Int {
val d = 2 val d = 2
var z = 0 var z = 0
when(d) { when(d) {
is 5, is 3 -> <!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY, UNUSED_CHANGED_VALUE!>z++<!> is 5, is 3 -> <!UNUSED_CHANGED_VALUE!>z++<!>
<!ELSE_MISPLACED_IN_WHEN!>else<!> -> { <!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>z = <!UNUSED_VALUE!>-1000<!><!> } <!ELSE_MISPLACED_IN_WHEN!>else<!> -> { z = <!UNUSED_VALUE!>-1000<!> }
<!UNREACHABLE_CODE!>return z -> 34<!> <!UNREACHABLE_CODE!>return z -> 34<!>
} }
} <!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>}<!>
//test unreachable code //test unreachable code
fun fff(): Int { fun fff(): Int {
@@ -1,8 +1,8 @@
package kt402 package kt402
fun getTypeChecker() : (Any)->Boolean { fun getTypeChecker() : (Any)->Boolean {
<!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY, UNUSED_FUNCTION_LITERAL!>{ (a : Any) -> a is <!UNRESOLVED_REFERENCE!>T<!> }<!> // reports unsupported <!UNUSED_FUNCTION_LITERAL!>{ (a : Any) -> a is <!UNRESOLVED_REFERENCE!>T<!> }<!> // reports unsupported
} <!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>}<!>
fun f() : (Any) -> Boolean { fun f() : (Any) -> Boolean {
return { (a : Any) -> a is String } return { (a : Any) -> a is String }
} }
@@ -6,7 +6,7 @@ fun testFunny() {
val <!UNUSED_VARIABLE!>a<!> : Int = funny {1} val <!UNUSED_VARIABLE!>a<!> : Int = funny {1}
} }
fun <T> funny2(<!UNUSED_PARAMETER!>f<!> : (t : T) -> T) : T <!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>{}<!> fun <T> funny2(<!UNUSED_PARAMETER!>f<!> : (t : T) -> T) : T {<!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>}<!>
fun testFunny2() { fun testFunny2() {
val <!UNUSED_VARIABLE!>a<!> : Int = funny2 {it} val <!UNUSED_VARIABLE!>a<!> : Int = funny2 {it}
@@ -4,15 +4,15 @@ package kt456
class A() { class A() {
val i: Int val i: Int
get() : Int <!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>{ //no error get() : Int { //no error
}<!> <!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>}<!>
} }
//more tests //more tests
class B() { class B() {
val i: Int val i: Int
get() <!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>{ //no error get() { //no error
}<!> <!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>}<!>
} }
class C() { class C() {
@@ -22,9 +22,9 @@ class C() {
doSmth() doSmth()
} }
finally { finally {
<!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>doSmth()<!> doSmth()
} }
} <!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>}<!>
} }
fun doSmth() {} fun doSmth() {}
@@ -42,18 +42,18 @@ fun t3() : Int {
doSmth(2) doSmth(2)
} }
finally { finally {
<!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>doSmth(3)<!> doSmth(3)
} }
} <!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>}<!>
fun t4() : Int { fun t4() : Int {
try { try {
return 1 return 1
} }
catch (e: UnsupportedOperationException) { catch (e: UnsupportedOperationException) {
<!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>doSmth(2)<!> doSmth(2)
} }
} <!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>}<!>
fun t5() : Int { fun t5() : Int {
try { try {
@@ -84,9 +84,9 @@ fun t7() : Int {
return 2 return 2
} }
finally { finally {
<!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>doSmth(3)<!> doSmth(3)
} }
} <!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>}<!>
fun doSmth(<!UNUSED_PARAMETER!>i<!>: Int) { fun doSmth(<!UNUSED_PARAMETER!>i<!>: Int) {
} }
+22 -22
View File
@@ -26,12 +26,12 @@ fun unitShort() : Unit = #()
fun unitShortConv() : Unit = <error>1</error> fun unitShortConv() : Unit = <error>1</error>
fun unitShortNull() : Unit = <error>null</error> fun unitShortNull() : Unit = <error>null</error>
fun intEmpty() : Int <error>{}</error> fun intEmpty() : Int {<error>}</error>
fun intShortInfer() = 1 fun intShortInfer() = 1
fun intShort() : Int = 1 fun intShort() : Int = 1
//fun intBlockInfer() {1} //fun intBlockInfer() {1}
fun intBlock() : Int {return 1} fun intBlock() : Int {return 1}
fun intBlock1() : Int {<error>1</error>} fun intBlock1() : Int {<warning>1</warning><error>}</error>
fun intString(): Int = <error>"s"</error> fun intString(): Int = <error>"s"</error>
fun intFunctionLiteral(): Int = <error>{ 10 }</error> fun intFunctionLiteral(): Int = <error>{ 10 }</error>
@@ -42,8 +42,8 @@ fun blockReturnValueTypeMatch() : Int {return 1}
fun blockReturnValueTypeMismatchUnit() : Int {return <error>#()</error>} fun blockReturnValueTypeMismatchUnit() : Int {return <error>#()</error>}
fun blockAndAndMismatch() : Int { fun blockAndAndMismatch() : Int {
<error>true && false</error> true && false
} <error>}</error>
fun blockAndAndMismatch1() : Int { fun blockAndAndMismatch1() : Int {
return <error>true && false</error> return <error>true && false</error>
} }
@@ -52,8 +52,8 @@ fun blockAndAndMismatch2() : Int {
} }
fun blockAndAndMismatch3() : Int { fun blockAndAndMismatch3() : Int {
<error>true || false</error> true || false
} <error>}</error>
fun blockAndAndMismatch4() : Int { fun blockAndAndMismatch4() : Int {
return <error>true || false</error> return <error>true || false</error>
} }
@@ -86,9 +86,9 @@ fun blockReturnValueTypeMatch6() : Int {
} }
fun blockReturnValueTypeMatch7() : Int { fun blockReturnValueTypeMatch7() : Int {
if (1 > 2) if (1 > 2)
<error>1.0</error> 1.0
else <error>2.0</error> else 2.0
} <error>}</error>
fun blockReturnValueTypeMatch8() : Int { fun blockReturnValueTypeMatch8() : Int {
if (1 > 2) if (1 > 2)
1.0 1.0
@@ -96,38 +96,38 @@ fun blockReturnValueTypeMatch8() : Int {
return 1 return 1
} }
fun blockReturnValueTypeMatch9() : Int { fun blockReturnValueTypeMatch9() : Int {
<error>if (1 > 2) if (1 > 2)
<error>1.0</error></error> 1.0
} <error>}</error>
fun blockReturnValueTypeMatch10() : Int { fun blockReturnValueTypeMatch10() : Int {
return <error>if (1 > 2) return <error>if (1 > 2)
1</error> 1</error>
} }
fun blockReturnValueTypeMatch11() : Int { fun blockReturnValueTypeMatch11() : Int {
<error>if (1 > 2) if (1 > 2)
else <error>1.0</error></error> else 1.0
} <error>}</error>
fun blockReturnValueTypeMatch12() : Int { fun blockReturnValueTypeMatch12() : Int {
if (1 > 2) if (1 > 2)
return 1 return 1
else return <error>1.0</error> else return <error>1.0</error>
} }
fun blockNoReturnIfValDeclaration(): Int { fun blockNoReturnIfValDeclaration(): Int {
<error>val x = 1</error> val <warning>x</warning> = 1
} <error>}</error>
fun blockNoReturnIfEmptyIf(): Int { fun blockNoReturnIfEmptyIf(): Int {
if (1 < 2) <error>{}</error> else <error>{}</error> if (1 < 2) {} else {}
} <error>}</error>
fun blockNoReturnIfUnitInOneBranch(): Int { fun blockNoReturnIfUnitInOneBranch(): Int {
if (1 < 2) { if (1 < 2) {
return 1 return 1
} else { } else {
if (3 < 4) <error>{ if (3 < 4) {
}</error> else { } else {
return 2 return 2
} }
} }
} <error>}</error>
fun nonBlockReturnIfEmptyIf(): Int = if (1 < 2) <error>{}</error> else <error>{}</error> fun nonBlockReturnIfEmptyIf(): Int = if (1 < 2) <error>{}</error> else <error>{}</error>
fun nonBlockNoReturnIfUnitInOneBranch(): Int = if (1 < 2) <error>{}</error> else 2 fun nonBlockNoReturnIfUnitInOneBranch(): Int = if (1 < 2) <error>{}</error> else 2