For-loop now cannot use extension iterator on a nullable #KT-7428 Fixed
This commit is contained in:
@@ -25,6 +25,7 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.kotlin.descriptors.*;
|
import org.jetbrains.kotlin.descriptors.*;
|
||||||
import org.jetbrains.kotlin.diagnostics.Diagnostic;
|
import org.jetbrains.kotlin.diagnostics.Diagnostic;
|
||||||
|
import org.jetbrains.kotlin.diagnostics.Severity;
|
||||||
import org.jetbrains.kotlin.psi.*;
|
import org.jetbrains.kotlin.psi.*;
|
||||||
import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt;
|
import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt;
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
||||||
@@ -35,11 +36,11 @@ import org.jetbrains.kotlin.types.KotlinType;
|
|||||||
import org.jetbrains.kotlin.types.TypeUtils;
|
import org.jetbrains.kotlin.types.TypeUtils;
|
||||||
import org.jetbrains.kotlin.types.expressions.KotlinTypeInfo;
|
import org.jetbrains.kotlin.types.expressions.KotlinTypeInfo;
|
||||||
import org.jetbrains.kotlin.types.expressions.typeInfoFactory.TypeInfoFactoryKt;
|
import org.jetbrains.kotlin.types.expressions.typeInfoFactory.TypeInfoFactoryKt;
|
||||||
import org.jetbrains.kotlin.util.slicedMap.MutableSlicedMap;
|
import org.jetbrains.kotlin.util.slicedMap.*;
|
||||||
import org.jetbrains.kotlin.util.slicedMap.ReadOnlySlice;
|
|
||||||
import org.jetbrains.kotlin.util.slicedMap.WritableSlice;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.EnumMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import static org.jetbrains.kotlin.diagnostics.Errors.AMBIGUOUS_LABEL;
|
import static org.jetbrains.kotlin.diagnostics.Errors.AMBIGUOUS_LABEL;
|
||||||
import static org.jetbrains.kotlin.resolve.BindingContext.*;
|
import static org.jetbrains.kotlin.resolve.BindingContext.*;
|
||||||
@@ -222,7 +223,7 @@ public class BindingContextUtils {
|
|||||||
map.forEach(new Function3<WritableSlice, Object, Object, Void>() {
|
map.forEach(new Function3<WritableSlice, Object, Object, Void>() {
|
||||||
@Override
|
@Override
|
||||||
public Void invoke(WritableSlice slice, Object key, Object value) {
|
public Void invoke(WritableSlice slice, Object key, Object value) {
|
||||||
if (filter == null || filter.accept(slice, key)) {
|
if (filter == null || filter.accept(slice, null, key)) {
|
||||||
trace.record(slice, key, value);
|
trace.record(slice, key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,7 +234,7 @@ public class BindingContextUtils {
|
|||||||
if (!commitDiagnostics) return;
|
if (!commitDiagnostics) return;
|
||||||
|
|
||||||
for (Diagnostic diagnostic : diagnostics.getOwnDiagnostics()) {
|
for (Diagnostic diagnostic : diagnostics.getOwnDiagnostics()) {
|
||||||
if (filter == null || filter.accept(null, diagnostic.getPsiElement())) {
|
if (filter == null || filter.accept(null, diagnostic, diagnostic.getPsiElement())) {
|
||||||
trace.report(diagnostic);
|
trace.report(diagnostic);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||||
import org.jetbrains.kotlin.descriptors.*;
|
import org.jetbrains.kotlin.descriptors.*;
|
||||||
|
import org.jetbrains.kotlin.diagnostics.Diagnostic;
|
||||||
import org.jetbrains.kotlin.diagnostics.rendering.Renderers;
|
import org.jetbrains.kotlin.diagnostics.rendering.Renderers;
|
||||||
import org.jetbrains.kotlin.name.Name;
|
import org.jetbrains.kotlin.name.Name;
|
||||||
import org.jetbrains.kotlin.psi.*;
|
import org.jetbrains.kotlin.psi.*;
|
||||||
@@ -333,7 +334,7 @@ public class DelegatedPropertyResolver {
|
|||||||
dataFlowInfo, traceToResolveDelegatedProperty);
|
dataFlowInfo, traceToResolveDelegatedProperty);
|
||||||
traceToResolveDelegatedProperty.commit(new TraceEntryFilter() {
|
traceToResolveDelegatedProperty.commit(new TraceEntryFilter() {
|
||||||
@Override
|
@Override
|
||||||
public boolean accept(@Nullable WritableSlice<?, ?> slice, Object key) {
|
public boolean accept(@Nullable WritableSlice<?, ?> slice, @Nullable Diagnostic diagnostic, Object key) {
|
||||||
return slice != CONSTRAINT_SYSTEM_COMPLETER;
|
return slice != CONSTRAINT_SYSTEM_COMPLETER;
|
||||||
}
|
}
|
||||||
}, true);
|
}, true);
|
||||||
|
|||||||
@@ -17,8 +17,9 @@
|
|||||||
package org.jetbrains.kotlin.resolve;
|
package org.jetbrains.kotlin.resolve;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.jetbrains.kotlin.diagnostics.Diagnostic;
|
||||||
import org.jetbrains.kotlin.util.slicedMap.WritableSlice;
|
import org.jetbrains.kotlin.util.slicedMap.WritableSlice;
|
||||||
|
|
||||||
public interface TraceEntryFilter {
|
public interface TraceEntryFilter {
|
||||||
boolean accept(@Nullable WritableSlice<?, ?> slice, Object key);
|
boolean accept(@Nullable WritableSlice<?, ?> slice, @Nullable Diagnostic diagnostic, Object key);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1178,7 +1178,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
|
|
||||||
traceInterpretingRightAsNullableAny.commit(new TraceEntryFilter() {
|
traceInterpretingRightAsNullableAny.commit(new TraceEntryFilter() {
|
||||||
@Override
|
@Override
|
||||||
public boolean accept(@Nullable WritableSlice<?, ?> slice, Object key) {
|
public boolean accept(@Nullable WritableSlice<?, ?> slice, @Nullable Diagnostic diagnostic, Object key) {
|
||||||
// the type of the right (and sometimes left) expression isn't 'Any?' actually
|
// the type of the right (and sometimes left) expression isn't 'Any?' actually
|
||||||
if ((key == right || key == left) && slice == EXPRESSION_TYPE_INFO) return false;
|
if ((key == right || key == left) && slice == EXPRESSION_TYPE_INFO) return false;
|
||||||
|
|
||||||
|
|||||||
@@ -18,11 +18,14 @@ package org.jetbrains.kotlin.types.expressions
|
|||||||
|
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
|
import org.jetbrains.kotlin.diagnostics.Errors
|
||||||
|
import org.jetbrains.kotlin.diagnostics.Severity
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.psi.Call
|
import org.jetbrains.kotlin.psi.Call
|
||||||
import org.jetbrains.kotlin.psi.KtExpression
|
import org.jetbrains.kotlin.psi.KtExpression
|
||||||
import org.jetbrains.kotlin.psi.KtPsiFactory
|
import org.jetbrains.kotlin.psi.KtPsiFactory
|
||||||
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
||||||
|
import org.jetbrains.kotlin.resolve.BindingContextUtils
|
||||||
import org.jetbrains.kotlin.resolve.TemporaryBindingTrace
|
import org.jetbrains.kotlin.resolve.TemporaryBindingTrace
|
||||||
import org.jetbrains.kotlin.resolve.calls.CallResolver
|
import org.jetbrains.kotlin.resolve.calls.CallResolver
|
||||||
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults
|
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults
|
||||||
@@ -75,17 +78,28 @@ class FakeCallResolver(
|
|||||||
context: ExpressionTypingContext,
|
context: ExpressionTypingContext,
|
||||||
valueArguments: List<KtExpression>,
|
valueArguments: List<KtExpression>,
|
||||||
name: Name,
|
name: Name,
|
||||||
callElement: KtExpression?
|
callElement: KtExpression?,
|
||||||
|
iteratorCheck: Boolean = false
|
||||||
): Pair<Call, OverloadResolutionResults<FunctionDescriptor>> {
|
): Pair<Call, OverloadResolutionResults<FunctionDescriptor>> {
|
||||||
val fakeTrace = TemporaryBindingTrace.create(context.trace, "trace to resolve fake call for", name)
|
val fakeTrace = TemporaryBindingTrace.create(context.trace, "trace to resolve fake call for", name)
|
||||||
val fakeBindingTrace = context.replaceBindingTrace(fakeTrace)
|
val fakeBindingTrace = context.replaceBindingTrace(fakeTrace)
|
||||||
|
|
||||||
return makeAndResolveFakeCallInContext(receiver, fakeBindingTrace, valueArguments, name, callElement) { fake ->
|
var hasUnreportedIteratorError = false
|
||||||
fakeTrace.commit({ slice, key ->
|
val result = makeAndResolveFakeCallInContext(receiver, fakeBindingTrace, valueArguments, name, callElement) { fake ->
|
||||||
|
fakeTrace.commit({ slice, diagnostic, key ->
|
||||||
// excluding all entries related to fake expression
|
// excluding all entries related to fake expression
|
||||||
key != fake
|
// convert all errors on this expression to ITERATOR_MISSING on callElement
|
||||||
|
val isFakeKey = key == fake
|
||||||
|
if (iteratorCheck && diagnostic?.severity == Severity.ERROR && isFakeKey) {
|
||||||
|
hasUnreportedIteratorError = true
|
||||||
|
}
|
||||||
|
!isFakeKey
|
||||||
}, true)
|
}, true)
|
||||||
}
|
}
|
||||||
|
if (hasUnreportedIteratorError && callElement != null) {
|
||||||
|
context.trace.report(Errors.ITERATOR_MISSING.on(callElement))
|
||||||
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmOverloads fun makeAndResolveFakeCallInContext(
|
@JvmOverloads fun makeAndResolveFakeCallInContext(
|
||||||
|
|||||||
+1
-1
@@ -67,7 +67,7 @@ public class ForLoopConventionsChecker {
|
|||||||
Name iterator = Name.identifier("iterator");
|
Name iterator = Name.identifier("iterator");
|
||||||
Pair<Call, OverloadResolutionResults<FunctionDescriptor>> calls =
|
Pair<Call, OverloadResolutionResults<FunctionDescriptor>> calls =
|
||||||
fakeCallResolver.makeAndResolveFakeCall(loopRange, context, Collections.<KtExpression>emptyList(), iterator,
|
fakeCallResolver.makeAndResolveFakeCall(loopRange, context, Collections.<KtExpression>emptyList(), iterator,
|
||||||
loopRange.getExpression());
|
loopRange.getExpression(), true);
|
||||||
OverloadResolutionResults<FunctionDescriptor> iteratorResolutionResults = calls.getSecond();
|
OverloadResolutionResults<FunctionDescriptor> iteratorResolutionResults = calls.getSecond();
|
||||||
|
|
||||||
if (iteratorResolutionResults.isSuccess()) {
|
if (iteratorResolutionResults.isSuccess()) {
|
||||||
|
|||||||
Vendored
+21
@@ -0,0 +1,21 @@
|
|||||||
|
// See also KT-7428
|
||||||
|
class Container<K>(val k: K)
|
||||||
|
// iterator() must be an extension, otherwise code will not compile
|
||||||
|
operator fun <K> Container<K>.iterator(): Iterator<K> = null!!
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
val container: Container<String>? = null
|
||||||
|
// Error
|
||||||
|
container<!UNSAFE_CALL!>.<!>iterator()
|
||||||
|
// for extension iterator, this code compiles, but should not
|
||||||
|
for (s in <!ITERATOR_MISSING!>container<!>) {}
|
||||||
|
}
|
||||||
|
class OtherContainer<K>(val k: K) {
|
||||||
|
operator fun iterator(): Iterator<K> = null!!
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test2() {
|
||||||
|
val other: OtherContainer<String>? = null
|
||||||
|
// Error
|
||||||
|
for (s in <!ITERATOR_MISSING!>other<!>) {}
|
||||||
|
}
|
||||||
Vendored
+22
@@ -0,0 +1,22 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public fun test(): kotlin.Unit
|
||||||
|
public fun test2(): kotlin.Unit
|
||||||
|
public operator fun </*0*/ K> Container<K>.iterator(): kotlin.collections.Iterator<K>
|
||||||
|
|
||||||
|
public final class Container</*0*/ K> {
|
||||||
|
public constructor Container</*0*/ K>(/*0*/ k: K)
|
||||||
|
public final val k: K
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class OtherContainer</*0*/ K> {
|
||||||
|
public constructor OtherContainer</*0*/ K>(/*0*/ k: K)
|
||||||
|
public final val k: K
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public final operator fun iterator(): kotlin.collections.Iterator<K>
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
@@ -3330,6 +3330,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("ForLoopWithExtensionIteratorOnNullable.kt")
|
||||||
|
public void testForLoopWithExtensionIteratorOnNullable() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlStructures/ForLoopWithExtensionIteratorOnNullable.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("forLoopWithNullableRange.kt")
|
@TestMetadata("forLoopWithNullableRange.kt")
|
||||||
public void testForLoopWithNullableRange() throws Exception {
|
public void testForLoopWithNullableRange() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlStructures/forLoopWithNullableRange.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlStructures/forLoopWithNullableRange.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user