Nullablility diagnostics for extension functions

This commit is contained in:
Andrey Breslav
2011-05-30 20:09:04 +04:00
parent 02565f669c
commit 6319ada926
17 changed files with 166 additions and 77 deletions
+2
View File
@@ -7,6 +7,8 @@ namespace typeinfo {
}
}
fun Any?.equals(other : Any?) : Boolean = this === other
class Iterator<out T> {
fun next() : T
@@ -5,14 +5,12 @@ import com.intellij.lang.ASTNode;
import com.intellij.lang.annotation.AnnotationHolder;
import com.intellij.lang.annotation.Annotator;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.MultiRangeReference;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.ErrorHandler;
import org.jetbrains.jet.lang.JetDiagnostic;
import org.jetbrains.jet.lang.JetHighlighter;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.AnalyzingUtils;
@@ -124,8 +122,8 @@ public class JetPsiChecker implements Annotator {
}
@Override
public void visitJetElement(JetElement elem) {
elem.acceptChildren(this);
public void visitJetElement(JetElement element) {
element.acceptChildren(this);
}
});
}
@@ -694,16 +694,16 @@ public class JetControlFlowProcessor {
}
@Override
public void visitJetElement(JetElement elem) {
throw new UnsupportedOperationException("[JetControlFlowProcessor] " + elem.toString());
public void visitJetElement(JetElement element) {
throw new UnsupportedOperationException("[JetControlFlowProcessor] " + element.toString());
}
});
}
}
@Override
public void visitJetElement(JetElement elem) {
throw new UnsupportedOperationException("[JetControlFlowProcessor] " + elem.toString());
public void visitJetElement(JetElement element) {
throw new UnsupportedOperationException("[JetControlFlowProcessor] " + element.toString());
}
});
}
@@ -726,8 +726,8 @@ public class JetControlFlowProcessor {
}
@Override
public void visitJetElement(JetElement elem) {
builder.unsupported(elem);
public void visitJetElement(JetElement element) {
builder.unsupported(element);
}
}
}
@@ -31,9 +31,9 @@ public class JetPsiUtil {
final Set<JetElement> shadowedElements = new HashSet<JetElement>();
JetVisitor shadowAllChildren = new JetVisitor() {
@Override
public void visitJetElement(JetElement elem) {
if (shadowedElements.add(elem)) {
elem.acceptChildren(this);
public void visitJetElement(JetElement element) {
if (shadowedElements.add(element)) {
element.acceptChildren(this);
}
}
};
@@ -6,8 +6,8 @@ import com.intellij.psi.PsiElementVisitor;
* @author max
*/
public class JetVisitor extends PsiElementVisitor {
public void visitJetElement(JetElement elem) {
visitElement(elem);
public void visitJetElement(JetElement element) {
visitElement(element);
}
public void visitDeclaration(JetDeclaration dcl) {
@@ -143,6 +143,10 @@ public class JetVisitor extends PsiElementVisitor {
}
public void visitSimpleNameExpression(JetSimpleNameExpression expression) {
visitReferenceExpression(expression);
}
public void visitReferenceExpression(JetReferenceExpression expression) {
visitExpression(expression);
}
@@ -231,7 +235,7 @@ public class JetVisitor extends PsiElementVisitor {
}
public void visitArrayAccessExpression(JetArrayAccessExpression expression) {
visitExpression(expression);
visitReferenceExpression(expression);
}
public void visitQualifiedExpression(JetQualifiedExpression expression) {
@@ -84,8 +84,8 @@ public class ClassDescriptorResolver {
}
@Override
public void visitJetElement(JetElement elem) {
throw new UnsupportedOperationException(elem.toString());
public void visitJetElement(JetElement element) {
throw new UnsupportedOperationException(element.toString());
}
});
}
@@ -7,6 +7,7 @@ import org.jetbrains.jet.lang.descriptors.FunctionGroup;
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.JetTypeChecker;
import org.jetbrains.jet.lang.types.TypeUtils;
import java.util.ArrayList;
import java.util.Collection;
@@ -62,9 +63,12 @@ public class OverloadResolver {
if (receiverType != null) {
// ASSERT : either the receiver in not present or we are in a scope with no top-level functions
final JetType functionReceiverType = descriptor.getReceiverType();
if (functionReceiverType != null && !typeChecker.isSubtypeOf(receiverType, functionReceiverType)) {
continue;
JetType functionReceiverType = descriptor.getReceiverType();
if (functionReceiverType != null) {
functionReceiverType = TypeUtils.makeNullable(functionReceiverType); // Too look things up in T for T?, and later check receiver's nullability
if (!typeChecker.isSubtypeOf(receiverType, functionReceiverType)) {
continue;
}
}
}
else if (descriptor.getReceiverType() != null) {
@@ -14,7 +14,7 @@ public class ScopeWithReceiver extends JetScopeImpl {
private final JetScope outerScope;
private final JetTypeChecker typeChecker;
public ScopeWithReceiver(JetScope outerScope, JetType receiverType, JetTypeChecker typeChecker) {
public ScopeWithReceiver(@NotNull JetScope outerScope, @NotNull JetType receiverType, @NotNull JetTypeChecker typeChecker) {
this.outerScope = outerScope;
this.receiverType = receiverType;
this.typeChecker = typeChecker;
@@ -23,7 +23,9 @@ public class ScopeWithReceiver extends JetScopeImpl {
@NotNull
@Override
public FunctionGroup getFunctionGroup(@NotNull String name) {
FunctionGroup functionGroup = receiverType.getMemberScope().getFunctionGroup(name);
JetScope memberScope = receiverType.getMemberScope();
assert memberScope != null : receiverType;
FunctionGroup functionGroup = memberScope.getFunctionGroup(name);
if (functionGroup.isEmpty()) {
return outerScope.getFunctionGroup(name);
// return FunctionDescriptorUtil.filteredFunctionGroup(outerScope.getFunctionGroup(name),
@@ -449,8 +449,8 @@ public class TopDownAnalyzer {
}
@Override
public void visitJetElement(JetElement elem) {
throw new UnsupportedOperationException(elem.getText() + " : " + elem);
public void visitJetElement(JetElement element) {
throw new UnsupportedOperationException(element.getText() + " : " + element);
}
});
}
@@ -149,8 +149,8 @@ public class TypeResolver {
}
@Override
public void visitJetElement(JetElement elem) {
throw new IllegalArgumentException("Unsupported type: " + elem);
public void visitJetElement(JetElement element) {
throw new IllegalArgumentException("Unsupported type: " + element);
}
});
}
@@ -123,11 +123,7 @@ public class ErrorUtils {
);
}
private static FunctionDescriptor createErrorFunction(int typeParameterCount, Map<String, JetType> valueParameters) {
throw new UnsupportedOperationException(); // TODO
}
private static FunctionDescriptor createErrorFunction(int typeParameterCount, List<JetType> positionedValueParameterTypes) {
public static FunctionDescriptor createErrorFunction(int typeParameterCount, List<JetType> positionedValueParameterTypes) {
return new FunctionDescriptorImpl(ERROR_CLASS, Collections.<Annotation>emptyList(), "<ERROR FUNCTION>").initialize(
null,
Collections.<TypeParameterDescriptor>emptyList(), // TODO
@@ -59,6 +59,7 @@ public final class JetTypeImpl extends AnnotatedImpl implements JetType {
return nullable;
}
@NotNull
@Override
public JetScope getMemberScope() {
if (memberScope == null) {
@@ -164,22 +164,22 @@ public class JetTypeInferrer {
trace.getErrorHandler().genericError(expression.getNode(), "Unsupported [JetTypeInferrer]");
// . or ?.
JetType receiverType = getType(scope, expression.getReceiverExpression(), false);
checkNullSafety(receiverType, expression.getOperationTokenNode());
JetExpression selectorExpression = expression.getSelectorExpression();
if (selectorExpression instanceof JetSimpleNameExpression) {
JetSimpleNameExpression referenceExpression = (JetSimpleNameExpression) selectorExpression;
String referencedName = referenceExpression.getReferencedName();
if (receiverType != null && referencedName != null) {
// No generics. Guaranteed
result[0] = semanticServices.getOverloadResolver().getOverloadDomain(receiverType, scope, referencedName);
reference[0] = referenceExpression;
}
} else {
throw new UnsupportedOperationException(); // TODO
}
// JetType receiverType = getType(scope, expression.getReceiverExpression(), false);
// checkNullSafety(receiverType, expression.getOperationTokenNode());
//
// JetExpression selectorExpression = expression.getSelectorExpression();
// if (selectorExpression instanceof JetSimpleNameExpression) {
// JetSimpleNameExpression referenceExpression = (JetSimpleNameExpression) selectorExpression;
// String referencedName = referenceExpression.getReferencedName();
//
// if (receiverType != null && referencedName != null) {
// // No generics. Guaranteed
// result[0] = semanticServices.getOverloadResolver().getOverloadDomain(receiverType, scope, referencedName);
// reference[0] = referenceExpression;
// }
// } else {
// throw new UnsupportedOperationException(); // TODO
// }
}
@Override
@@ -200,22 +200,25 @@ public class JetTypeInferrer {
}
@Override
public void visitJetElement(JetElement elem) {
trace.getErrorHandler().genericError(elem.getNode(), "Unsupported in call element"); // TODO : Message
public void visitJetElement(JetElement element) {
trace.getErrorHandler().genericError(element.getNode(), "Unsupported in call element"); // TODO : Message
}
});
return wrapForTracing(result[0], reference[0], argumentList, true);
}
private void checkNullSafety(JetType receiverType, ASTNode operationTokenNode) {
if (receiverType != null) {
private void checkNullSafety(@Nullable JetType receiverType, @NotNull ASTNode operationTokenNode, @Nullable FunctionDescriptor callee) {
if (receiverType != null && callee != null) {
boolean namespaceType = receiverType instanceof NamespaceType;
boolean nullable = !namespaceType && receiverType.isNullable();
JetType calleeReceiverType = callee.getReceiverType();
boolean nullableReceiver = !namespaceType && receiverType.isNullable();
boolean calleeForbidsNullableReceiver = calleeReceiverType == null || !calleeReceiverType.isNullable();
IElementType operationSign = operationTokenNode.getElementType();
if (nullable && operationSign == JetTokens.DOT) {
if (nullableReceiver && calleeForbidsNullableReceiver && operationSign == JetTokens.DOT) {
trace.getErrorHandler().genericError(operationTokenNode, "Only safe calls (?.) are allowed on a nullable receiver of type " + receiverType);
}
else if (!nullable && operationSign == JetTokens.SAFE_ACCESS) {
else if ((!nullableReceiver || !calleeForbidsNullableReceiver) && operationSign == JetTokens.SAFE_ACCESS) {
if (namespaceType) {
trace.getErrorHandler().genericError(operationTokenNode, "Safe calls are not allowed on namespaces");
}
@@ -1054,7 +1057,7 @@ public class JetTypeInferrer {
// TODO : exhaustive patterns
for (JetWhenEntry whenEntry : expression.getEntries()) {
final JetType finalSubjectType = subjectType;
final JetType finalSubjectType = subjectType != null ? subjectType : ErrorUtils.createErrorType("Unknown type");
JetWhenCondition condition = whenEntry.getCondition();
if (condition != null) {
condition.accept(new JetVisitor() {
@@ -1073,12 +1076,12 @@ public class JetTypeInferrer {
@Override
public void visitWhenConditionCall(JetWhenConditionCall condition) {
checkNullSafety(finalSubjectType, condition.getOperationTokenNode());
JetExpression callSuffixExpression = condition.getCallSuffixExpression();
JetScope compositeScope = new ScopeWithReceiver(scope, finalSubjectType, semanticServices.getTypeChecker());
if (callSuffixExpression != null) {
JetType selectorReturnType = getType(compositeScope, callSuffixExpression, false);
ensureBooleanResultWithCustomSubject(callSuffixExpression, selectorReturnType, "This expression");
checkNullSafety(finalSubjectType, condition.getOperationTokenNode(), getCalleeFunctionDescriptor(callSuffixExpression));
}
}
@@ -1099,8 +1102,8 @@ public class JetTypeInferrer {
}
@Override
public void visitJetElement(JetElement elem) {
trace.getErrorHandler().genericError(elem.getNode(), "Unsupported [JetTypeInferrer] : " + elem);
public void visitJetElement(JetElement element) {
trace.getErrorHandler().genericError(element.getNode(), "Unsupported [JetTypeInferrer] : " + element);
}
});
}
@@ -1182,8 +1185,8 @@ public class JetTypeInferrer {
}
@Override
public void visitJetElement(JetElement elem) {
trace.getErrorHandler().genericError(elem.getNode(), "Unsupported [JetTypeInferrer]");
public void visitJetElement(JetElement element) {
trace.getErrorHandler().genericError(element.getNode(), "Unsupported [JetTypeInferrer]");
}
});
}
@@ -1437,8 +1440,6 @@ public class JetTypeInferrer {
JetExpression receiverExpression = expression.getReceiverExpression();
JetType receiverType = new TypeInferrerVisitorWithNamespaces(scope, false).getType(receiverExpression);
if (receiverType != null) {
// TODO : extensions to 'Any?'
checkNullSafety(receiverType, expression.getOperationTokenNode());
JetType selectorReturnType = getSelectorReturnType(receiverType, selectorExpression);
if (expression.getOperationSign() == JetTokens.QUEST) {
if (selectorReturnType != null && !isBoolean(selectorReturnType) && selectorExpression != null) {
@@ -1453,9 +1454,57 @@ public class JetTypeInferrer {
if (selectorExpression != null && result != null) {
trace.recordExpressionType(selectorExpression, result);
}
if (selectorReturnType != null) {
// TODO : extensions to 'Any?'
assert selectorExpression != null;
checkNullSafety(receiverType, expression.getOperationTokenNode(), getCalleeFunctionDescriptor(selectorExpression));
}
}
}
@NotNull
private FunctionDescriptor getCalleeFunctionDescriptor(@NotNull JetExpression selectorExpression) {
final FunctionDescriptor[] result = new FunctionDescriptor[1];
selectorExpression.accept(new JetVisitor() {
@Override
public void visitCallExpression(JetCallExpression callExpression) {
callExpression.getCalleeExpression().accept(this);
}
@Override
public void visitReferenceExpression(JetReferenceExpression referenceExpression) {
DeclarationDescriptor declarationDescriptor = trace.getBindingContext().resolveReferenceExpression(referenceExpression);
if (declarationDescriptor instanceof FunctionDescriptor) {
result[0] = (FunctionDescriptor) declarationDescriptor;
}
}
@Override
public void visitArrayAccessExpression(JetArrayAccessExpression expression) {
expression.getArrayExpression().accept(this);
}
@Override
public void visitBinaryExpression(JetBinaryExpression expression) {
expression.getLeft().accept(this);
}
@Override
public void visitQualifiedExpression(JetQualifiedExpression expression) {
expression.getReceiverExpression().accept(this);
}
@Override
public void visitJetElement(JetElement element) {
trace.getErrorHandler().genericError(element.getNode(), "Unsupported [getCalleeFunctionDescriptor]: " + element);
}
});
if (result[0] == null) {
result[0] = ErrorUtils.createErrorFunction(0, Collections.<JetType>emptyList());
}
return result[0];
}
private JetType getCallExpressionType(@Nullable JetType receiverType, @NotNull JetCallExpression callExpression) {
OverloadDomain overloadDomain = getOverloadDomain(receiverType, scope, callExpression.getCalleeExpression(), callExpression.getValueArgumentList());
return resolveCall(scope, overloadDomain, callExpression);
@@ -1769,8 +1818,8 @@ public class JetTypeInferrer {
}
@Override
public void visitJetElement(JetElement elem) {
trace.getErrorHandler().genericError(elem.getNode(), "[JetTypeInferrer] Unsupported element: " + elem + " " + elem.getClass().getCanonicalName());
public void visitJetElement(JetElement element) {
trace.getErrorHandler().genericError(element.getNode(), "[JetTypeInferrer] Unsupported element: " + element + " " + element.getClass().getCanonicalName());
}
}
@@ -1919,9 +1968,10 @@ public class JetTypeInferrer {
}
@Override
public void visitJetElement(JetElement elem) {
trace.getErrorHandler().genericError(elem.getNode(), "Unsupported element in a block: " + elem + " " + elem.getClass().getCanonicalName());
public void visitJetElement(JetElement element) {
trace.getErrorHandler().genericError(element.getNode(), "Unsupported element in a block: " + element + " " + element.getClass().getCanonicalName());
}
}
// private class CachedBindingTrace extends BindingTraceAdapter {
@@ -15,15 +15,16 @@ import java.util.*;
* @author abreslav
*/
public class TypeUtils {
public static JetType makeNullable(JetType type) {
public static JetType makeNullable(@NotNull JetType type) {
return makeNullableAsSpecified(type, true);
}
public static JetType makeNotNullable(JetType type) {
public static JetType makeNotNullable(@NotNull JetType type) {
return makeNullableAsSpecified(type, false);
}
public static JetType makeNullableAsSpecified(JetType type, boolean nullable) {
@NotNull
public static JetType makeNullableAsSpecified(@NotNull JetType type, boolean nullable) {
if (type.isNullable() == nullable) {
return type;
}
+34 -2
View File
@@ -6,7 +6,7 @@ fun <T, E> T.foo(x : E, y : A) : T {
y plus 1
y + 1.0
this?.minus<T>(this)
this<warning>?.</warning>minus<T>(this)
this
}
@@ -35,4 +35,36 @@ val Int.abs : Int
val <T> T.foo : T
fun Int.foo() = this
fun Int.foo() = this
namespace null_safety {
fun parse(cmd: String): Command? { return null }
class Command() {
// fun equals(other : Any?) : Boolean
val foo : Int
}
fun Any.equals(other : Any?) : Boolean
fun Any?.equals1(other : Any?) : Boolean
fun main(args: Array<String>) {
System.out?.print(1)
val command = parse("")
command<error>.</error>foo
command<error>.</error>equals(null)
command?.equals(null)
command.equals1(null)
command<warning>?.</warning>equals1(null)
val c = new Command()
c<warning>?.</warning>equals(null)
if (command == null) 1
}
}
@@ -2,7 +2,6 @@ package org.jetbrains.jet;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.codegen.ClassCodegen;
import org.jetbrains.jet.lang.ErrorHandler;
import org.jetbrains.jet.lang.JetDiagnostic;
import org.jetbrains.jet.lang.descriptors.*;
@@ -136,7 +135,7 @@ public class JetTestUtils {
@Override
public DeclarationDescriptor resolveReferenceExpression(JetReferenceExpression referenceExpression) {
throw new UnsupportedOperationException(); // TODO
return null;
}
@Override
@@ -52,10 +52,10 @@ public class JetParsingTest extends ParsingTestCase {
protected void checkResult(@NonNls String targetDataName, PsiFile file) throws IOException {
file.acceptChildren(new JetVisitor() {
@Override
public void visitJetElement(JetElement elem) {
elem.acceptChildren(this);
public void visitJetElement(JetElement element) {
element.acceptChildren(this);
try {
checkPsiGetters(elem);
checkPsiGetters(element);
} catch (Throwable throwable) {
throw new RuntimeException(throwable);
}