JET-39 Process ?. correctly in the receiver types
This commit is contained in:
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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')
|
||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user