Report special diagnostic when fake-call receiver is nullable

#KT-3602 Fixed
This commit is contained in:
Denis Zharkov
2016-02-01 13:49:49 +03:00
parent 829a5639f1
commit 816c66063b
14 changed files with 34 additions and 35 deletions
@@ -547,6 +547,7 @@ public interface Errors {
DiagnosticFactory0<KtDestructuringDeclaration> INITIALIZER_REQUIRED_FOR_DESTRUCTURING_DECLARATION = DiagnosticFactory0.create(ERROR, DEFAULT);
DiagnosticFactory2<KtExpression, Name, KotlinType> COMPONENT_FUNCTION_MISSING = DiagnosticFactory2.create(ERROR, DEFAULT);
DiagnosticFactory1<KtExpression, Name> COMPONENT_FUNCTION_ON_NULLABLE = DiagnosticFactory1.create(ERROR, DEFAULT);
DiagnosticFactory2<KtExpression, Name, Collection<? extends ResolvedCall<?>>> COMPONENT_FUNCTION_AMBIGUITY = DiagnosticFactory2.create(ERROR, DEFAULT);
DiagnosticFactory3<KtExpression, Name, KotlinType, KotlinType> COMPONENT_FUNCTION_RETURN_TYPE_MISMATCH = DiagnosticFactory3.create(ERROR, DEFAULT);
@@ -589,6 +590,7 @@ public interface Errors {
DiagnosticFactory1<KtExpression, KotlinType> NEXT_NONE_APPLICABLE = DiagnosticFactory1.create(ERROR);
DiagnosticFactory0<KtExpression> ITERATOR_MISSING = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtExpression> ITERATOR_ON_NULLABLE = DiagnosticFactory0.create(ERROR);
DiagnosticFactory1<PsiElement, Collection<? extends ResolvedCall<?>>> ITERATOR_AMBIGUITY = DiagnosticFactory1.create(ERROR);
DiagnosticFactory2<KtExpression, String, KotlinType> DELEGATE_SPECIAL_FUNCTION_MISSING = DiagnosticFactory2.create(ERROR);
@@ -214,6 +214,7 @@ public class DefaultErrorMessages {
MAP.put(INITIALIZER_REQUIRED_FOR_DESTRUCTURING_DECLARATION, "Initializer required for destructuring declaration");
MAP.put(COMPONENT_FUNCTION_MISSING, "Destructuring declaration initializer of type {1} must have a ''{0}()'' function", TO_STRING, RENDER_TYPE);
MAP.put(COMPONENT_FUNCTION_ON_NULLABLE, "Not nullable value required to call ''{0}()'' function of destructuring declaration initializer", TO_STRING);
MAP.put(COMPONENT_FUNCTION_AMBIGUITY, "Function ''{0}()'' is ambiguous for this expression: {1}", TO_STRING, AMBIGUOUS_CALLS);
MAP.put(COMPONENT_FUNCTION_RETURN_TYPE_MISMATCH, "''{0}()'' function returns ''{1}'', but ''{2}'' is expected",
TO_STRING, RENDER_TYPE, RENDER_TYPE);
@@ -368,6 +369,7 @@ public class DefaultErrorMessages {
MAP.put(NEXT_NONE_APPLICABLE, "None of the next() functions is applicable for iterator() of type ''{0}''", RENDER_TYPE);
MAP.put(ITERATOR_MISSING, "For-loop range must have an iterator() method");
MAP.put(ITERATOR_ON_NULLABLE, "Not nullable value required to call an iterator() method on for-loop range");
MAP.put(ITERATOR_AMBIGUITY, "Method ''iterator()'' is ambiguous for this expression: {0}", AMBIGUOUS_CALLS);
MAP.put(DELEGATE_SPECIAL_FUNCTION_MISSING, "Missing ''{0}'' method on delegate of type ''{1}''", STRING, RENDER_TYPE);
@@ -25,7 +25,6 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.diagnostics.Diagnostic;
import org.jetbrains.kotlin.diagnostics.Severity;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
@@ -39,8 +38,6 @@ import org.jetbrains.kotlin.types.expressions.typeInfoFactory.TypeInfoFactoryKt;
import org.jetbrains.kotlin.util.slicedMap.*;
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.resolve.BindingContext.*;
@@ -223,7 +220,7 @@ public class BindingContextUtils {
map.forEach(new Function3<WritableSlice, Object, Object, Void>() {
@Override
public Void invoke(WritableSlice slice, Object key, Object value) {
if (filter == null || filter.accept(slice, null, key)) {
if (filter == null || filter.accept(slice, key)) {
trace.record(slice, key, value);
}
@@ -234,7 +231,7 @@ public class BindingContextUtils {
if (!commitDiagnostics) return;
for (Diagnostic diagnostic : diagnostics.getOwnDiagnostics()) {
if (filter == null || filter.accept(null, diagnostic, diagnostic.getPsiElement())) {
if (filter == null || filter.accept(null, diagnostic.getPsiElement())) {
trace.report(diagnostic);
}
}
@@ -23,7 +23,6 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.diagnostics.Diagnostic;
import org.jetbrains.kotlin.diagnostics.rendering.Renderers;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.psi.*;
@@ -334,7 +333,7 @@ public class DelegatedPropertyResolver {
dataFlowInfo, traceToResolveDelegatedProperty);
traceToResolveDelegatedProperty.commit(new TraceEntryFilter() {
@Override
public boolean accept(@Nullable WritableSlice<?, ?> slice, @Nullable Diagnostic diagnostic, Object key) {
public boolean accept(@Nullable WritableSlice<?, ?> slice, Object key) {
return slice != CONSTRAINT_SYSTEM_COMPLETER;
}
}, true);
@@ -17,9 +17,8 @@
package org.jetbrains.kotlin.resolve;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.diagnostics.Diagnostic;
import org.jetbrains.kotlin.util.slicedMap.WritableSlice;
public interface TraceEntryFilter {
boolean accept(@Nullable WritableSlice<?, ?> slice, @Nullable Diagnostic diagnostic, Object key);
boolean accept(@Nullable WritableSlice<?, ?> slice, Object key);
}
@@ -17,7 +17,6 @@
package org.jetbrains.kotlin.types.expressions;
import com.google.common.collect.Lists;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.PsiElement;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
@@ -32,7 +31,6 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.annotations.Annotations;
import org.jetbrains.kotlin.diagnostics.Diagnostic;
import org.jetbrains.kotlin.diagnostics.DiagnosticUtilsKt;
import org.jetbrains.kotlin.diagnostics.Errors;
import org.jetbrains.kotlin.lexer.KtKeywordToken;
import org.jetbrains.kotlin.lexer.KtTokens;
@@ -1181,7 +1179,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
traceInterpretingRightAsNullableAny.commit(new TraceEntryFilter() {
@Override
public boolean accept(@Nullable WritableSlice<?, ?> slice, @Nullable Diagnostic diagnostic, Object key) {
public boolean accept(@Nullable WritableSlice<?, ?> slice, Object key) {
// the type of the right (and sometimes left) expression isn't 'Any?' actually
if ((key == right || key == left) && slice == EXPRESSION_TYPE_INFO) return false;
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.types.expressions
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.diagnostics.Severity
import org.jetbrains.kotlin.name.Name
@@ -28,10 +29,10 @@ import org.jetbrains.kotlin.psi.KtSimpleNameExpression
import org.jetbrains.kotlin.resolve.TemporaryBindingTrace
import org.jetbrains.kotlin.resolve.calls.CallResolver
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResultsImpl
import org.jetbrains.kotlin.resolve.calls.util.CallMaker
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.utils.doNothing
import java.util.*
enum class FakeCallKind {
@@ -88,31 +89,34 @@ class FakeCallResolver(
val fakeTrace = TemporaryBindingTrace.create(context.trace, "trace to resolve fake call for", name)
val fakeBindingTrace = context.replaceBindingTrace(fakeTrace)
var hasUnreportedError = false
val result = makeAndResolveFakeCallInContext(receiver, fakeBindingTrace, valueArguments, name, callElement) { fake ->
fakeTrace.commit({ slice, diagnostic, key ->
var unreportedDiagnostic: Diagnostic? = null
val result = makeAndResolveFakeCallInContext(receiver, fakeBindingTrace, valueArguments, name, callElement) { fake, isSuccess ->
unreportedDiagnostic = fakeTrace.bindingContext.diagnostics.noSuppression().forElement(fake).firstOrNull { it.severity == Severity.ERROR }
if (!isSuccess) return@makeAndResolveFakeCallInContext
fakeTrace.commit({ slice, key ->
// excluding all entries related to fake expression
// convert all errors on this expression to ITERATOR_MISSING on callElement
val isFakeKey = key == fake
if (diagnostic?.severity == Severity.ERROR && isFakeKey) {
hasUnreportedError = true
}
!isFakeKey
key != fake
}, true)
}
val resolutionResults = result.second
if ((!resolutionResults.isSuccess || hasUnreportedError) && reportErrorsOn != null) {
if ((!resolutionResults.isSuccess || unreportedDiagnostic != null) && reportErrorsOn != null) {
val isUnsafeCall = unreportedDiagnostic?.factory == Errors.UNSAFE_CALL
val diagnostic = when (callKind) {
FakeCallKind.ITERATOR ->
when {
resolutionResults.isAmbiguity -> Errors.ITERATOR_AMBIGUITY.on(reportErrorsOn, resolutionResults.resultingCalls)
isUnsafeCall -> Errors.ITERATOR_ON_NULLABLE.on(reportErrorsOn)
else -> Errors.ITERATOR_MISSING.on(reportErrorsOn)
}
FakeCallKind.COMPONENT ->
when {
resolutionResults.isAmbiguity -> Errors.COMPONENT_FUNCTION_AMBIGUITY.on(
reportErrorsOn, name, resolutionResults.resultingCalls)
isUnsafeCall -> Errors.COMPONENT_FUNCTION_ON_NULLABLE.on(reportErrorsOn, name)
receiver != null -> Errors.COMPONENT_FUNCTION_MISSING.on(reportErrorsOn, name, receiver.type)
else -> null
}
@@ -132,15 +136,13 @@ class FakeCallResolver(
valueArguments: List<KtExpression>,
name: Name,
callElement: KtExpression?,
onSuccess: (KtSimpleNameExpression) -> Unit = doNothing()
onComplete: (KtSimpleNameExpression, Boolean) -> Unit = { x, y -> }
): Pair<Call, OverloadResolutionResults<FunctionDescriptor>> {
val fake = KtPsiFactory(project).createSimpleName(name.asString())
val call = CallMaker.makeCallWithExpressions(callElement ?: fake, receiver, null, fake, valueArguments)
val results = callResolver.resolveCallWithGivenName(context, call, fake, name)
if (results.isSuccess) {
onSuccess(fake)
}
onComplete(fake, results.isSuccess)
return Pair(call, results)
}
@@ -8,7 +8,7 @@ fun test() {
// Error
container<!UNSAFE_CALL!>.<!>iterator()
// for extension iterator, this code compiles, but should not
for (s in <!ITERATOR_MISSING!>container<!>) {}
for (s in <!ITERATOR_ON_NULLABLE!>container<!>) {}
}
class OtherContainer<K>(val k: K) {
operator fun iterator(): Iterator<K> = null!!
@@ -17,5 +17,5 @@ class OtherContainer<K>(val k: K) {
fun test2() {
val other: OtherContainer<String>? = null
// Error
for (s in <!ITERATOR_MISSING!>other<!>) {}
for (s in <!ITERATOR_ON_NULLABLE!>other<!>) {}
}
@@ -8,7 +8,7 @@ class It {
}
fun test(c: Coll?) {
for (x in <!ITERATOR_MISSING!>c<!>) {}
for (x in <!ITERATOR_ON_NULLABLE!>c<!>) {}
if (c != null) {
for(x in <!DEBUG_INFO_SMARTCAST!>c<!>) {}
@@ -7,7 +7,7 @@ operator fun <T> Data<T>.component2() = y
fun foo(): Int {
val d: Data<Int>? = null
// An error must be here
val (x, y) = <!COMPONENT_FUNCTION_MISSING, COMPONENT_FUNCTION_MISSING!>d<!>
val (x, y) = <!COMPONENT_FUNCTION_ON_NULLABLE, COMPONENT_FUNCTION_ON_NULLABLE!>d<!>
return x + y
}
@@ -16,6 +16,6 @@ data class NormalData<T>(val x: T, val y: T)
fun bar(): Int {
val d: NormalData<Int>? = null
// An error must be here
val (x, y) = <!COMPONENT_FUNCTION_MISSING, COMPONENT_FUNCTION_MISSING!>d<!>
val (x, y) = <!COMPONENT_FUNCTION_ON_NULLABLE, COMPONENT_FUNCTION_ON_NULLABLE!>d<!>
return <!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>x<!> <!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>+<!> <!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>y<!>
}
@@ -22,7 +22,7 @@ fun test() {
val platformJ = J.staticJ
for (x in platformNN) {}
for (x in <!ITERATOR_MISSING!>platformN<!>) {}
for (x in <!ITERATOR_ON_NULLABLE!>platformN<!>) {}
for (x in platformJ) {}
}
@@ -27,6 +27,6 @@ fun test() {
val platformJ = J.staticJ
val (a1, b1) = platformNN
val (a2, b2) = <!COMPONENT_FUNCTION_MISSING, COMPONENT_FUNCTION_MISSING!>platformN<!>
val (a2, b2) = <!COMPONENT_FUNCTION_ON_NULLABLE, COMPONENT_FUNCTION_ON_NULLABLE!>platformN<!>
val (a3, b3) = platformJ
}
@@ -298,7 +298,7 @@ class QuickFixRegistrar : QuickFixContributor {
NEXT_MISSING.registerFactory(CreateNextFunctionActionFactory)
NEXT_NONE_APPLICABLE.registerFactory(CreateNextFunctionActionFactory)
ITERATOR_MISSING.registerFactory(CreateIteratorFunctionActionFactory)
ITERATOR_MISSING.registerFactory(MissingIteratorExclExclFixFactory)
ITERATOR_ON_NULLABLE.registerFactory(MissingIteratorExclExclFixFactory)
COMPONENT_FUNCTION_MISSING.registerFactory(CreateComponentFunctionActionFactory)
DELEGATE_SPECIAL_FUNCTION_MISSING.registerFactory(CreatePropertyDelegateAccessorsActionFactory)
@@ -1,6 +1,6 @@
// "Add non-null asserted (!!) call" "false"
// ACTION: Replace with a 'forEach' function call
// ERROR: For-loop range must have an iterator() method
// ERROR: Not nullable value required to call an iterator() method on for-loop range
class Some {
fun iterator(): Iterator<Int> = null!!