A warning for redundant '?' in types

This commit is contained in:
Andrey Breslav
2012-10-19 18:37:04 +04:00
parent 92ef1c9417
commit 80039d8533
9 changed files with 75 additions and 25 deletions
@@ -283,6 +283,7 @@ public interface Errors {
SimpleDiagnosticFactory<JetClassInitializer> ANONYMOUS_INITIALIZER_WITHOUT_CONSTRUCTOR = SimpleDiagnosticFactory.create(ERROR);
SimpleDiagnosticFactory<JetNullableType> NULLABLE_SUPERTYPE = SimpleDiagnosticFactory.create(ERROR, NULLABLE_TYPE);
SimpleDiagnosticFactory<JetNullableType> REDUNDANT_NULLABLE = SimpleDiagnosticFactory.create(WARNING, NULLABLE_TYPE);
DiagnosticFactory1<PsiElement, JetType> UNSAFE_CALL = DiagnosticFactory1.create(ERROR);
SimpleDiagnosticFactory<JetSimpleNameExpression> AMBIGUOUS_LABEL = SimpleDiagnosticFactory.create(ERROR);
DiagnosticFactory1<PsiElement, String> UNSUPPORTED = DiagnosticFactory1.create(ERROR);
@@ -306,6 +306,7 @@ public class DefaultErrorMessages {
MAP.put(ANONYMOUS_INITIALIZER_WITHOUT_CONSTRUCTOR, "Anonymous initializers are only allowed in the presence of a primary constructor");
MAP.put(NULLABLE_SUPERTYPE, "A supertype cannot be nullable");
MAP.put(REDUNDANT_NULLABLE, "Redundant '?'");
MAP.put(UNSAFE_CALL, "Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type {0}", RENDER_TYPE);
MAP.put(AMBIGUOUS_LABEL, "Ambiguous label");
MAP.put(UNSUPPORTED, "Unsupported [{0}]", TO_STRING);
@@ -200,21 +200,8 @@ public class DescriptorResolver {
JetTypeReference typeReference = delegationSpecifier.getTypeReference();
if (typeReference != null) {
result.add(resolver.resolveType(extensibleScope, typeReference, trace, checkBounds));
JetTypeElement typeElement = typeReference.getTypeElement();
while (typeElement instanceof JetNullableType) {
JetNullableType nullableType = (JetNullableType) typeElement;
trace.report(NULLABLE_SUPERTYPE.on(nullableType));
typeElement = nullableType.getInnerType();
}
if (typeElement instanceof JetUserType) {
JetUserType userType = (JetUserType) typeElement;
List<JetTypeProjection> typeArguments = userType.getTypeArguments();
for (JetTypeProjection typeArgument : typeArguments) {
if (typeArgument.getProjectionKind() != JetProjectionKind.NONE) {
trace.report(PROJECTION_IN_IMMEDIATE_ARGUMENT_TO_SUPERTYPE.on(typeArgument));
}
}
}
JetTypeElement bareSuperType = checkNullableSupertypeAndStripQuestionMarks(trace, typeReference.getTypeElement());
checkProjectionsInImmediateArguments(trace, bareSuperType);
}
else {
result.add(ErrorUtils.createErrorType("No type reference"));
@@ -223,6 +210,31 @@ public class DescriptorResolver {
return result;
}
@Nullable
private static JetTypeElement checkNullableSupertypeAndStripQuestionMarks(@NotNull BindingTrace trace, @Nullable JetTypeElement typeElement) {
while (typeElement instanceof JetNullableType) {
JetNullableType nullableType = (JetNullableType) typeElement;
typeElement = nullableType.getInnerType();
// report only for innermost '?', the rest gets a 'redundant' warning
if (!(typeElement instanceof JetNullableType)) {
trace.report(NULLABLE_SUPERTYPE.on(nullableType));
}
}
return typeElement;
}
private static void checkProjectionsInImmediateArguments(@NotNull BindingTrace trace, @Nullable JetTypeElement typeElement) {
if (typeElement instanceof JetUserType) {
JetUserType userType = (JetUserType) typeElement;
List<JetTypeProjection> typeArguments = userType.getTypeArguments();
for (JetTypeProjection typeArgument : typeArguments) {
if (typeArgument.getProjectionKind() != JetProjectionKind.NONE) {
trace.report(PROJECTION_IN_IMMEDIATE_ARGUMENT_TO_SUPERTYPE.on(typeArgument));
}
}
}
}
@NotNull
public SimpleFunctionDescriptor resolveFunctionDescriptor(
DeclarationDescriptor containingDescriptor,
@@ -77,7 +77,7 @@ public class TypeResolver {
final List<AnnotationDescriptor> annotations = annotationResolver.getResolvedAnnotations(typeReference.getAnnotations(), trace);
JetTypeElement typeElement = typeReference.getTypeElement();
JetType type = resolveTypeElement(scope, annotations, typeElement, false, trace, checkBounds);
JetType type = resolveTypeElement(scope, annotations, typeElement, trace, checkBounds);
trace.record(BindingContext.TYPE, typeReference, type);
trace.record(BindingContext.TYPE_RESOLUTION_SCOPE, typeReference, scope);
@@ -86,7 +86,7 @@ public class TypeResolver {
@NotNull
private JetType resolveTypeElement(final JetScope scope, final List<AnnotationDescriptor> annotations,
JetTypeElement typeElement, final boolean nullable, final BindingTrace trace, final boolean checkBounds) {
JetTypeElement typeElement, final BindingTrace trace, final boolean checkBounds) {
final JetType[] result = new JetType[1];
if (typeElement != null) {
@@ -118,7 +118,7 @@ public class TypeResolver {
result[0] = new JetTypeImpl(
annotations,
typeParameterDescriptor.getTypeConstructor(),
nullable || TypeUtils.hasNullableLowerBound(typeParameterDescriptor),
TypeUtils.hasNullableLowerBound(typeParameterDescriptor),
Collections.<TypeProjection>emptyList(),
scopeForTypeParameter
);
@@ -151,7 +151,7 @@ public class TypeResolver {
result[0] = new JetTypeImpl(
annotations,
typeConstructor,
nullable,
false,
arguments,
classDescriptor.getMemberScope(arguments)
);
@@ -174,7 +174,11 @@ public class TypeResolver {
@Override
public void visitNullableType(JetNullableType nullableType) {
result[0] = resolveTypeElement(scope, annotations, nullableType.getInnerType(), true, trace, checkBounds);
JetType baseType = resolveTypeElement(scope, annotations, nullableType.getInnerType(), trace, checkBounds);
if (baseType.isNullable()) {
trace.report(REDUNDANT_NULLABLE.on(nullableType));
}
result[0] = TypeUtils.makeNullable(baseType);
}
@Override
@@ -222,9 +226,6 @@ public class TypeResolver {
if (result[0] == null) {
return ErrorUtils.createErrorType(typeElement == null ? "No type element" : typeElement.getText());
}
if (nullable) {
return TypeUtils.makeNullable(result[0]);
}
return result[0];
}
@@ -3,4 +3,4 @@ trait B<T> {}
trait C<T> {}
trait D<T> {}
trait Test : A<<!PROJECTION_IN_IMMEDIATE_ARGUMENT_TO_SUPERTYPE!>in<!> Int>, B<<!PROJECTION_IN_IMMEDIATE_ARGUMENT_TO_SUPERTYPE!>out<!> Int>, C<<!PROJECTION_IN_IMMEDIATE_ARGUMENT_TO_SUPERTYPE!>*<!>><!NULLABLE_SUPERTYPE!>?<!><!NULLABLE_SUPERTYPE!>?<!><!NULLABLE_SUPERTYPE!>?<!>, D<Int> {}
trait Test : A<<!PROJECTION_IN_IMMEDIATE_ARGUMENT_TO_SUPERTYPE!>in<!> Int>, B<<!PROJECTION_IN_IMMEDIATE_ARGUMENT_TO_SUPERTYPE!>out<!> Int>, C<<!PROJECTION_IN_IMMEDIATE_ARGUMENT_TO_SUPERTYPE!>*<!>><!NULLABLE_SUPERTYPE!>?<!><!REDUNDANT_NULLABLE!>?<!><!REDUNDANT_NULLABLE!>?<!>, D<Int> {}
@@ -0,0 +1,9 @@
class Generic<T>
fun redundantNullable(
<!UNUSED_PARAMETER!>i<!>: Int?<!REDUNDANT_NULLABLE!>?<!>,
<!UNUSED_PARAMETER!>three<!>: Int?<!REDUNDANT_NULLABLE!>?<!><!REDUNDANT_NULLABLE!>?<!>,
<!UNUSED_PARAMETER!>gOut<!>: Generic<Int>?<!REDUNDANT_NULLABLE!>?<!>,
<!UNUSED_PARAMETER!>gIn<!>: Generic<Int?<!REDUNDANT_NULLABLE!>?<!>>
) {
}
@@ -0,0 +1,8 @@
trait X: Any<!NULLABLE_SUPERTYPE!>?<!><!REDUNDANT_NULLABLE!>?<!> {
}
fun interaction<T>(t: T) {
if (<!SENSELESS_COMPARISON!>t == null<!>) {}
}
@@ -2288,6 +2288,24 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
}
@TestMetadata("compiler/testData/diagnostics/tests/nullableTypes")
public static class NullableTypes extends AbstractDiagnosticsTestWithEagerResolve {
public void testAllFilesPresentInNullableTypes() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.checkers.AbstractDiagnosticsTestWithEagerResolve", new File("compiler/testData/diagnostics/tests/nullableTypes"), "kt", true);
}
@TestMetadata("redundantNullable.kt")
public void testRedundantNullable() throws Exception {
doTest("compiler/testData/diagnostics/tests/nullableTypes/redundantNullable.kt");
}
@TestMetadata("redundantNullableInSupertype.kt")
public void testRedundantNullableInSupertype() throws Exception {
doTest("compiler/testData/diagnostics/tests/nullableTypes/redundantNullableInSupertype.kt");
}
}
@TestMetadata("compiler/testData/diagnostics/tests/objects")
public static class Objects extends AbstractDiagnosticsTestWithEagerResolve {
public void testAllFilesPresentInObjects() throws Exception {
@@ -3,4 +3,4 @@ trait B<T> {}
trait C<T> {}
trait D<T> {}
trait Test : A<<error>in</error> Int>, B<<error>out</error> Int>, C<<error>*</error>><error>?</error><error>?</error><error>?</error>, D<Int> {}
trait Test : A<<error>in</error> Int>, B<<error>out</error> Int>, C<<error>*</error>><error>?</error><warning>?</warning><warning>?</warning>, D<Int> {}