JET-39 Process ?. correctly in the receiver types

This commit is contained in:
Andrey Breslav
2011-05-23 10:42:50 +04:00
parent 9d123b5c4f
commit dd612c74e3
8 changed files with 104 additions and 66 deletions
@@ -1,10 +1,7 @@
package org.jetbrains.jet.lang.descriptors; package org.jetbrains.jet.lang.descriptors;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.collect.BiMap; import com.google.common.collect.*;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.resolve.*; import org.jetbrains.jet.lang.resolve.*;
@@ -270,15 +267,21 @@ public class FunctionDescriptorUtil {
@Override @Override
public boolean isEmpty() { public boolean isEmpty() {
// TODO: not implemented return getFunctionDescriptors().isEmpty();
return false;
} }
@NotNull @NotNull
@Override @Override
public Set<FunctionDescriptor> getFunctionDescriptors() { public Set<FunctionDescriptor> getFunctionDescriptors() {
// TODO: not implemented Set<FunctionDescriptor> functionDescriptors = Sets.newHashSet(functionGroup.getFunctionDescriptors());
return null; for (Iterator<FunctionDescriptor> iterator = functionDescriptors.iterator(); iterator.hasNext(); ) {
FunctionDescriptor functionDescriptor = iterator.next();
if (!criterion.apply(functionDescriptor)) {
iterator.remove();
}
}
return functionDescriptors;
} }
}; };
} }
@@ -38,6 +38,7 @@ public class JetParsing extends AbstractJetParsing {
private static final TokenSet PARAMETER_NAME_RECOVERY_SET = TokenSet.create(COLON, EQ, COMMA, RPAR); private static final TokenSet PARAMETER_NAME_RECOVERY_SET = TokenSet.create(COLON, EQ, COMMA, RPAR);
private static final TokenSet NAMESPACE_NAME_RECOVERY_SET = TokenSet.create(DOT, EOL_OR_SEMICOLON); private static final TokenSet NAMESPACE_NAME_RECOVERY_SET = TokenSet.create(DOT, EOL_OR_SEMICOLON);
/*package*/ static final TokenSet TYPE_REF_FIRST = TokenSet.create(LBRACKET, IDENTIFIER, LBRACE, LPAR, CAPITALIZED_THIS_KEYWORD); /*package*/ static final TokenSet TYPE_REF_FIRST = TokenSet.create(LBRACKET, IDENTIFIER, LBRACE, LPAR, CAPITALIZED_THIS_KEYWORD);
private static final TokenSet RECEIVER_TYPE_TERMINATORS = TokenSet.create(DOT, SAFE_ACCESS);
public static JetParsing createForTopLevel(SemanticWhitespaceAwarePsiBuilder builder) { public static JetParsing createForTopLevel(SemanticWhitespaceAwarePsiBuilder builder) {
builder.setDebugMode(true); builder.setDebugMode(true);
@@ -725,35 +726,38 @@ public class JetParsing extends AbstractJetParsing {
// TODO: extract constant // TODO: extract constant
int lastDot = matchTokenStreamPredicate(new FirstBefore( int lastDot = matchTokenStreamPredicate(new FirstBefore(
new AbstractTokenStreamPredicate() { // new AbstractTokenStreamPredicate() {
@Override // @Override
public boolean matching(boolean topLevel) { // public boolean matching(boolean topLevel) {
return topLevel // return topLevel
&& at(DOT); // && at(DOT);
} // }
}, // },
new AtSet(DOT, SAFE_ACCESS),
new AbstractTokenStreamPredicate() { new AbstractTokenStreamPredicate() {
@Override @Override
public boolean matching(boolean topLevel) { public boolean matching(boolean topLevel) {
if (topLevel && (at(EQ) || at(COLON))) return true; if (topLevel && (at(EQ) || at(COLON))) return true;
if (topLevel && at(IDENTIFIER)) { if (topLevel && at(IDENTIFIER)) {
IElementType lookahead = lookahead(1); IElementType lookahead = lookahead(1);
return lookahead != LT && lookahead != DOT; return lookahead != LT && lookahead != DOT && lookahead != SAFE_ACCESS;
} }
return false; return false;
} }
})); }));
if (lastDot == -1) { parseReceiverType("property", propertyNameFollow, lastDot);
parseAttributeList();
expect(IDENTIFIER, "Expecting property name or receiver type", propertyNameFollow);
}
else {
createTruncatedBuilder(lastDot).parseTypeRef();
expect(DOT, "Expecting '.' before a property name", propertyNameFollow); // if (lastDot == -1) {
expect(IDENTIFIER, "Expecting property name", propertyNameFollow); // parseAttributeList();
} // expect(IDENTIFIER, "Expecting property name or receiver type", propertyNameFollow);
// }
// else {
// createTruncatedBuilder(lastDot).parseTypeRef();
//
// expect(DOT, "Expecting '.' before a property name", propertyNameFollow);
// expect(IDENTIFIER, "Expecting property name", propertyNameFollow);
// }
if (at(COLON)) { if (at(COLON)) {
advance(); // COLON advance(); // COLON
@@ -868,35 +872,8 @@ public class JetParsing extends AbstractJetParsing {
typeParameterListOccured = true; typeParameterListOccured = true;
} }
TokenSet receiverTypeTerminators = TokenSet.create(DOT, SAFE_ACCESS); int lastDot = findLastBefore(RECEIVER_TYPE_TERMINATORS, TokenSet.create(LPAR), true);
int lastDot = findLastBefore(receiverTypeTerminators, TokenSet.create(LPAR), true); parseReceiverType("function", TokenSet.create(LT, LPAR, COLON, EQ), lastDot);
if (lastDot == -1) { // There's no explicit receiver type specified
parseAttributeList();
expect(IDENTIFIER, "Expecting function name or receiver type");
} else {
PsiBuilder.Marker typeRefMarker = mark();
PsiBuilder.Marker nullableType = mark();
typeRefMarker = createTruncatedBuilder(lastDot).parseTypeRefContents(typeRefMarker);
if (at(SAFE_ACCESS)) {
nullableType.done(NULLABLE_TYPE);
}
else {
nullableType.drop();
}
typeRefMarker.done(TYPE_REFERENCE);
TokenSet functionNameFollow = TokenSet.create(LT, LPAR, COLON, EQ);
if (atSet(receiverTypeTerminators)) {
advance(); // expectation
}
else {
errorWithRecovery("Expecting '.' before a function name", functionNameFollow);
}
// expect(DOT, "Expecting '.' before a function name", functionNameFollow);
expect(IDENTIFIER, "Expecting function name", functionNameFollow);
}
TokenSet valueParametersFollow = TokenSet.create(COLON, EQ, LBRACE, SEMICOLON, RPAR); TokenSet valueParametersFollow = TokenSet.create(COLON, EQ, LBRACE, SEMICOLON, RPAR);
@@ -929,6 +906,38 @@ public class JetParsing extends AbstractJetParsing {
return FUN; return FUN;
} }
/*
* :
* (type "." | attributes)?
*/
private void parseReceiverType(String title, TokenSet nameFollow, int lastDot) {
if (lastDot == -1) { // There's no explicit receiver type specified
parseAttributeList();
expect(IDENTIFIER, "Expecting " + title + " name or receiver type", nameFollow);
} else {
PsiBuilder.Marker typeRefMarker = mark();
PsiBuilder.Marker nullableType = mark();
typeRefMarker = createTruncatedBuilder(lastDot).parseTypeRefContents(typeRefMarker);
if (at(SAFE_ACCESS)) {
nullableType.done(NULLABLE_TYPE);
}
else {
nullableType.drop();
}
typeRefMarker.done(TYPE_REFERENCE);
if (atSet(RECEIVER_TYPE_TERMINATORS)) {
advance(); // expectation
}
else {
errorWithRecovery("Expecting '.' before a " + title + " name", nameFollow);
}
expect(IDENTIFIER, "Expecting " + title + " name", nameFollow);
}
}
/* /*
* functionBody * functionBody
* : block * : block
@@ -12,20 +12,20 @@ import org.jetbrains.jet.lang.types.JetTypeChecker;
*/ */
public class ScopeWithReceiver extends JetScopeImpl { public class ScopeWithReceiver extends JetScopeImpl {
private final JetScope receiverTypeScope; private final JetType receiverType;
private final JetScope outerScope; private final JetScope outerScope;
private final JetTypeChecker typeChecker; private final JetTypeChecker typeChecker;
public ScopeWithReceiver(JetScope outerScope, JetType receiverType, JetTypeChecker typeChecker) { public ScopeWithReceiver(JetScope outerScope, JetType receiverType, JetTypeChecker typeChecker) {
this.outerScope = outerScope; this.outerScope = outerScope;
this.receiverTypeScope = receiverType.getMemberScope(); this.receiverType = receiverType;
this.typeChecker = typeChecker; this.typeChecker = typeChecker;
} }
@NotNull @NotNull
@Override @Override
public FunctionGroup getFunctionGroup(@NotNull String name) { public FunctionGroup getFunctionGroup(@NotNull String name) {
FunctionGroup functionGroup = receiverTypeScope.getFunctionGroup(name); FunctionGroup functionGroup = receiverType.getMemberScope().getFunctionGroup(name);
if (functionGroup.isEmpty()) { if (functionGroup.isEmpty()) {
return FunctionDescriptorUtil.filteredFunctionGroup(outerScope.getFunctionGroup(name), return FunctionDescriptorUtil.filteredFunctionGroup(outerScope.getFunctionGroup(name),
new Function<FunctionDescriptor, Boolean>() { new Function<FunctionDescriptor, Boolean>() {
@@ -37,7 +37,7 @@ public class ScopeWithReceiver extends JetScopeImpl {
return false; return false;
} }
// TODO : in case of inferred type arguments, substitute the receiver type first // TODO : in case of inferred type arguments, substitute the receiver type first
return typeChecker.isSubtypeOf(receiverTypeScope.getThisType(), functionReceiverType); return typeChecker.isSubtypeOf(receiverType, functionReceiverType);
} }
}); });
} }
@@ -51,7 +51,7 @@ public class ScopeWithReceiver extends JetScopeImpl {
@Override @Override
public VariableDescriptor getVariable(@NotNull String name) { public VariableDescriptor getVariable(@NotNull String name) {
VariableDescriptor variable = receiverTypeScope.getVariable(name); VariableDescriptor variable = receiverType.getMemberScope().getVariable(name);
if (variable != null) { if (variable != null) {
return variable; return variable;
} }
@@ -61,7 +61,7 @@ public class ScopeWithReceiver extends JetScopeImpl {
JetType receiverType = propertyDescriptor.getReceiverType(); JetType receiverType = propertyDescriptor.getReceiverType();
// TODO : in case of type arguments, substitute the receiver type first // TODO : in case of type arguments, substitute the receiver type first
if (receiverType != null if (receiverType != null
&& typeChecker.isSubtypeOf(receiverTypeScope.getThisType(), receiverType)) { && typeChecker.isSubtypeOf(receiverType, receiverType)) {
return variable; return variable;
} }
} }
@@ -70,13 +70,13 @@ public class ScopeWithReceiver extends JetScopeImpl {
@Override @Override
public NamespaceDescriptor getNamespace(@NotNull String name) { public NamespaceDescriptor getNamespace(@NotNull String name) {
return receiverTypeScope.getNamespace(name); return receiverType.getMemberScope().getNamespace(name);
} }
@NotNull @NotNull
@Override @Override
public JetType getThisType() { public JetType getThisType() {
return receiverTypeScope.getThisType(); return receiverType;
} }
@NotNull @NotNull
@@ -0,0 +1,2 @@
fun Int?.optint() : Unit
val Int?.optval : Unit
+2
View File
@@ -73,3 +73,5 @@ fun foo() {
val IList<T>.lastIndex : Int val IList<T>.lastIndex : Int
get() = this.size - 1 get() = this.size - 1
val Int?.opt : Int
+19 -1
View File
@@ -777,4 +777,22 @@ JetFile: Properties.jet
PsiElement(MINUS)('-') PsiElement(MINUS)('-')
PsiWhiteSpace(' ') PsiWhiteSpace(' ')
INTEGER_CONSTANT INTEGER_CONSTANT
PsiElement(INTEGER_LITERAL)('1') PsiElement(INTEGER_LITERAL)('1')
PsiWhiteSpace('\n\n')
PROPERTY
PsiElement(val)('val')
PsiWhiteSpace(' ')
TYPE_REFERENCE
NULLABLE_TYPE
USER_TYPE
REFERENCE_EXPRESSION
PsiElement(IDENTIFIER)('Int')
PsiElement(SAFE_ACCESS)('?.')
PsiElement(IDENTIFIER)('opt')
PsiWhiteSpace(' ')
PsiElement(COLON)(':')
PsiWhiteSpace(' ')
TYPE_REFERENCE
USER_TYPE
REFERENCE_EXPRESSION
PsiElement(IDENTIFIER)('Int')
+6 -2
View File
@@ -3,7 +3,7 @@ fun <~T~T, ~E~E> `T`T.foo(x : `E`E, y : `A`A) : `T`T {
y `+`plus 1 y `+`plus 1
y `+1`+ 1.0 y `+1`+ 1.0
this?.`-`minus<T>(this) this?.minus<T>(this)
this this
} }
@@ -11,6 +11,10 @@ fun <~T~T, ~E~E> `T`T.foo(x : `E`E, y : `A`A) : `T`T {
~A~class A ~A~class A
~+1~fun `A`A.plus(a : Any) { ~+1~fun `A`A.plus(a : Any) {
1.`foo`foo()
true.`!`foo()
1 1
} }
@@ -28,4 +32,4 @@ fun test() {
val <~TT~T> `TT`T.foo : `TT`T val <~TT~T> `TT`T.foo : `TT`T
fun Int.foo() = this`:std::Int` ~foo~fun Int.foo() = this`:std::Int`
@@ -161,7 +161,7 @@ public class ExpectedResolveData {
actualName = actual.toString(); actualName = actual.toString();
} }
} }
assertNotNull(reference); assertNotNull(element.getText(), reference);
if (expected instanceof JetParameter || actual instanceof JetParameter) { if (expected instanceof JetParameter || actual instanceof JetParameter) {
DeclarationDescriptor expectedDescriptor; DeclarationDescriptor expectedDescriptor;