Error on 'if' without an 'else' branch when used as an expression
This commit is contained in:
@@ -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);
|
||||
|
||||
+2
@@ -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
@@ -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
@@ -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
@@ -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"
|
||||
|
||||
@@ -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)<!>;
|
||||
}
|
||||
+33
@@ -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 {}
|
||||
}
|
||||
+3
@@ -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
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -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
@@ -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
-1
@@ -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
@@ -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");
|
||||
|
||||
-18
@@ -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,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
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user