From 71610fb7410e4b0e9c891847d6a4e4ec267c002e Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Wed, 16 Mar 2011 17:11:00 +0300 Subject: [PATCH] Checks for . and ?. --- .../org/jetbrains/jet/lang/ErrorHandler.java | 11 ++-- .../jet/lang/annotations/JetPsiChecker.java | 15 ++++-- .../lang/resolve/ClassDescriptorResolver.java | 6 +-- .../jet/lang/types/JetTypeInferrer.java | 50 +++++++++++++++---- 4 files changed, 59 insertions(+), 23 deletions(-) diff --git a/idea/src/org/jetbrains/jet/lang/ErrorHandler.java b/idea/src/org/jetbrains/jet/lang/ErrorHandler.java index 02bac1d8126..327fd11e89d 100644 --- a/idea/src/org/jetbrains/jet/lang/ErrorHandler.java +++ b/idea/src/org/jetbrains/jet/lang/ErrorHandler.java @@ -18,7 +18,7 @@ public class ErrorHandler { } @Override - public void structuralError(ASTNode node, String errorMessage) { + public void genericError(ASTNode node, String errorMessage) { throw new IllegalStateException(errorMessage); } @@ -36,12 +36,15 @@ public class ErrorHandler { public void unresolvedReference(JetReferenceExpression referenceExpression) { } - public void structuralError(ASTNode node, String errorMessage) { - } - public void typeMismatch(JetExpression expression, JetType expectedType, JetType actualType) { } public void redeclaration(DeclarationDescriptor existingDescriptor, DeclarationDescriptor redeclaredDescriptor) { } + + public void genericError(ASTNode node, String errorMessage) { + } + + public void genericWarning(ASTNode node, String message) { + } } diff --git a/idea/src/org/jetbrains/jet/lang/annotations/JetPsiChecker.java b/idea/src/org/jetbrains/jet/lang/annotations/JetPsiChecker.java index cd31e59b3f0..3e793893c23 100644 --- a/idea/src/org/jetbrains/jet/lang/annotations/JetPsiChecker.java +++ b/idea/src/org/jetbrains/jet/lang/annotations/JetPsiChecker.java @@ -50,11 +50,6 @@ public class JetPsiChecker implements Annotator { } } - @Override - public void structuralError(ASTNode node, String errorMessage) { - holder.createErrorAnnotation(node, errorMessage); - } - @Override public void typeMismatch(JetExpression expression, JetType expectedType, JetType actualType) { holder.createErrorAnnotation(expression, "Type mismatch: inferred type is " + actualType + " but " + expectedType + " was expected"); @@ -65,6 +60,16 @@ public class JetPsiChecker implements Annotator { redeclarations.add(existingDescriptor); redeclarations.add(redeclaredDescriptor); } + + @Override + public void genericError(ASTNode node, String errorMessage) { + holder.createErrorAnnotation(node, errorMessage); + } + + @Override + public void genericWarning(ASTNode node, String message) { + holder.createWarningAnnotation(node, message); + } }); for (DeclarationDescriptor redeclaration : redeclarations) { PsiElement declarationPsiElement = bindingContext.getDeclarationPsiElement(redeclaration); diff --git a/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java b/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java index 8ca4fd6cc08..f70dbda8d71 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java @@ -159,7 +159,7 @@ public class ClassDescriptorResolver { } } else { if (bodyExpression == null) { - semanticServices.getErrorHandler().structuralError(function.getNode(), "This function must either declare a return type or have a body expression"); + semanticServices.getErrorHandler().genericError(function.getNode(), "This function must either declare a return type or have a body expression"); returnType = ErrorType.createErrorType("No type, no body"); } else { // TODO : Recursion possible @@ -184,7 +184,7 @@ public class ClassDescriptorResolver { JetType type; if (typeReference == null) { - semanticServices.getErrorHandler().structuralError(valueParameter.getNode(), "A type annotation is required on a value parameter " + valueParameter.getName()); + semanticServices.getErrorHandler().genericError(valueParameter.getNode(), "A type annotation is required on a value parameter " + valueParameter.getName()); type = ErrorType.createErrorType("Type annotation was missing"); } else { type = typeResolver.resolveType(parameterScope, typeReference); @@ -265,7 +265,7 @@ public class ClassDescriptorResolver { if (propertyTypeRef == null) { JetExpression initializer = property.getInitializer(); if (initializer == null) { - semanticServices.getErrorHandler().structuralError(property.getNode(), "This property must either have a type annotation or be initialized"); + semanticServices.getErrorHandler().genericError(property.getNode(), "This property must either have a type annotation or be initialized"); type = ErrorType.createErrorType("No type, no body"); } else { // TODO : ??? Fix-point here: what if we have something like "val a = foo {a.bar()}" diff --git a/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java b/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java index 1ee029edb4a..d4e68f4ca15 100644 --- a/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java +++ b/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java @@ -137,11 +137,13 @@ public class JetTypeInferrer { @Override public void visitQualifiedExpression(JetQualifiedExpression expression) { // . or ?. + JetType receiverType = getType(scope, expression.getReceiverExpression(), false); + checkNullSafety(receiverType, expression); + JetExpression selectorExpression = expression.getSelectorExpression(); if (selectorExpression instanceof JetSimpleNameExpression) { JetSimpleNameExpression referenceExpression = (JetSimpleNameExpression) selectorExpression; - JetType receiverType = getType(scope, expression.getReceiverExpression(), false); if (receiverType != null) { result[0] = semanticServices.getOverloadResolver().getOverloadDomain(receiverType, scope, referenceExpression.getReferencedName()); reference[0] = referenceExpression; @@ -172,6 +174,17 @@ public class JetTypeInferrer { return wrapForTracing(result[0], reference[0], true); } + private void checkNullSafety(JetType receiverType, JetQualifiedExpression expression) { + if (receiverType != null) { + if (receiverType.isNullable() && expression.getOperationSign() == JetTokens.DOT) { + semanticServices.getErrorHandler().genericError(expression.getOperationTokenNode(), "Only safe calls (?.) are allowed on a nullable receiver of type " + receiverType); + } + else if (!receiverType.isNullable() && expression.getOperationSign() == JetTokens.SAFE_ACCESS) { + semanticServices.getErrorHandler().genericWarning(expression.getOperationTokenNode(), "Unnecessary safe call on a non-null receiver of type " + receiverType); + } + } + } + private OverloadDomain wrapForTracing(final OverloadDomain overloadDomain, @NotNull final JetReferenceExpression referenceExpression, final boolean reportUnresolved) { if (overloadDomain == null) return OverloadDomain.EMPTY; return new OverloadDomain() { @@ -408,7 +421,7 @@ public class JetTypeInferrer { // TODO : Check for cast impossibility } else { - semanticServices.getErrorHandler().structuralError(expression.getOperationSign().getNode(), "Unsupported binary operation"); + semanticServices.getErrorHandler().genericError(expression.getOperationSign().getNode(), "Unsupported binary operation"); } result = targetType; } @@ -422,7 +435,7 @@ public class JetTypeInferrer { JetType conditionType = getType(scope, condition, false); if (conditionType != null && !isBoolean(conditionType)) { - semanticServices.getErrorHandler().structuralError(condition.getNode(), "Condition must be of type Boolean, but was of type " + conditionType); + semanticServices.getErrorHandler().genericError(condition.getNode(), "Condition must be of type Boolean, but was of type " + conditionType); } } // TODO : change types according to is and nullability checks @@ -517,14 +530,29 @@ public class JetTypeInferrer { } @Override - public void visitDotQualifiedExpression(JetDotQualifiedExpression expression) { + public void visitHashQualifiedExpression(JetHashQualifiedExpression expression) { + throw new UnsupportedOperationException(); // TODO + } + + @Override + public void visitPredicateExpression(JetPredicateExpression expression) { + throw new UnsupportedOperationException(); // TODO + } + + @Override + public void visitQualifiedExpression(JetQualifiedExpression expression) { // TODO : functions JetExpression receiverExpression = expression.getReceiverExpression(); JetExpression selectorExpression = expression.getSelectorExpression(); JetType receiverType = getType(scope, receiverExpression, false); - if (receiverType != null && selectorExpression != null) { // TODO : review - JetScope compositeScope = new ScopeWithReceiver(scope, receiverType); - result = getType(compositeScope, selectorExpression, false); + if (receiverType != null) { + checkNullSafety(receiverType, expression); + if (selectorExpression != null) { + // TODO : review + + JetScope compositeScope = new ScopeWithReceiver(scope, receiverType); + result = getType(compositeScope, selectorExpression, false); + } } } @@ -597,7 +625,7 @@ public class JetTypeInferrer { JetSimpleNameExpression operationSign = expression.getOperationSign(); String name = unaryOperationNames.get(operationSign.getReferencedNameElementType()); if (name == null) { - semanticServices.getErrorHandler().structuralError(operationSign.getNode(), "Unknown unary operation"); + semanticServices.getErrorHandler().genericError(operationSign.getNode(), "Unknown unary operation"); } else { JetType type = getType(scope, expression.getBaseExpression(), false); @@ -645,7 +673,7 @@ public class JetTypeInferrer { if (constructor.equals(intTypeConstructor)) { result = standardLibrary.getBooleanType(); } else { - semanticServices.getErrorHandler().structuralError(operationSign.getNode(), "compareTo must return Int, but returns " + compareToReturnType); + semanticServices.getErrorHandler().genericError(operationSign.getNode(), "compareTo must return Int, but returns " + compareToReturnType); } } } @@ -692,7 +720,7 @@ public class JetTypeInferrer { result = semanticServices.getStandardLibrary().getBooleanType(); } else { - semanticServices.getErrorHandler().structuralError(operationSign.getNode(), "Unknown operation"); + semanticServices.getErrorHandler().genericError(operationSign.getNode(), "Unknown operation"); } } @@ -700,7 +728,7 @@ public class JetTypeInferrer { if (resultType != null) { // TODO : Relax? if (!isBoolean(resultType)) { - semanticServices.getErrorHandler().structuralError(operationSign.getNode(), "'" + name + "' must return Boolean but returns " + resultType); + semanticServices.getErrorHandler().genericError(operationSign.getNode(), "'" + name + "' must return Boolean but returns " + resultType); return null; } else { return resultType;