diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/tasks/TracingStrategyImpl.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/tasks/TracingStrategyImpl.java index c415fb6c4f9..eeb74ee4adb 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/tasks/TracingStrategyImpl.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/tasks/TracingStrategyImpl.java @@ -21,7 +21,6 @@ import com.intellij.lang.ASTNode; import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.descriptors.*; -import org.jetbrains.jet.lang.diagnostics.DiagnosticFactory2; import org.jetbrains.jet.lang.diagnostics.Errors; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.BindingTrace; @@ -223,7 +222,14 @@ public class TracingStrategyImpl implements TracingStrategy { return; } if (constraintSystem.hasOnlyExpectedTypeMismatch()) { - reportTypeInferenceExpectedTypeMismatch(TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH, reference, data, trace); + JetType declaredReturnType = data.descriptor.getReturnType(); + if (declaredReturnType == null) return; + + JetType substitutedReturnType = constraintSystem.getResultingSubstitutor().substitute(declaredReturnType, Variance.INVARIANT); + assert substitutedReturnType != null; //todo + + assert !noExpectedType(data.expectedType) : "Expected type doesn't exist, but there is an expected type mismatch error"; + trace.report(TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH.on(reference, data.expectedType, substitutedReturnType)); } else if (constraintSystem.hasTypeConstructorMismatch()) { trace.report(TYPE_INFERENCE_TYPE_CONSTRUCTOR_MISMATCH.on(reference, data)); @@ -237,20 +243,6 @@ public class TracingStrategyImpl implements TracingStrategy { } } - public static void reportTypeInferenceExpectedTypeMismatch( - @NotNull DiagnosticFactory2 diagnosticFactory, - @NotNull JetExpression reportOn, @NotNull InferenceErrorData.ExtendedInferenceErrorData data, @NotNull BindingTrace trace - ) { - JetType declaredReturnType = data.descriptor.getReturnType(); - if (declaredReturnType == null) return; - - JetType substitutedReturnType = data.constraintSystem.getResultingSubstitutor().substitute(declaredReturnType, Variance.INVARIANT); - assert substitutedReturnType != null; //todo - - assert !noExpectedType(data.expectedType) : "Expected type doesn't exist, but there is an expected type mismatch error"; - trace.report(diagnosticFactory.on(reportOn, data.expectedType, substitutedReturnType)); - } - @Override public void upperBoundViolated(@NotNull BindingTrace trace, @NotNull InferenceErrorData inferenceErrorData) { trace.report(Errors.TYPE_INFERENCE_UPPER_BOUND_VIOLATED.on(reference, inferenceErrorData)); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ControlStructureTypingUtils.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ControlStructureTypingUtils.java index befe59dfa96..f8241958722 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ControlStructureTypingUtils.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ControlStructureTypingUtils.java @@ -27,6 +27,7 @@ import org.jetbrains.jet.lang.descriptors.impl.SimpleFunctionDescriptorImpl; import org.jetbrains.jet.lang.descriptors.impl.TypeParameterDescriptorImpl; import org.jetbrains.jet.lang.descriptors.impl.ValueParameterDescriptorImpl; import org.jetbrains.jet.lang.psi.*; +import org.jetbrains.jet.lang.resolve.BindingContextUtils; import org.jetbrains.jet.lang.resolve.BindingTrace; import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo; import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystem; @@ -37,21 +38,16 @@ import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCallWithTrace; import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResults; import org.jetbrains.jet.lang.resolve.calls.tasks.ResolutionCandidate; import org.jetbrains.jet.lang.resolve.calls.tasks.TracingStrategy; -import org.jetbrains.jet.lang.resolve.calls.tasks.TracingStrategyImpl; import org.jetbrains.jet.lang.resolve.calls.util.CallMaker; import org.jetbrains.jet.lang.resolve.name.Name; import org.jetbrains.jet.lang.resolve.scopes.JetScope; import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue; -import org.jetbrains.jet.lang.types.ErrorUtils; -import org.jetbrains.jet.lang.types.JetType; -import org.jetbrains.jet.lang.types.JetTypeImpl; -import org.jetbrains.jet.lang.types.Variance; +import org.jetbrains.jet.lang.types.*; import java.util.Collection; import java.util.Collections; import java.util.List; -import static org.jetbrains.jet.lang.diagnostics.Errors.TYPE_MISMATCH; import static org.jetbrains.jet.lang.resolve.BindingContext.CALL; import static org.jetbrains.jet.lang.resolve.BindingContext.RESOLVED_CALL; @@ -216,6 +212,50 @@ public class ControlStructureTypingUtils { } /*package*/ static TracingStrategy createTracingForIf(final @NotNull Call callForIf) { + class CheckTypeContext { + public BindingTrace trace; + public JetType expectedType; + + CheckTypeContext(@NotNull BindingTrace trace, @NotNull JetType expectedType) { + this.trace = trace; + this.expectedType = expectedType; + } + } + + final JetVisitor checkTypeVisitor = new JetVisitor() { + private void checkExpressionType(@Nullable JetExpression expression, CheckTypeContext c) { + if (expression == null) return; + expression.accept(this, c); + } + + @Override + public Void visitIfExpression(JetIfExpression ifExpression, CheckTypeContext c) { + checkExpressionType(ifExpression.getThen(), c); + checkExpressionType(ifExpression.getElse(), c); + return null; + } + + @Override + public Void visitBlockExpression(JetBlockExpression expression, CheckTypeContext c) { + List statements = expression.getStatements(); + if (!statements.isEmpty()) { + JetElement lastElement = statements.get(statements.size() - 1); + if (lastElement instanceof JetExpression) { + checkExpressionType((JetExpression) lastElement, c); + } + } + return null; + } + + @Override + public Void visitExpression(JetExpression expression, CheckTypeContext c) { + JetTypeInfo typeInfo = BindingContextUtils.getRecordedTypeInfo(expression, c.trace.getBindingContext()); + if (typeInfo != null) { + DataFlowUtils.checkType(typeInfo.getType(), expression, c.expectedType, typeInfo.getDataFlowInfo(), c.trace); + } + return null; + } + }; return new ThrowingOnErrorTracingStrategy("resolve 'if' as a call") { @Override @@ -245,9 +285,10 @@ public class ControlStructureTypingUtils { return; } if (constraintSystem.hasOnlyExpectedTypeMismatch()) { - JetExpression ifExpression = callForIf.getCalleeExpression(); - assert ifExpression != null; - TracingStrategyImpl.reportTypeInferenceExpectedTypeMismatch(TYPE_MISMATCH, ifExpression, data, trace); + JetExpression expression = callForIf.getCalleeExpression(); + if (expression != null) { + expression.accept(checkTypeVisitor, new CheckTypeContext(trace, data.expectedType)); + } return; } super.typeInferenceFailed(trace, data); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ControlStructureTypingVisitor.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ControlStructureTypingVisitor.java index c172fd5a3e7..86ff6b18ecd 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ControlStructureTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ControlStructureTypingVisitor.java @@ -151,7 +151,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { result = thenTypeInfo; } else { - result = JetTypeInfo.create(CommonSupertypes.commonSupertype(Arrays.asList(thenType, elseType)), thenDataFlowInfo.or(elseDataFlowInfo)); + result = JetTypeInfo.create(TypeUtils.commonSupertypeForPossiblyNumberTypes(Arrays.asList(thenType, elseType)), thenDataFlowInfo.or(elseDataFlowInfo)); } return DataFlowUtils.checkImplicitCast(result.getType(), ifExpression, contextWithExpectedType, isStatement, result.getDataFlowInfo()); diff --git a/compiler/testData/diagnostics/tests/controlStructures/if/reportTypeMismatchDeeplyOnBranches.kt b/compiler/testData/diagnostics/tests/controlStructures/if/reportTypeMismatchDeeplyOnBranches.kt new file mode 100644 index 00000000000..f916115aed2 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlStructures/if/reportTypeMismatchDeeplyOnBranches.kt @@ -0,0 +1,27 @@ +package b + +fun bar(i: Int) = i + +fun test(a: Int?, b: Int?) { + bar(if (a == null) return else b) +} + +fun test(a: Int?, b: Int?, c: Int?) { + bar(if (a == null) return else if (b == null) return else c) +} + +fun test(a: Any?, b: Any?, c: Int?) { + bar(if (a == null) if (b == null) c else return else return) +} + +fun test(a: Int?, b: Any?, c: Int?) { + bar(if (a == null) { + return + } else { + if (b == null) { + return + } else { + c + } + }) +} diff --git a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java index 2bb83ac4d22..d35e225b977 100644 --- a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java @@ -1258,6 +1258,7 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage } @TestMetadata("compiler/testData/diagnostics/tests/controlStructures") + @InnerTestClasses({ControlStructures.If.class}) public static class ControlStructures extends AbstractDiagnosticsTestWithEagerResolve { public void testAllFilesPresentInControlStructures() throws Exception { JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/diagnostics/tests/controlStructures"), Pattern.compile("^(.+)\\.kt$"), true); @@ -1323,6 +1324,25 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage doTest("compiler/testData/diagnostics/tests/controlStructures/when.kt234.kt973.kt"); } + @TestMetadata("compiler/testData/diagnostics/tests/controlStructures/if") + public static class If extends AbstractDiagnosticsTestWithEagerResolve { + public void testAllFilesPresentInIf() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/diagnostics/tests/controlStructures/if"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("reportTypeMismatchDeeplyOnBranches.kt") + public void testReportTypeMismatchDeeplyOnBranches() throws Exception { + doTest("compiler/testData/diagnostics/tests/controlStructures/if/reportTypeMismatchDeeplyOnBranches.kt"); + } + + } + + public static Test innerSuite() { + TestSuite suite = new TestSuite("ControlStructures"); + suite.addTestSuite(ControlStructures.class); + suite.addTestSuite(If.class); + return suite; + } } @TestMetadata("compiler/testData/diagnostics/tests/dataClasses") @@ -5353,7 +5373,7 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage suite.addTestSuite(Cast.class); suite.addTestSuite(CheckArguments.class); suite.addTestSuite(ControlFlowAnalysis.class); - suite.addTestSuite(ControlStructures.class); + suite.addTest(ControlStructures.innerSuite()); suite.addTestSuite(DataClasses.class); suite.addTest(DataFlow.innerSuite()); suite.addTestSuite(DataFlowInfoTraversal.class);