Error on 'if' without an 'else' branch when used as an expression

This commit is contained in:
Yan Zhulanow
2015-10-13 18:13:51 +03:00
parent de5dc61820
commit e14c9645dc
29 changed files with 121 additions and 91 deletions
@@ -27,7 +27,9 @@ public object SystemOutLogger : Logger {
private fun out(level: String, msg: CharSequence) = println("[$level] $msg")
public var isDebugEnabled: Boolean = true
override fun debug(msg: CharSequence) = if (isDebugEnabled) out("DEBUG", msg)
override fun debug(msg: CharSequence) {
if (isDebugEnabled) out("DEBUG", msg)
}
override fun info(msg: CharSequence) = out("INFO", msg)
override fun warn(msg: CharSequence) = out("WARN", msg)
override fun error(msg: CharSequence) = out("ERROR", msg)
@@ -610,6 +610,8 @@ public interface Errors {
DiagnosticFactory0<JetElement> SENSELESS_NULL_IN_WHEN = DiagnosticFactory0.create(WARNING);
DiagnosticFactory0<JetIfExpression> INVALID_IF_AS_EXPRESSION = DiagnosticFactory0.create(ERROR);
// Nullability
DiagnosticFactory1<PsiElement, JetType> UNSAFE_CALL = DiagnosticFactory1.create(ERROR);
@@ -537,6 +537,8 @@ public class DefaultErrorMessages {
MAP.put(SENSELESS_COMPARISON, "Condition ''{0}'' is always ''{1}''", ELEMENT_TEXT, TO_STRING);
MAP.put(SENSELESS_NULL_IN_WHEN, "Expression under 'when' is never equal to null");
MAP.put(INVALID_IF_AS_EXPRESSION, "'if' must have both main and 'else' branches if used as an expression");
MAP.put(OVERRIDING_FINAL_MEMBER, "''{0}'' in ''{1}'' is final and cannot be overridden", NAME, NAME);
MAP.put(CANNOT_WEAKEN_ACCESS_PRIVILEGE, "Cannot weaken access privilege ''{0}'' for ''{1}'' in ''{2}''", TO_STRING, NAME, NAME);
MAP.put(CANNOT_CHANGE_ACCESS_PRIVILEGE, "Cannot change access privilege ''{0}'' for ''{1}'' in ''{2}''", TO_STRING, NAME, NAME);
@@ -504,8 +504,11 @@ public class CandidateResolver(
private fun <D : CallableDescriptor> CallCandidateResolutionContext<D>.shouldContinue() =
candidateResolveMode == CandidateResolveMode.FULLY || candidateCall.getStatus().possibleTransformToSuccess()
private inline fun <D : CallableDescriptor> CallCandidateResolutionContext<D>
.check(checker: CallCandidateResolutionContext<D>.() -> Unit): Unit = if (shouldContinue()) checker()
private inline fun <D : CallableDescriptor> CallCandidateResolutionContext<D>.check(
checker: CallCandidateResolutionContext<D>.() -> Unit
) {
if (shouldContinue()) checker()
}
private inline fun <D : CallableDescriptor> CallCandidateResolutionContext<D>.
checkAndReport(checker: CallCandidateResolutionContext<D>.() -> ResolutionStatus) {
@@ -256,10 +256,21 @@ public class DataFlowAnalyzer {
@Nullable
public JetType checkImplicitCast(@Nullable JetType expressionType, @NotNull JetExpression expression, @NotNull ResolutionContext context, boolean isStatement) {
if (expressionType != null && context.expectedType == NO_EXPECTED_TYPE && context.contextDependency == INDEPENDENT && !isStatement
boolean isIfExpression = expression instanceof JetIfExpression;
if (expressionType != null && (context.expectedType == NO_EXPECTED_TYPE || isIfExpression)
&& context.contextDependency == INDEPENDENT && !isStatement
&& (KotlinBuiltIns.isUnit(expressionType) || KotlinBuiltIns.isAnyOrNullableAny(expressionType))
&& !DynamicTypesKt.isDynamic(expressionType)) {
context.trace.report(IMPLICIT_CAST_TO_UNIT_OR_ANY.on(expression, expressionType));
if (isIfExpression && KotlinBuiltIns.isUnit(expressionType)) {
JetIfExpression ifExpression = (JetIfExpression) expression;
if (ifExpression.getThen() == null || ifExpression.getElse() == null) {
context.trace.report(INVALID_IF_AS_EXPRESSION.on((JetIfExpression) expression));
return expressionType;
}
}
else {
context.trace.report(IMPLICIT_CAST_TO_UNIT_OR_ANY.on(expression, expressionType));
}
}
return expressionType;
}
@@ -1,12 +0,0 @@
fun box(): String {
if (true);
if (false);
val iftrue = if (true);
val iffalse = if (false);
var state = 0
val k = if (state++==1);
if (state != 1) return "Fail: $state"
return "OK"
}
@@ -1,5 +0,0 @@
fun box(): String {
val a = if(true) {
}
return if (a.toString() == "kotlin.Unit") "OK" else "fail"
}
@@ -1,9 +0,0 @@
fun foo(condition: Boolean): String {
val u = if (condition) {
"OK"
} else {
}
return u.toString()
}
fun box() = foo(true)
+7 -2
View File
@@ -1,5 +1,10 @@
fun unsupportedEx() = if (true) throw UnsupportedOperationException()
fun runtimeEx() = if (true) throw RuntimeException()
fun unsupportedEx() {
if (true) throw UnsupportedOperationException()
}
fun runtimeEx() {
if (true) throw RuntimeException()
}
fun test1() : String {
var s = "";
+7 -2
View File
@@ -1,5 +1,10 @@
fun unsupportedEx() = if (true) throw UnsupportedOperationException()
fun runtimeEx() = if (true) throw RuntimeException()
fun unsupportedEx() {
if (true) throw UnsupportedOperationException()
}
fun runtimeEx() {
if (true) throw RuntimeException()
}
fun test1WithFinally() : String {
var s = "";
@@ -1,7 +1,8 @@
// This test checks that our bytecode is consistent with javac bytecode
fun _assert(condition: Boolean): Unit =
fun _assert(condition: Boolean) {
if (!condition) throw AssertionError("Fail")
}
fun _assertFalse(condition: Boolean) = _assert(!condition)
+2 -2
View File
@@ -4,9 +4,9 @@ class A (val p: String, p1: String, p2: String) {
var cond2: String = ""
val prop1 = if (cond1(p)) p1
val prop1 = if (cond1(p)) p1 else null
val prop2 = if (cond2(p)) else;
val prop2 = if (cond2(p)) p2 else null;
fun cond1(p: String): Boolean {
@@ -6,9 +6,9 @@ class A (val p: String, p1: String, p2: String) {
val prop: String = if (p == "test") p1 else p2
val prop1 = if (cond1(p)) p1
val prop1 = if (cond1(p)) p1 else false
val prop2 = if (cond2(p)) else;
val prop2 = if (cond2(p)) true else false
fun cond1(p: String): Boolean {
cond1 = "cond1"
+3 -3
View File
@@ -69,10 +69,10 @@ fun blockReturnValueTypeMatch1() : Int {
return if (1 > 2) <!CONSTANT_EXPECTED_TYPE_MISMATCH!>1.0<!> else <!CONSTANT_EXPECTED_TYPE_MISMATCH!>2.0<!>
}
fun blockReturnValueTypeMatch2() : Int {
return <!TYPE_MISMATCH!>if (1 > 2) 1<!>
return <!TYPE_MISMATCH, INVALID_IF_AS_EXPRESSION!>if (1 > 2) 1<!>
}
fun blockReturnValueTypeMatch3() : Int {
return <!TYPE_MISMATCH!>if (1 > 2) else 1<!>
return <!TYPE_MISMATCH, INVALID_IF_AS_EXPRESSION!>if (1 > 2) else 1<!>
}
fun blockReturnValueTypeMatch4() : Int {
if (1 > 2)
@@ -105,7 +105,7 @@ fun blockReturnValueTypeMatch9() : Int {
<!UNUSED_EXPRESSION!>1.0<!>
<!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>}<!>
fun blockReturnValueTypeMatch10() : Int {
return <!TYPE_MISMATCH!>if (1 > 2)
return <!TYPE_MISMATCH, INVALID_IF_AS_EXPRESSION!>if (1 > 2)
1<!>
}
fun blockReturnValueTypeMatch11() : Int {
@@ -4,13 +4,13 @@ fun test() {
if (false);
if (true);
val x = <!IMPLICIT_CAST_TO_UNIT_OR_ANY!>if (false)<!>;
val x = <!INVALID_IF_AS_EXPRESSION!>if (false)<!>;
foo(x)
val y: Unit = if (false);
val y: Unit = <!INVALID_IF_AS_EXPRESSION!>if (false)<!>;
foo(y)
foo({if (1==1);}())
return if (true);
return <!INVALID_IF_AS_EXPRESSION!>if (true)<!>;
}
@@ -0,0 +1,33 @@
// !DIAGNOSTICS: -UNUSED_VARIABLE
fun example() {
val a = if (true) true else false
val b = <!INVALID_IF_AS_EXPRESSION!>if (true) else false<!>
val c = <!INVALID_IF_AS_EXPRESSION!>if (true) true<!>
val d = <!INVALID_IF_AS_EXPRESSION!>if (true) true else<!>;
val e = <!IMPLICIT_CAST_TO_UNIT_OR_ANY!>if (true) {} else false<!>
val f = <!IMPLICIT_CAST_TO_UNIT_OR_ANY!>if (true) true else {}<!>
{
if (true) <!UNUSED_EXPRESSION!>true<!>
}();
{
if (true) true else false
}();
{
if (true) {} else false
}();
{
if (true) true else {}
}()
fun t(): Boolean {
return <!TYPE_MISMATCH, INVALID_IF_AS_EXPRESSION!>if (true) true<!>
}
return if (true) <!CONSTANT_EXPECTED_TYPE_MISMATCH!>true<!> else {}
}
@@ -0,0 +1,3 @@
package
public fun example(): kotlin.Unit
@@ -7,5 +7,5 @@ fun test(a: Boolean, b: Boolean): Int {
<!TYPE_MISMATCH!>if (b) {
3
}<!>
} // no error, but must be
}
}
@@ -34,7 +34,7 @@ fun foo() {
fun box() : Int {
val d = 2
var z = 0
when(d) {
when(d) {
5, 3 -> z++
else -> z = -1000
}
@@ -100,10 +100,10 @@ fun testImplicitCoercion() {
else -> <!UNUSED_CHANGED_VALUE!>z--<!>
}<!>
var <!UNUSED_VARIABLE!>iff<!> = <!IMPLICIT_CAST_TO_UNIT_OR_ANY!>if (true) {
var <!UNUSED_VARIABLE!>iff<!> = <!INVALID_IF_AS_EXPRESSION!>if (true) {
<!UNUSED_VALUE!>z =<!> 34
}<!>
val <!UNUSED_VARIABLE!>g<!> = <!IMPLICIT_CAST_TO_UNIT_OR_ANY!>if (true) 4<!>
val <!UNUSED_VARIABLE!>g<!> = <!INVALID_IF_AS_EXPRESSION!>if (true) 4<!>
val <!UNUSED_VARIABLE!>h<!> = <!IMPLICIT_CAST_TO_UNIT_OR_ANY!>if (false) 4 else {}<!>
bar(if (true) {
+3 -3
View File
@@ -18,7 +18,7 @@ fun f3(s: Number?) {
}
fun f4(s: Int?) {
var u = <!IMPLICIT_CAST_TO_UNIT_OR_ANY!>if (s!! == 42)<!>;
if (u == Unit) u = if (s == 239);
var u = <!INVALID_IF_AS_EXPRESSION!>if (s!! == 42)<!>;
if (u == Unit) u = <!INVALID_IF_AS_EXPRESSION!>if (s == 239)<!>;
return u
}
}
@@ -1,7 +1,7 @@
val flag = true
// type of a was checked by txt
val a/*: () -> Any*/ = l@ { // commonSupertype(Int, Unit) = Any
val a/*: () -> Any*/ = l@ {
if (flag) return@l 4
}
+1 -5
View File
@@ -2,10 +2,6 @@ fun foo() {
if (0 < 1) {
return
}
val u: Unit = if (0 < 1) {
return
}
}
// 2 3 6 7 6 9
// 2 3 5
@@ -3258,6 +3258,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
doTest(fileName);
}
@TestMetadata("improperElseInExpression.kt")
public void testImproperElseInExpression() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlStructures/improperElseInExpression.kt");
doTest(fileName);
}
@TestMetadata("jumpAcrossFunctionBoundary.kt")
public void testJumpAcrossFunctionBoundary() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlStructures/jumpAcrossFunctionBoundary.kt");
@@ -2053,12 +2053,6 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest(fileName);
}
@TestMetadata("emptyIf.kt")
public void testEmptyIf() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/emptyIf.kt");
doTest(fileName);
}
@TestMetadata("emptyWhile.kt")
public void testEmptyWhile() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/emptyWhile.kt");
@@ -2149,12 +2143,6 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest(fileName);
}
@TestMetadata("kt2062.kt")
public void testKt2062() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/kt2062.kt");
doTest(fileName);
}
@TestMetadata("kt2147.kt")
public void testKt2147() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/kt2147.kt");
@@ -2197,12 +2185,6 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest(fileName);
}
@TestMetadata("kt2598.kt")
public void testKt2598() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/kt2598.kt");
doTest(fileName);
}
@TestMetadata("kt299.kt")
public void testKt299() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/kt299.kt");
@@ -28,8 +28,9 @@ import org.jetbrains.kotlin.generators.builtins.ranges.GenerateRanges
import java.io.File
import java.io.PrintWriter
fun assertExists(file: File): Unit =
if (!file.exists()) error("Output dir does not exist: ${file.getAbsolutePath()}")
fun assertExists(file: File) {
if (!file.exists()) error("Output dir does not exist: ${file.getAbsolutePath()}")
}
val BUILT_INS_NATIVE_DIR = File("core/builtins/native/")
val BUILT_INS_SRC_DIR = File("core/builtins/src/")
+1
View File
@@ -1,4 +1,5 @@
// IS_APPLICABLE: false
// ERROR: 'if' must have both main and 'else' branches if used as an expression
// ERROR: Expression 'if "test" is String' of type 'kotlin.Unit' cannot be invoked as a function. The function invoke() is not found
fun doSomething<T>(a: T) {}
@@ -320,7 +320,9 @@ public class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR
val compilerServices = Services.Builder()
.register(javaClass<IncrementalCompilationComponents>(), IncrementalCompilationComponentsImpl(incrementalCaches, lookupTracker))
.register(javaClass<CompilationCanceledStatus>(), object : CompilationCanceledStatus {
override fun checkCanceled(): Unit = if (context.getCancelStatus().isCanceled()) throw CompilationCanceledException()
override fun checkCanceled() {
if (context.getCancelStatus().isCanceled()) throw CompilationCanceledException()
}
})
.build()
@@ -42,19 +42,20 @@ public fun parseFunction(code: String, offset: Int, reporter: ErrorReporter, sco
private class FunctionParsingObserver : Observer {
var functionsStarted = 0
override fun update(o: Observable?, arg: Any?): Unit =
when (arg) {
is ParserEvents.OnFunctionParsingStart -> {
functionsStarted++
}
is ParserEvents.OnFunctionParsingEnd -> {
functionsStarted--
override fun update(o: Observable?, arg: Any?) {
when (arg) {
is ParserEvents.OnFunctionParsingStart -> {
functionsStarted++
}
is ParserEvents.OnFunctionParsingEnd -> {
functionsStarted--
if (functionsStarted == 0) {
arg.tokenStream.ungetToken(TokenStream.EOF)
}
if (functionsStarted == 0) {
arg.tokenStream.ungetToken(TokenStream.EOF)
}
}
}
}
}
inline
@@ -8,7 +8,7 @@ fun bar(s: String, value: Boolean): Boolean {
}
fun box(): String {
val a = if (true || if(bar("A", false)) {2} else {3} == 0) { }
val a = if (true || if(bar("A", false)) {2} else {3} == 0) true else false
assertEquals("", global)
true || if(bar("A", false)) {2} else {3} == 0