Autocasts implemented

This commit is contained in:
Andrey Breslav
2011-06-12 18:21:35 +04:00
parent 11c0d924a6
commit d64b5b3a74
14 changed files with 345 additions and 52 deletions
@@ -0,0 +1,55 @@
package org.jetbrains.jet.lang;
import com.google.common.collect.Lists;
import com.intellij.lang.ASTNode;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lang.psi.JetReferenceExpression;
import org.jetbrains.jet.lang.types.JetType;
import java.util.List;
/**
* @author abreslav
*/
public class CollectingErrorHandler extends ErrorHandler {
private final List<JetDiagnostic> diagnostics;
public CollectingErrorHandler() {
this(Lists.<JetDiagnostic>newArrayList());
}
public CollectingErrorHandler(List<JetDiagnostic> diagnostics) {
this.diagnostics = diagnostics;
}
public List<JetDiagnostic> getDiagnostics() {
return diagnostics;
}
@Override
public void unresolvedReference(@NotNull JetReferenceExpression referenceExpression) {
diagnostics.add(new JetDiagnostic.UnresolvedReferenceError(referenceExpression));
}
@Override
public void typeMismatch(@NotNull JetExpression expression, @NotNull JetType expectedType, @NotNull JetType actualType) {
diagnostics.add(new JetDiagnostic.TypeMismatchError(expression, expectedType, actualType));
}
@Override
public void redeclaration(@NotNull DeclarationDescriptor existingDescriptor, @NotNull DeclarationDescriptor redeclaredDescriptor) {
diagnostics.add(new JetDiagnostic.RedeclarationError(existingDescriptor, redeclaredDescriptor));
}
@Override
public void genericError(@NotNull ASTNode node, @NotNull String errorMessage) {
diagnostics.add(new JetDiagnostic.GenericError(node, errorMessage));
}
@Override
public void genericWarning(@NotNull ASTNode node, @NotNull String message) {
diagnostics.add(new JetDiagnostic.GenericWarning(node, message));
}
}
@@ -0,0 +1,44 @@
package org.jetbrains.jet.lang;
import com.intellij.lang.ASTNode;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lang.psi.JetReferenceExpression;
import org.jetbrains.jet.lang.types.JetType;
/**
* @author abreslav
*/
public class ErrorHandlerAdapter extends ErrorHandler {
protected ErrorHandler worker;
public ErrorHandlerAdapter(ErrorHandler worker) {
this.worker = worker;
}
@Override
public void unresolvedReference(@NotNull JetReferenceExpression referenceExpression) {
worker.unresolvedReference(referenceExpression);
}
@Override
public void typeMismatch(@NotNull JetExpression expression, @NotNull JetType expectedType, @NotNull JetType actualType) {
worker.typeMismatch(expression, expectedType, actualType);
}
@Override
public void redeclaration(@NotNull DeclarationDescriptor existingDescriptor, @NotNull DeclarationDescriptor redeclaredDescriptor) {
worker.redeclaration(existingDescriptor, redeclaredDescriptor);
}
@Override
public void genericError(@NotNull ASTNode node, @NotNull String errorMessage) {
worker.genericError(node, errorMessage);
}
@Override
public void genericWarning(@NotNull ASTNode node, @NotNull String message) {
worker.genericWarning(node, message);
}
}
@@ -0,0 +1,97 @@
package org.jetbrains.jet.lang;
import com.intellij.lang.ASTNode;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lang.psi.JetReferenceExpression;
import org.jetbrains.jet.lang.resolve.AnalyzingUtils;
import org.jetbrains.jet.lang.types.JetType;
import java.util.Stack;
/**
* @author abreslav
*/
public class ErrorHandlerWithRegions extends ErrorHandler {
public class DiagnosticsRegion {
private final CollectingErrorHandler errorHandler;
private boolean committed = false;
private DiagnosticsRegion(CollectingErrorHandler errorHandler) {
this.errorHandler = errorHandler;
}
public CollectingErrorHandler getErrorHandler() {
assert !committed;
return errorHandler;
}
public void commit() {
assert !committed;
AnalyzingUtils.applyHandler(parent, errorHandler.getDiagnostics());
committed = true;
}
}
private final ErrorHandler parent;
private final Stack<CollectingErrorHandler> workers;
private ErrorHandler worker;
public ErrorHandlerWithRegions(ErrorHandler parent) {
this.parent = parent;
this.worker = parent;
this.workers = new Stack<CollectingErrorHandler>();
}
public void openRegion() {
CollectingErrorHandler newWorker = new CollectingErrorHandler();
workers.push(newWorker);
worker = newWorker;
}
public void closeAndCommitCurrentRegion() {
assert !workers.isEmpty();
CollectingErrorHandler region = workers.pop();
AnalyzingUtils.applyHandler(parent, region.getDiagnostics());
setWorker();
}
public DiagnosticsRegion closeAndReturnCurrentRegion() {
assert !workers.isEmpty();
CollectingErrorHandler currentWorker = workers.pop();
setWorker();
return new DiagnosticsRegion(currentWorker);
}
private void setWorker() {
worker = workers.isEmpty() ? parent : workers.peek();
}
@Override
public void unresolvedReference(@NotNull JetReferenceExpression referenceExpression) {
worker.unresolvedReference(referenceExpression);
}
@Override
public void typeMismatch(@NotNull JetExpression expression, @NotNull JetType expectedType, @NotNull JetType actualType) {
worker.typeMismatch(expression, expectedType, actualType);
}
@Override
public void redeclaration(@NotNull DeclarationDescriptor existingDescriptor, @NotNull DeclarationDescriptor redeclaredDescriptor) {
worker.redeclaration(existingDescriptor, redeclaredDescriptor);
}
@Override
public void genericError(@NotNull ASTNode node, @NotNull String errorMessage) {
worker.genericError(node, errorMessage);
}
@Override
public void genericWarning(@NotNull ASTNode node, @NotNull String message) {
worker.genericWarning(node, message);
}
}
@@ -7,6 +7,7 @@ import com.intellij.lexer.Lexer;
import com.intellij.openapi.editor.HighlighterColors;
import com.intellij.openapi.editor.SyntaxHighlighterColors;
import com.intellij.openapi.editor.colors.TextAttributesKey;
import com.intellij.openapi.editor.markup.TextAttributes;
import com.intellij.openapi.fileTypes.SyntaxHighlighterBase;
import com.intellij.psi.TokenType;
import com.intellij.psi.tree.IElementType;
@@ -14,6 +15,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lexer.JetLexer;
import org.jetbrains.jet.lexer.JetTokens;
import java.awt.*;
import java.util.HashMap;
import java.util.Map;
@@ -54,7 +56,7 @@ public class JetHighlighter extends SyntaxHighlighterBase {
SyntaxHighlighterColors.NUMBER.getDefaultAttributes()
);
private static final TextAttributesKey JET_STRING = TextAttributesKey.createTextAttributesKey(
public static final TextAttributesKey JET_STRING = TextAttributesKey.createTextAttributesKey(
"JET.STRING",
SyntaxHighlighterColors.STRING.getDefaultAttributes()
);
@@ -69,6 +71,14 @@ public class JetHighlighter extends SyntaxHighlighterBase {
HighlighterColors.BAD_CHARACTER.getDefaultAttributes()
);
public static final TextAttributesKey JET_AUTO_CAST_EXPRESSION;
static {
TextAttributes clone = SyntaxHighlighterColors.STRING.getDefaultAttributes().clone();
clone.setFontType(Font.PLAIN);
// TODO: proper attributes
JET_AUTO_CAST_EXPRESSION = TextAttributesKey.createTextAttributesKey("JET.AUTO.CAST.EXPRESSION", clone);
}
@NotNull
public Lexer getHighlightingLexer() {
return new JetLexer();
@@ -88,6 +88,22 @@ public class JetPsiChecker implements Annotator {
AnalyzingUtils.applyHandler(errorHandler, bindingContext);
highlightBackingFields(holder, file, bindingContext);
file.acceptChildren(new JetVisitor() {
@Override
public void visitExpression(JetExpression expression) {
JetType autoCast = bindingContext.getAutoCastType(expression);
if (autoCast != null) {
holder.createInfoAnnotation(expression, "Automatically cast to " + autoCast).setTextAttributes(JetHighlighter.JET_AUTO_CAST_EXPRESSION);
}
expression.acceptChildren(this);
}
@Override
public void visitJetElement(JetElement element) {
element.acceptChildren(this);
}
});
}
catch (ProcessCanceledException e) {
throw e;
@@ -22,6 +22,7 @@ import org.jetbrains.jet.lang.psi.JetNamespace;
import org.jetbrains.jet.lang.resolve.java.JavaPackageScope;
import org.jetbrains.jet.lang.resolve.java.JavaSemanticServices;
import java.util.Collection;
import java.util.Collections;
/**
@@ -115,7 +116,12 @@ public class AnalyzingUtils {
}
public static void applyHandler(@NotNull ErrorHandler errorHandler, @NotNull BindingContext bindingContext) {
for (JetDiagnostic jetDiagnostic : bindingContext.getDiagnostics()) {
Collection<JetDiagnostic> diagnostics = bindingContext.getDiagnostics();
applyHandler(errorHandler, diagnostics);
}
public static void applyHandler(@NotNull ErrorHandler errorHandler, @NotNull Collection<JetDiagnostic> diagnostics) {
for (JetDiagnostic jetDiagnostic : diagnostics) {
jetDiagnostic.acceptHandler(errorHandler);
}
}
@@ -1,6 +1,8 @@
package org.jetbrains.jet.lang.resolve;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.JetDiagnostic;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.psi.*;
@@ -41,5 +43,8 @@ public interface BindingContext {
ConstructorDescriptor resolveSuperConstructor(JetDelegatorToSuperCall superCall);
@Nullable
JetType getAutoCastType(@NotNull JetExpression expression);
Collection<JetDiagnostic> getDiagnostics();
}
@@ -2,7 +2,7 @@ package org.jetbrains.jet.lang.resolve;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.ErrorHandler;
import org.jetbrains.jet.lang.ErrorHandlerWithRegions;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.PropertyDescriptor;
import org.jetbrains.jet.lang.psi.*;
@@ -35,8 +35,10 @@ public interface BindingTrace {
void requireBackingField(@NotNull PropertyDescriptor propertyDescriptor);
void recordAutoCast(@NotNull JetExpression expression, @NotNull JetType type);
@NotNull
ErrorHandler getErrorHandler();
ErrorHandlerWithRegions getErrorHandler();
boolean isProcessed(@NotNull JetExpression expression);
@@ -2,7 +2,7 @@ package org.jetbrains.jet.lang.resolve;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.ErrorHandler;
import org.jetbrains.jet.lang.ErrorHandlerWithRegions;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.PropertyDescriptor;
import org.jetbrains.jet.lang.psi.*;
@@ -14,6 +14,11 @@ import org.jetbrains.jet.lang.types.JetType;
public class BindingTraceAdapter implements BindingTrace {
private final BindingTrace originalTrace;
@Override
public void recordAutoCast(@NotNull JetExpression expression, @NotNull JetType type) {
originalTrace.recordAutoCast(expression, type);
}
public BindingTraceAdapter(BindingTrace originalTrace) {
this.originalTrace = originalTrace;
}
@@ -63,7 +68,7 @@ public class BindingTraceAdapter implements BindingTrace {
@NotNull
@Override
public ErrorHandler getErrorHandler() {
public ErrorHandlerWithRegions getErrorHandler() {
return originalTrace.getErrorHandler();
}
@@ -3,11 +3,11 @@ package org.jetbrains.jet.lang.resolve;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.ErrorHandler;
import org.jetbrains.jet.lang.CollectingErrorHandler;
import org.jetbrains.jet.lang.ErrorHandlerWithRegions;
import org.jetbrains.jet.lang.JetDiagnostic;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.psi.*;
@@ -32,39 +32,15 @@ public class BindingTraceContext implements BindingContext, BindingTrace {
private final Set<JetElement> statements = new HashSet<JetElement>();
private final Set<PropertyDescriptor> backingFieldRequired = new HashSet<PropertyDescriptor>();
private final Set<JetExpression> processed = Sets.newHashSet();
private final Map<JetExpression, JetType> autoCasts = Maps.newHashMap();
private Collection<JetDiagnostic> diagnostics = Lists.newArrayList();
private ErrorHandler errorHandler = new ErrorHandler() {
@Override
public void unresolvedReference(@NotNull JetReferenceExpression referenceExpression) {
diagnostics.add(new JetDiagnostic.UnresolvedReferenceError(referenceExpression));
}
@Override
public void typeMismatch(@NotNull JetExpression expression, @NotNull JetType expectedType, @NotNull JetType actualType) {
diagnostics.add(new JetDiagnostic.TypeMismatchError(expression, expectedType, actualType));
}
@Override
public void redeclaration(@NotNull DeclarationDescriptor existingDescriptor, @NotNull DeclarationDescriptor redeclaredDescriptor) {
diagnostics.add(new JetDiagnostic.RedeclarationError(existingDescriptor, redeclaredDescriptor));
}
@Override
public void genericError(@NotNull ASTNode node, @NotNull String errorMessage) {
diagnostics.add(new JetDiagnostic.GenericError(node, errorMessage));
}
@Override
public void genericWarning(@NotNull ASTNode node, @NotNull String message) {
diagnostics.add(new JetDiagnostic.GenericWarning(node, message));
}
};
private final List<JetDiagnostic> diagnostics = Lists.newArrayList();
private final ErrorHandlerWithRegions errorHandler = new ErrorHandlerWithRegions(new CollectingErrorHandler(diagnostics));
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@NotNull
public ErrorHandler getErrorHandler() {
public ErrorHandlerWithRegions getErrorHandler() {
return errorHandler;
}
@@ -133,6 +109,11 @@ public class BindingTraceContext implements BindingContext, BindingTrace {
backingFieldRequired.add(propertyDescriptor);
}
@Override
public void recordAutoCast(@NotNull JetExpression expression, @NotNull JetType type) {
safePut(autoCasts, expression, type);
}
@Override
public void recordBlock(JetFunctionLiteralExpression expression) {
blocks.add(expression);
@@ -281,6 +262,11 @@ public class BindingTraceContext implements BindingContext, BindingTrace {
return descriptor instanceof ConstructorDescriptor ? (ConstructorDescriptor) descriptor : null;
}
@Override
public JetType getAutoCastType(@NotNull JetExpression expression) {
return autoCasts.get(expression);
}
@Override
public Collection<JetDiagnostic> getDiagnostics() {
return diagnostics;
@@ -14,10 +14,10 @@ import java.util.*;
public class DataFlowInfo {
private static DataFlowInfo EMPTY = new DataFlowInfo(ImmutableMap.<VariableDescriptor, NullabilityFlags>of(), Multimaps.forMap(Collections.<VariableDescriptor, JetType>emptyMap()));
public static final Supplier<SortedSet<JetType>> SORTED_SET_SUPPLIER = new Supplier<SortedSet<JetType>>() {
public static final Supplier<List<JetType>> ARRAY_LIST_SUPPLIER = new Supplier<List<JetType>>() {
@Override
public SortedSet<JetType> get() {
return new TreeSet<JetType>();
public List<JetType> get() {
return Lists.newArrayList();
}
};
@@ -83,7 +83,7 @@ public class DataFlowInfo {
public DataFlowInfo isInstanceOf(@NotNull VariableDescriptor variableDescriptor, @NotNull JetType type) {
Multimap<VariableDescriptor, JetType> newTypeInfo = copyTypeInfo();
newTypeInfo.put(variableDescriptor, type);
return new DataFlowInfo(getEqualsToNullMap(variableDescriptor, false), newTypeInfo);
return new DataFlowInfo(getEqualsToNullMap(variableDescriptor, !type.isNullable()), newTypeInfo);
}
public DataFlowInfo and(DataFlowInfo other) {
@@ -107,7 +107,7 @@ public class DataFlowInfo {
}
private Multimap<VariableDescriptor, JetType> copyTypeInfo() {
Multimap<VariableDescriptor, JetType> newTypeInfo = Multimaps.newSortedSetMultimap(Maps.<VariableDescriptor, Collection<JetType>>newHashMap(), SORTED_SET_SUPPLIER);
Multimap<VariableDescriptor, JetType> newTypeInfo = Multimaps.newListMultimap(Maps.<VariableDescriptor, Collection<JetType>>newHashMap(), ARRAY_LIST_SUPPLIER);
newTypeInfo.putAll(typeInfo);
return newTypeInfo;
}
@@ -10,6 +10,7 @@ import com.intellij.psi.tree.IElementType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.JetNodeTypes;
import org.jetbrains.jet.lang.ErrorHandlerWithRegions;
import org.jetbrains.jet.lang.JetSemanticServices;
import org.jetbrains.jet.lang.cfg.JetFlowInformationProvider;
import org.jetbrains.jet.lang.descriptors.*;
@@ -1346,12 +1347,14 @@ public class JetTypeInferrer {
condition.accept(new JetVisitor() {
@Override
public void visitIsExpression(JetIsExpression expression) {
VariableDescriptor variableDescriptor = getVariableDescriptorFromSimpleName(expression.getLeftHandSide());
if (variableDescriptor != null) {
JetPattern pattern = expression.getPattern();
if (pattern instanceof JetTypePattern) {
JetTypePattern jetTypePattern = (JetTypePattern) pattern;
result[0] = dataFlowInfo.isInstanceOf(variableDescriptor, trace.getBindingContext().resolveTypeReference(jetTypePattern.getTypeReference()));
if (conditionValue) {
VariableDescriptor variableDescriptor = getVariableDescriptorFromSimpleName(expression.getLeftHandSide());
if (variableDescriptor != null) {
JetPattern pattern = expression.getPattern();
if (pattern instanceof JetTypePattern) {
JetTypePattern jetTypePattern = (JetTypePattern) pattern;
result[0] = dataFlowInfo.isInstanceOf(variableDescriptor, trace.getBindingContext().resolveTypeReference(jetTypePattern.getTypeReference()));
}
}
}
}
@@ -1642,25 +1645,37 @@ public class JetTypeInferrer {
JetExpression receiverExpression = expression.getReceiverExpression();
JetType receiverType = new TypeInferrerVisitorWithNamespaces(scope, false, dataFlowInfo).getType(receiverExpression);
if (receiverType != null) {
ErrorHandlerWithRegions errorHandler = trace.getErrorHandler();
errorHandler.openRegion();
JetType selectorReturnType = getSelectorReturnType(receiverType, selectorExpression);
if (selectorReturnType == null) {
if (selectorReturnType != null) {
errorHandler.closeAndCommitCurrentRegion();
}
else {
ErrorHandlerWithRegions.DiagnosticsRegion regionToCommit = errorHandler.closeAndReturnCurrentRegion();
VariableDescriptor variableDescriptor = getVariableDescriptorFromSimpleName(receiverExpression);
if (variableDescriptor != null) {
Collection<JetType> possibleTypes = dataFlowInfo.getPossibleTypes(variableDescriptor);
for (JetType possibleType : possibleTypes) {
errorHandler.openRegion();
selectorReturnType = getSelectorReturnType(possibleType, selectorExpression);
if (selectorReturnType != null) {
regionToCommit = errorHandler.closeAndReturnCurrentRegion();
trace.recordAutoCast(receiverExpression, possibleType);
break;
}
}
}
regionToCommit.commit();
}
if (expression.getOperationSign() == JetTokens.QUEST) {
if (selectorReturnType != null && !isBoolean(selectorReturnType) && selectorExpression != null) {
// TODO : more comprehensible error message
trace.getErrorHandler().typeMismatch(selectorExpression, semanticServices.getStandardLibrary().getBooleanType(), selectorReturnType);
errorHandler.typeMismatch(selectorExpression, semanticServices.getStandardLibrary().getBooleanType(), selectorReturnType);
}
result = TypeUtils.makeNullable(receiverType);
}
+41
View File
@@ -0,0 +1,41 @@
class A() {
fun foo() {}
}
class B() : A() {
fun bar() {}
}
fun f9() {
val a : A?
a?.foo()
a?.<error>bar</error>()
if (a is B) {
<info descr="Automatically cast to B">a</info>.bar()
a.foo()
}
a?.foo()
a?.<error>bar</error>()
if (!(a is B)) {
a?.<error>bar</error>()
a?.foo()
}
if (!(a is B) || <info descr="Automatically cast to B">a</info>.bar() == ()) {
a?.<error>bar</error>()
}
if (!(a is B)) {
return;
}
<info descr="Automatically cast to B">a</info>.bar()
a.foo()
}
fun f10() {
val a : A?
if (!(a is B)) {
return;
}
if (!(a is B)) {
return;
}
}
+13 -2
View File
@@ -3,6 +3,7 @@ package org.jetbrains.jet;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.ErrorHandler;
import org.jetbrains.jet.lang.ErrorHandlerWithRegions;
import org.jetbrains.jet.lang.JetDiagnostic;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.psi.*;
@@ -63,10 +64,15 @@ public class JetTestUtils {
public void requireBackingField(@NotNull PropertyDescriptor propertyDescriptor) {
}
@Override
public void recordAutoCast(@NotNull JetExpression expression, @NotNull JetType type) {
}
@NotNull
@Override
public ErrorHandler getErrorHandler() {
return ErrorHandler.DO_NOTHING;
public ErrorHandlerWithRegions getErrorHandler() {
return new ErrorHandlerWithRegions(ErrorHandler.DO_NOTHING);
}
@Override
@@ -178,6 +184,11 @@ public class JetTestUtils {
throw new UnsupportedOperationException(); // TODO
}
@Override
public JetType getAutoCastType(@NotNull JetExpression expression) {
throw new UnsupportedOperationException(); // TODO
}
@Override
public Collection<JetDiagnostic> getDiagnostics() {
throw new UnsupportedOperationException(); // TODO