diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/parsing/JetExpressionParsing.java b/compiler/frontend/src/org/jetbrains/jet/lang/parsing/JetExpressionParsing.java
index 1c6dbfb32d7..dba0dd21d60 100644
--- a/compiler/frontend/src/org/jetbrains/jet/lang/parsing/JetExpressionParsing.java
+++ b/compiler/frontend/src/org/jetbrains/jet/lang/parsing/JetExpressionParsing.java
@@ -372,7 +372,7 @@ public class JetExpressionParsing extends AbstractJetParsing {
/*
* callableReference
- * : userType? "::" SimpleName
+ * : (userType "?"*)? "::" SimpleName
* ;
*/
private boolean parseCallableReferenceExpression() {
@@ -381,7 +381,9 @@ public class JetExpressionParsing extends AbstractJetParsing {
if (!at(COLONCOLON)) {
PsiBuilder.Marker typeReference = mark();
myJetParsing.parseUserType();
+ typeReference = myJetParsing.parseNullableTypeSuffix(typeReference);
typeReference.done(TYPE_REFERENCE);
+
if (!at(COLONCOLON)) {
expression.rollbackTo();
return false;
diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/parsing/JetParsing.java b/compiler/frontend/src/org/jetbrains/jet/lang/parsing/JetParsing.java
index e14ec5a3e90..1644e65d41a 100644
--- a/compiler/frontend/src/org/jetbrains/jet/lang/parsing/JetParsing.java
+++ b/compiler/frontend/src/org/jetbrains/jet/lang/parsing/JetParsing.java
@@ -19,6 +19,7 @@ package org.jetbrains.jet.lang.parsing;
import com.intellij.lang.PsiBuilder;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
+import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.JetNodeType;
import org.jetbrains.jet.lexer.JetKeywordToken;
@@ -1478,14 +1479,7 @@ public class JetParsing extends AbstractJetParsing {
TokenSet.create(EQ, COMMA, GT, RBRACKET, DOT, RPAR, RBRACE, LBRACE, SEMICOLON), extraRecoverySet));
}
- while (at(QUEST)) {
- PsiBuilder.Marker precede = typeRefMarker.precede();
-
- advance(); // QUEST
- typeRefMarker.done(NULLABLE_TYPE);
-
- typeRefMarker = precede;
- }
+ typeRefMarker = parseNullableTypeSuffix(typeRefMarker);
if (at(DOT)) {
// This is a receiver for a function type
@@ -1513,6 +1507,17 @@ public class JetParsing extends AbstractJetParsing {
return typeRefMarker;
}
+ @NotNull
+ PsiBuilder.Marker parseNullableTypeSuffix(@NotNull PsiBuilder.Marker typeRefMarker) {
+ while (at(QUEST)) {
+ PsiBuilder.Marker precede = typeRefMarker.precede();
+ advance(); // QUEST
+ typeRefMarker.done(NULLABLE_TYPE);
+ typeRefMarker = precede;
+ }
+ return typeRefMarker;
+ }
+
/*
* userType
* : ("package" ".")? simpleUserType{"."}
diff --git a/compiler/testData/diagnostics/testsWithStdLib/callableReference/function/extensionOnNullable.kt b/compiler/testData/diagnostics/testsWithStdLib/callableReference/function/extensionOnNullable.kt
new file mode 100644
index 00000000000..f6fb90d4f42
--- /dev/null
+++ b/compiler/testData/diagnostics/testsWithStdLib/callableReference/function/extensionOnNullable.kt
@@ -0,0 +1,8 @@
+class A {
+ fun foo() {}
+}
+
+fun A?.foo() {}
+
+val f = A::foo : KMemberFunction0
+val g = A?::foo : KExtensionFunction0
diff --git a/compiler/testData/psi/DoubleColon.kt b/compiler/testData/psi/DoubleColon.kt
index bf44a1c132d..acb42293fbe 100644
--- a/compiler/testData/psi/DoubleColon.kt
+++ b/compiler/testData/psi/DoubleColon.kt
@@ -23,6 +23,11 @@ fun ok() {
(a::b)()
a.(b::c)()
a.b::c()
+
+ a?::b
+ a??::b
+ a?::c
+ a?::d
}
fun err0() {
diff --git a/compiler/testData/psi/DoubleColon.txt b/compiler/testData/psi/DoubleColon.txt
index a1e37dc967d..278f3d7554f 100644
--- a/compiler/testData/psi/DoubleColon.txt
+++ b/compiler/testData/psi/DoubleColon.txt
@@ -392,6 +392,78 @@ JetFile: DoubleColon.kt
PsiErrorElement:Unexpected tokens (use ';' to separate expressions on the same line)
PsiElement(LPAR)('(')
PsiElement(RPAR)(')')
+ PsiWhiteSpace('\n\n ')
+ CALLABLE_REFERENCE_EXPRESSION
+ TYPE_REFERENCE
+ NULLABLE_TYPE
+ USER_TYPE
+ REFERENCE_EXPRESSION
+ PsiElement(IDENTIFIER)('a')
+ PsiElement(QUEST)('?')
+ PsiElement(COLONCOLON)('::')
+ REFERENCE_EXPRESSION
+ PsiElement(IDENTIFIER)('b')
+ PsiWhiteSpace('\n ')
+ CALLABLE_REFERENCE_EXPRESSION
+ TYPE_REFERENCE
+ NULLABLE_TYPE
+ NULLABLE_TYPE
+ USER_TYPE
+ REFERENCE_EXPRESSION
+ PsiElement(IDENTIFIER)('a')
+ PsiElement(QUEST)('?')
+ PsiElement(QUEST)('?')
+ PsiElement(COLONCOLON)('::')
+ REFERENCE_EXPRESSION
+ PsiElement(IDENTIFIER)('b')
+ PsiWhiteSpace('\n ')
+ CALLABLE_REFERENCE_EXPRESSION
+ TYPE_REFERENCE
+ NULLABLE_TYPE
+ USER_TYPE
+ REFERENCE_EXPRESSION
+ PsiElement(IDENTIFIER)('a')
+ TYPE_ARGUMENT_LIST
+ PsiElement(LT)('<')
+ TYPE_PROJECTION
+ TYPE_REFERENCE
+ USER_TYPE
+ REFERENCE_EXPRESSION
+ PsiElement(IDENTIFIER)('b')
+ PsiElement(GT)('>')
+ PsiElement(QUEST)('?')
+ PsiElement(COLONCOLON)('::')
+ REFERENCE_EXPRESSION
+ PsiElement(IDENTIFIER)('c')
+ PsiWhiteSpace('\n ')
+ CALLABLE_REFERENCE_EXPRESSION
+ TYPE_REFERENCE
+ NULLABLE_TYPE
+ USER_TYPE
+ REFERENCE_EXPRESSION
+ PsiElement(IDENTIFIER)('a')
+ TYPE_ARGUMENT_LIST
+ PsiElement(LT)('<')
+ TYPE_PROJECTION
+ TYPE_REFERENCE
+ NULLABLE_TYPE
+ USER_TYPE
+ REFERENCE_EXPRESSION
+ PsiElement(IDENTIFIER)('b')
+ PsiElement(QUEST)('?')
+ PsiElement(COMMA)(',')
+ TYPE_PROJECTION
+ TYPE_REFERENCE
+ NULLABLE_TYPE
+ USER_TYPE
+ REFERENCE_EXPRESSION
+ PsiElement(IDENTIFIER)('c')
+ PsiElement(QUEST)('?')
+ PsiElement(GT)('>')
+ PsiElement(QUEST)('?')
+ PsiElement(COLONCOLON)('::')
+ REFERENCE_EXPRESSION
+ PsiElement(IDENTIFIER)('d')
PsiWhiteSpace('\n')
PsiElement(RBRACE)('}')
PsiWhiteSpace('\n\n')
diff --git a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestWithStdLibGenerated.java b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestWithStdLibGenerated.java
index 186d535e122..ed8963048ac 100644
--- a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestWithStdLibGenerated.java
+++ b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestWithStdLibGenerated.java
@@ -192,6 +192,11 @@ public class JetDiagnosticsTestWithStdLibGenerated extends AbstractJetDiagnostic
doTest("compiler/testData/diagnostics/testsWithStdLib/callableReference/function/extensionInClassDisallowed.kt");
}
+ @TestMetadata("extensionOnNullable.kt")
+ public void testExtensionOnNullable() throws Exception {
+ doTest("compiler/testData/diagnostics/testsWithStdLib/callableReference/function/extensionOnNullable.kt");
+ }
+
@TestMetadata("genericClassFromTopLevel.kt")
public void testGenericClassFromTopLevel() throws Exception {
doTest("compiler/testData/diagnostics/testsWithStdLib/callableReference/function/genericClassFromTopLevel.kt");
diff --git a/grammar/src/expressions.grm b/grammar/src/expressions.grm
index 5b4bae7859e..39e547baaf0 100644
--- a/grammar/src/expressions.grm
+++ b/grammar/src/expressions.grm
@@ -104,7 +104,7 @@ postfixUnaryExpression
// TODO: callSuffix is forbidden after callableReference, since parentheses will be used to provide parameter types
callableReference
- : userType? "::" SimpleName
+ : (userType "?"*)? "::" SimpleName
;
// !!! When you add here, remember to update the FIRST set in the parser