Constructor references with type parameters are not recognized as PsiMethodReferenceExpression

StatementParser used to ignore type parameters to constructor references
and it prevents the tokens of such statements from being grouped under
the PsiMethodReferenceExpression node in the PSI tree.

Since constructor references can have type parameters one might look
like a variable declaration. The parser didn't take this fact into
account and fails due to "::" after closing ">" of type arguments.

This patch adds a new check in StatementParser#parseStatement after
the attempt of parsing of a declaration statement fails to see if the
type declaration follows by "::" which is a marker that the examined
line might be a constructor reference.

Signed-off-by: Nikita Eshkeev <neshkeev@yandex.ru>

GitOrigin-RevId: 968f4884c45c922ce2fd6a8e3614cca01423d5b3
This commit is contained in:
Nikita Eshkeev
2020-01-29 20:20:18 +01:00
committed by intellij-monorepo-bot
parent b6c9e5cce3
commit 707933fffd
4 changed files with 57 additions and 6 deletions

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.lang.java.parser;
import com.intellij.codeInsight.daemon.JavaErrorBundle;
@@ -160,16 +160,31 @@ public class StatementParser {
skipQualifiedName(builder);
IElementType suspectedLT = builder.getTokenType(), next = builder.lookAhead(1);
refPos.rollbackTo();
if (suspectedLT == JavaTokenType.LT || suspectedLT == JavaTokenType.DOT && next == JavaTokenType.AT) {
final boolean constructorRef;
PsiBuilder.Marker declStatement = builder.mark();
PsiBuilder.Marker decl = myParser.getDeclarationParser().parse(builder, DeclarationParser.Context.CODE_BLOCK);
if (decl == null) {
PsiBuilder.Marker marker = myParser.getReferenceParser().parseType(builder, 0);
error(builder, JavaErrorBundle.message("expected.identifier"));
if (marker == null) builder.advanceLexer();
// if the type declaration ends with "::" then it is a method reference to a constructor
constructorRef = builder.getTokenType() == JavaTokenType.DOUBLE_COLON;
if (!constructorRef) {
error(builder, JavaErrorBundle.message("expected.identifier"));
if (marker == null) builder.advanceLexer();
}
}
done(declStatement, JavaElementType.DECLARATION_STATEMENT);
return declStatement;
else {
constructorRef = false;
}
if (!constructorRef) {
done(declStatement, JavaElementType.DECLARATION_STATEMENT);
return declStatement;
}
declStatement.rollbackTo();
}
}

View File

@@ -0,0 +1,13 @@
PsiJavaFile:ConstructorRef.java
PsiExpressionStatement
PsiMethodReferenceExpression
PsiReferenceExpression:Foo
PsiReferenceParameterList
<empty list>
PsiIdentifier:Foo('Foo')
PsiJavaToken:DOUBLE_COLON('::')
PsiReferenceParameterList
<empty list>
PsiKeyword:new('new')
PsiErrorElement:';' expected
<empty list>

View File

@@ -0,0 +1,20 @@
PsiJavaFile:ConstructorWithTypeParamsRef.java
PsiExpressionStatement
PsiMethodReferenceExpression
PsiTypeElement:Foo<Integer>
PsiJavaCodeReferenceElement:Foo<Integer>
PsiIdentifier:Foo('Foo')
PsiReferenceParameterList
PsiJavaToken:LT('<')
PsiTypeElement:Integer
PsiJavaCodeReferenceElement:Integer
PsiIdentifier:Integer('Integer')
PsiReferenceParameterList
<empty list>
PsiJavaToken:GT('>')
PsiJavaToken:DOUBLE_COLON('::')
PsiReferenceParameterList
<empty list>
PsiKeyword:new('new')
PsiErrorElement:';' expected
<empty list>

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.java.parser.partial;
import com.intellij.java.parser.JavaParsingTestCase;
@@ -185,6 +185,9 @@ public class StatementParserTest extends JavaParsingTestCase {
public void testWhileIncomplete4() { doParserTest("while(cond)"); }
public void testWhileIncomplete5() { doParserTest("while() foo();"); }
public void testConstructorRef() { doParserTest("Foo::new"); }
public void testConstructorWithTypeParamsRef() { doParserTest("Foo<Integer>::new"); }
private void doBlockParserTest(String text) {
doParserTest(text, builder -> JavaParser.INSTANCE.getStatementParser().parseCodeBlockDeep(builder, true));
}