Lambda expressions support: second round

- typed parameters
- type parameter list
- resolve inside lambda
This commit is contained in:
Roman Shevchenko
2012-06-08 14:34:03 +04:00
parent 54e2d2c005
commit ee50d99da0
15 changed files with 444 additions and 58 deletions
@@ -22,6 +22,14 @@ import org.jetbrains.annotations.Nullable;
* Represents a Java lambda expression.
*/
public interface PsiLambdaExpression extends PsiExpression {
/**
* Returns this lambda expression's type parameter list (if any).
*
* @return type parameter list or null.
*/
@Nullable
PsiTypeParameterList getTypeParameterList();
/**
* Returns this lambda expression's parameter list.
*
@@ -31,7 +39,8 @@ public interface PsiLambdaExpression extends PsiExpression {
PsiParameterList getParameterList();
/**
* Returns PSI element representing lambda expression body: {@link PsiCodeBlock}, {@link PsiExpression}, or null if none.
* Returns PSI element representing lambda expression body: {@link PsiCodeBlock}, {@link PsiExpression},
* or null if the expression is incomplete.
*
* @return lambda expression body.
*/
@@ -163,7 +163,7 @@ public class ExpressionParser {
return parseBinary(builder, ExprType.UNARY, MULTIPLICATIVE_OPS);
case UNARY:
return parseUnary(builder);
return parseUnary(builder, false);
case TYPE:
return myParser.getReferenceParser().parseType(builder, ReferenceParser.EAT_LAST_DOT | ReferenceParser.WILDCARD);
@@ -246,14 +246,14 @@ public class ExpressionParser {
}
@Nullable
private PsiBuilder.Marker parseUnary(final PsiBuilder builder) {
private PsiBuilder.Marker parseUnary(final PsiBuilder builder, final boolean afterCast) {
final IElementType tokenType = builder.getTokenType();
if (PREFIX_OPS.contains(tokenType)) {
final PsiBuilder.Marker unary = builder.mark();
builder.advanceLexer();
final PsiBuilder.Marker operand = parseUnary(builder);
final PsiBuilder.Marker operand = parseUnary(builder, false);
if (operand == null) {
error(builder, JavaErrorMessages.message("expected.expression"));
}
@@ -269,19 +269,19 @@ public class ExpressionParser {
myParser.getReferenceParser().parseTypeInfo(builder, ReferenceParser.EAT_LAST_DOT | ReferenceParser.WILDCARD);
if (typeInfo == null || !expect(builder, JavaTokenType.RPARENTH)) {
typeCast.rollbackTo();
return parsePostfix(builder);
return parsePostfix(builder, false);
}
if (PREF_ARITHMETIC_OPS.contains(builder.getTokenType()) && !typeInfo.isPrimitive) {
typeCast.rollbackTo();
return parsePostfix(builder);
return parsePostfix(builder, false);
}
final PsiBuilder.Marker expr = parseUnary(builder);
final PsiBuilder.Marker expr = parseUnary(builder, true);
if (expr == null) {
if (!typeInfo.isParameterized) { // cannot parse correct parenthesized expression after correct parameterized type
typeCast.rollbackTo();
return parsePostfix(builder);
return parsePostfix(builder, false);
}
else {
error(builder, JavaErrorMessages.message("expected.expression"));
@@ -292,13 +292,13 @@ public class ExpressionParser {
return typeCast;
}
else {
return parsePostfix(builder);
return parsePostfix(builder, afterCast);
}
}
@Nullable
private PsiBuilder.Marker parsePostfix(final PsiBuilder builder) {
PsiBuilder.Marker operand = parsePrimary(builder, null, -1);
private PsiBuilder.Marker parsePostfix(final PsiBuilder builder, final boolean afterCast) {
PsiBuilder.Marker operand = parsePrimary(builder, null, -1, afterCast);
if (operand == null) return null;
while (POSTFIX_OPS.contains(builder.getTokenType())) {
@@ -315,10 +315,13 @@ public class ExpressionParser {
// todo[r.sh] make 'this', 'super' and 'class' reference expressions
@Nullable
private PsiBuilder.Marker parsePrimary(final PsiBuilder builder, @Nullable final BreakPoint breakPoint, final int breakOffset) {
private PsiBuilder.Marker parsePrimary(final PsiBuilder builder,
@Nullable final BreakPoint breakPoint,
final int breakOffset,
final boolean afterCast) {
PsiBuilder.Marker startMarker = builder.mark();
PsiBuilder.Marker expr = parsePrimaryExpressionStart(builder);
PsiBuilder.Marker expr = parsePrimaryExpressionStart(builder, afterCast);
if (expr == null) {
startMarker.drop();
return null;
@@ -346,7 +349,7 @@ public class ExpressionParser {
final PsiBuilder.Marker classObjAccess = parseClassAccessOrMethodReference(builder);
if (classObjAccess == null || builder.getCurrentOffset() < offset) {
copy.rollbackTo();
return parsePrimary(builder, BreakPoint.P1, offset);
return parsePrimary(builder, BreakPoint.P1, offset, false);
}
startMarker = copy;
@@ -370,13 +373,13 @@ public class ExpressionParser {
final PsiBuilder.Marker ref = myParser.getReferenceParser().parseJavaCodeReference(builder, false, true, false, false, false);
if (ref == null || builder.getTokenType() != JavaTokenType.DOT || builder.getCurrentOffset() != dotOffset) {
copy.rollbackTo();
return parsePrimary(builder, BreakPoint.P2, offset);
return parsePrimary(builder, BreakPoint.P2, offset, false);
}
builder.advanceLexer();
if (builder.getTokenType() != dotTokenType) {
copy.rollbackTo();
return parsePrimary(builder, BreakPoint.P2, offset);
return parsePrimary(builder, BreakPoint.P2, offset, false);
}
builder.advanceLexer();
@@ -417,7 +420,7 @@ public class ExpressionParser {
final PsiBuilder.Marker copy = startMarker.precede();
startMarker.rollbackTo();
final PsiBuilder.Marker qualifier = parsePrimaryExpressionStart(builder);
final PsiBuilder.Marker qualifier = parsePrimaryExpressionStart(builder, false);
if (qualifier != null) {
final PsiBuilder.Marker refExpr = qualifier.precede();
if (builder.getTokenType() == JavaTokenType.DOT) {
@@ -433,7 +436,7 @@ public class ExpressionParser {
}
copy.rollbackTo();
return parsePrimary(builder, BreakPoint.P3, -1);
return parsePrimary(builder, BreakPoint.P3, -1, false);
}
else {
startMarker.drop();
@@ -462,7 +465,7 @@ public class ExpressionParser {
final PsiBuilder.Marker classObjAccess = parseClassAccessOrMethodReference(builder);
if (classObjAccess == null || builder.getCurrentOffset() <= pos) {
copy.rollbackTo();
return parsePrimary(builder, BreakPoint.P4, -1);
return parsePrimary(builder, BreakPoint.P4, -1, false);
}
startMarker = copy;
@@ -502,7 +505,7 @@ public class ExpressionParser {
}
@Nullable
private PsiBuilder.Marker parsePrimaryExpressionStart(final PsiBuilder builder) {
private PsiBuilder.Marker parsePrimaryExpressionStart(final PsiBuilder builder, final boolean afterCast) {
IElementType tokenType = builder.getTokenType();
if (LITERALS.contains(tokenType)) {
@@ -512,21 +515,21 @@ public class ExpressionParser {
return literal;
}
if (tokenType == JavaTokenType.LBRACE) {
return parseArrayInitializer(builder);
}
if (tokenType == JavaTokenType.NEW_KEYWORD) {
return parseNew(builder, null);
}
if (tokenType == JavaTokenType.LPARENTH) {
final PsiBuilder.Marker lambda = parseLambdaAfterParenth(builder, null);
if (lambda != null) {
return lambda;
}
final PsiBuilder.Marker parenth = builder.mark();
final IElementType nextToken1 = builder.lookAhead(1);
final IElementType nextToken2 = builder.lookAhead(2);
if (nextToken1 == JavaTokenType.IDENTIFIER) {
if (nextToken2 == JavaTokenType.COMMA ||
nextToken2 == JavaTokenType.RPARENTH && builder.lookAhead(3) == JavaTokenType.ARROW) {
return parseLambdaExpression(builder, parenth, false);
}
}
else if (nextToken1 == JavaTokenType.RPARENTH && nextToken2 == JavaTokenType.ARROW) {
return parseLambdaExpression(builder, parenth, false);
}
builder.advanceLexer();
final PsiBuilder.Marker inner = parse(builder);
@@ -544,17 +547,15 @@ public class ExpressionParser {
return parenth;
}
if (tokenType == JavaTokenType.LBRACE) {
return parseArrayInitializer(builder);
}
if (TYPE_START.contains(tokenType)) {
final PsiBuilder.Marker mark = builder.mark();
final ReferenceParser.TypeInfo typeInfo = myParser.getReferenceParser().parseTypeInfo(builder, 0);
if (typeInfo != null && (typeInfo.isPrimitive || !typeInfo.hasErrors && typeInfo.isParameterized)) {
final PsiBuilder.Marker result = continueClassAccessOrMethodReference(builder, mark, typeInfo.isPrimitive);
if (result != null) return result;
if (result != null) {
return result;
}
}
mark.rollbackTo();
@@ -568,7 +569,7 @@ public class ExpressionParser {
if (tokenType == JavaTokenType.IDENTIFIER) {
if (builder.lookAhead(1) == JavaTokenType.ARROW) {
return parseLambdaExpression(builder, builder.mark(), false);
return parseLambdaExpression(builder, false, null);
}
final PsiBuilder.Marker refExpr;
@@ -596,6 +597,29 @@ public class ExpressionParser {
if (tokenType == JavaTokenType.LT) {
expr = builder.mark();
if (!afterCast) {
final PsiBuilder.Marker typeParams = myParser.getReferenceParser().parseTypeParameters(builder, true);
if (typeParams != null) {
tokenType = builder.getTokenType();
PsiBuilder.Marker lambda = null;
if (tokenType == JavaTokenType.LPARENTH) {
lambda = parseLambdaAfterParenth(builder, typeParams);
}
else if (tokenType == JavaTokenType.IDENTIFIER && builder.lookAhead(1) == JavaTokenType.ARROW) {
lambda = parseLambdaExpression(builder, false, typeParams);
}
if (lambda != null) {
expr.drop();
return lambda;
}
expr.rollbackTo();
expr = builder.mark();
}
}
if (!myParser.getReferenceParser().parseReferenceParameterList(builder, false, false)) {
expr.rollbackTo();
return null;
@@ -622,10 +646,6 @@ public class ExpressionParser {
return expr;
}
if (tokenType == JavaTokenType.NEW_KEYWORD) {
return parseNew(builder, null);
}
return null;
}
@@ -827,15 +847,75 @@ public class ExpressionParser {
return start;
}
@NotNull
private PsiBuilder.Marker parseLambdaExpression(final PsiBuilder builder, final PsiBuilder.Marker start, final boolean typed) {
myParser.getDeclarationParser().parseLambdaParameterList(builder, typed);
return parseLambdaExpressionFromArrow(builder, start);
@Nullable
private PsiBuilder.Marker parseLambdaAfterParenth(final PsiBuilder builder, @Nullable final PsiBuilder.Marker typeList) {
final boolean isLambda;
final boolean isTyped;
final IElementType nextToken1 = builder.lookAhead(1);
final IElementType nextToken2 = builder.lookAhead(2);
if (nextToken1 == JavaTokenType.RPARENTH && nextToken2 == JavaTokenType.ARROW) {
isLambda = true;
isTyped = false;
}
else if (nextToken1 == JavaTokenType.AT ||
ElementType.MODIFIER_BIT_SET.contains(nextToken1) || ElementType.PRIMITIVE_TYPE_BIT_SET.contains(nextToken1)) {
isLambda = true;
isTyped = true;
}
else if (nextToken1 == JavaTokenType.IDENTIFIER) {
if (nextToken2 == JavaTokenType.COMMA || nextToken2 == JavaTokenType.RPARENTH && builder.lookAhead(3) == JavaTokenType.ARROW) {
isLambda = true;
isTyped = false;
}
else if (nextToken2 == JavaTokenType.ARROW) {
isLambda = false;
isTyped = false;
}
else {
boolean arrow = false;
final PsiBuilder.Marker marker = builder.mark();
while (!builder.eof()) {
builder.advanceLexer();
final IElementType tokenType = builder.getTokenType();
if (tokenType == JavaTokenType.ARROW) {
arrow = true;
break;
}
if (tokenType == JavaTokenType.RPARENTH) {
arrow = builder.lookAhead(1) == JavaTokenType.ARROW;
break;
}
else if (tokenType == JavaTokenType.LPARENTH || tokenType == JavaTokenType.SEMICOLON ||
tokenType == JavaTokenType.LBRACE || tokenType == JavaTokenType.RBRACE) {
break;
}
}
marker.rollbackTo();
isLambda = arrow;
isTyped = true;
}
}
else {
isLambda = false;
isTyped = false;
}
return isLambda ? parseLambdaExpression(builder, isTyped, typeList) : null;
}
@NotNull
private PsiBuilder.Marker parseLambdaExpressionFromArrow(final PsiBuilder builder, final PsiBuilder.Marker start) {
builder.advanceLexer();
@Nullable
private PsiBuilder.Marker parseLambdaExpression(final PsiBuilder builder, final boolean typed, @Nullable final PsiBuilder.Marker typeList) {
final PsiBuilder.Marker start = typeList != null ? typeList.precede() : builder.mark();
myParser.getDeclarationParser().parseLambdaParameterList(builder, typed);
if (!expect(builder, JavaTokenType.ARROW)) {
start.rollbackTo();
return null;
}
final PsiBuilder.Marker body;
if (builder.getTokenType() == JavaTokenType.LBRACE) {
@@ -297,6 +297,13 @@ public class ReferenceParser {
@NotNull
public PsiBuilder.Marker parseTypeParameters(final PsiBuilder builder) {
final PsiBuilder.Marker marker = parseTypeParameters(builder, false);
assert marker != null;
return marker;
}
@Nullable
public PsiBuilder.Marker parseTypeParameters(final PsiBuilder builder, final boolean stopOnErrors) {
final PsiBuilder.Marker list = builder.mark();
if (!expect(builder, JavaTokenType.LT)) {
list.done(JavaElementType.TYPE_PARAMETER_LIST);
@@ -306,6 +313,10 @@ public class ReferenceParser {
while (true) {
final PsiBuilder.Marker param = parseTypeParameter(builder);
if (param == null) {
if (stopOnErrors) {
list.rollbackTo();
return null;
}
error(builder, JavaErrorMessages.message("expected.type.parameter"));
}
if (!expect(builder, JavaTokenType.COMMA)) {
@@ -314,6 +325,11 @@ public class ReferenceParser {
}
if (!expect(builder, JavaTokenType.GT)) {
if (stopOnErrors) {
list.rollbackTo();
return null;
}
// hack for completion
if (builder.getTokenType() == JavaTokenType.IDENTIFIER) {
if (builder.lookAhead(1) == JavaTokenType.GT) {
@@ -184,14 +184,39 @@ public class PsiImplUtil {
@NotNull final ResolveState state,
final PsiElement lastParent,
@NotNull final PsiElement place) {
final boolean fromBody = lastParent instanceof PsiCodeBlock;
final PsiTypeParameterList typeParameterList = method.getTypeParameterList();
final PsiParameterList parameterList = method.getParameterList();
return processDeclarationsInMethodLike(method, processor, state, fromBody, place, typeParameterList, parameterList);
}
public static boolean processDeclarationsInLambda(@NotNull final PsiLambdaExpression lambda,
@NotNull final PsiScopeProcessor processor,
@NotNull final ResolveState state,
final PsiElement lastParent,
@NotNull final PsiElement place) {
final boolean fromBody = lastParent != null && lastParent == lambda.getBody();
final PsiTypeParameterList typeParameterList = lambda.getTypeParameterList();
final PsiParameterList parameterList = lambda.getParameterList();
return processDeclarationsInMethodLike(lambda, processor, state, fromBody, place, typeParameterList, parameterList);
}
private static boolean processDeclarationsInMethodLike(@NotNull final PsiElement element,
@NotNull final PsiScopeProcessor processor,
@NotNull final ResolveState state,
final boolean fromBody,
@NotNull final PsiElement place,
@Nullable final PsiTypeParameterList typeParameterList,
@NotNull final PsiParameterList parameterList) {
processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, element);
final ElementClassHint hint = processor.getHint(ElementClassHint.KEY);
processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, method);
if (hint == null || hint.shouldProcess(ElementClassHint.DeclarationKind.CLASS)) {
final PsiTypeParameterList list = method.getTypeParameterList();
if (list != null && !list.processDeclarations(processor, state, null, place)) return false;
if (typeParameterList != null && !typeParameterList.processDeclarations(processor, state, null, place)) return false;
}
if (lastParent instanceof PsiCodeBlock) {
final PsiParameter[] parameters = method.getParameterList().getParameters();
if (fromBody) {
final PsiParameter[] parameters = parameterList.getParameters();
for (PsiParameter parameter : parameters) {
if (!processor.execute(parameter, state)) return false;
}
@@ -16,7 +16,9 @@
package com.intellij.psi.impl.source.tree.java;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiImplUtil;
import com.intellij.psi.impl.source.tree.JavaElementType;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
@@ -25,16 +27,22 @@ public class PsiLambdaExpressionImpl extends ExpressionPsiElement implements Psi
super(JavaElementType.LAMBDA_EXPRESSION);
}
@Override
public PsiTypeParameterList getTypeParameterList() {
final PsiElement element = getFirstChild();
return element instanceof PsiTypeParameterList ? (PsiTypeParameterList)element : null;
}
@NotNull
@Override
public PsiParameterList getParameterList() {
return PsiTreeUtil.getRequiredChildOfType(this, PsiParameterList.class);
}
@SuppressWarnings("unchecked")
@Override
public PsiElement getBody() {
return PsiTreeUtil.getChildOfAnyType(this, PsiExpression.class, PsiCodeBlock.class);
final PsiElement element = getLastChild();
return element instanceof PsiExpression || element instanceof PsiCodeBlock ? element : null;
}
@Override
@@ -52,6 +60,14 @@ public class PsiLambdaExpressionImpl extends ExpressionPsiElement implements Psi
}
}
@Override
public boolean processDeclarations(@NotNull final PsiScopeProcessor processor,
@NotNull final ResolveState state,
final PsiElement lastParent,
@NotNull final PsiElement place) {
return PsiImplUtil.processDeclarationsInLambda(this, processor, state, lastParent, place);
}
@Override
public String toString() {
return "PsiLambdaExpression:" + getText();
@@ -14,15 +14,27 @@
* limitations under the License.
*/
import java.util.*;
class C {
interface Simplest {
void m();
}
void simplest() { }
void use(Simplest s) { }
interface IntParser {
int parse(String s);
}
interface ListProducer<T> {
List<T> produce();
}
void test() {
Simplest simplest = <weak_warning descr="Lambda expressions type check is not yet implemented">() -> { }</weak_warning>;
use(<weak_warning descr="Lambda expressions type check is not yet implemented">() -> { }</weak_warning>);
IntParser intParser = <weak_warning descr="Lambda expressions type check is not yet implemented">(String s) -> Integer.parseInt(s)</weak_warning>;
ListProducer<String> listProducer = <weak_warning descr="Lambda expressions type check is not yet implemented"><T>() -> new ArrayList<T>()</weak_warning>;
}
}
@@ -0,0 +1,33 @@
PsiJavaFile:LambdaExpression12.java
PsiLambdaExpression:<T>() -> new C<T>()
PsiTypeParameterList
PsiJavaToken:LT('<')
PsiTypeParameter:T
PsiIdentifier:T('T')
PsiElement(EXTENDS_BOUND_LIST)
<empty list>
PsiJavaToken:GT('>')
PsiParameterList:()
PsiJavaToken:LPARENTH('(')
PsiJavaToken:RPARENTH(')')
PsiWhiteSpace(' ')
PsiJavaToken:ARROW('->')
PsiWhiteSpace(' ')
PsiNewExpression:new C<T>()
PsiKeyword:new('new')
PsiReferenceParameterList
<empty list>
PsiWhiteSpace(' ')
PsiJavaCodeReferenceElement:C<T>
PsiIdentifier:C('C')
PsiReferenceParameterList
PsiJavaToken:LT('<')
PsiTypeElement:T
PsiJavaCodeReferenceElement:T
PsiIdentifier:T('T')
PsiReferenceParameterList
<empty list>
PsiJavaToken:GT('>')
PsiExpressionList
PsiJavaToken:LPARENTH('(')
PsiJavaToken:RPARENTH(')')
@@ -0,0 +1,21 @@
PsiJavaFile:LambdaExpression13.java
PsiLambdaExpression:<T>t -> t
PsiTypeParameterList
PsiJavaToken:LT('<')
PsiTypeParameter:T
PsiIdentifier:T('T')
PsiElement(EXTENDS_BOUND_LIST)
<empty list>
PsiJavaToken:GT('>')
PsiParameterList:t
PsiParameter:t
PsiModifierList:
<empty list>
PsiIdentifier:t('t')
PsiWhiteSpace(' ')
PsiJavaToken:ARROW('->')
PsiWhiteSpace(' ')
PsiReferenceExpression:t
PsiReferenceParameterList
<empty list>
PsiIdentifier:t('t')
@@ -0,0 +1,22 @@
PsiJavaFile:LambdaExpression14.java
PsiLambdaExpression:(String t) -> t
PsiParameterList:(String t)
PsiJavaToken:LPARENTH('(')
PsiParameter:t
PsiModifierList:
<empty list>
PsiTypeElement:String
PsiJavaCodeReferenceElement:String
PsiIdentifier:String('String')
PsiReferenceParameterList
<empty list>
PsiWhiteSpace(' ')
PsiIdentifier:t('t')
PsiJavaToken:RPARENTH(')')
PsiWhiteSpace(' ')
PsiJavaToken:ARROW('->')
PsiWhiteSpace(' ')
PsiReferenceExpression:t
PsiReferenceParameterList
<empty list>
PsiIdentifier:t('t')
@@ -0,0 +1,36 @@
PsiJavaFile:LambdaExpression15.java
PsiLambdaExpression:(int a, int b) -> a + b
PsiParameterList:(int a, int b)
PsiJavaToken:LPARENTH('(')
PsiParameter:a
PsiModifierList:
<empty list>
PsiTypeElement:int
PsiKeyword:int('int')
PsiWhiteSpace(' ')
PsiIdentifier:a('a')
PsiJavaToken:COMMA(',')
PsiWhiteSpace(' ')
PsiParameter:b
PsiModifierList:
<empty list>
PsiTypeElement:int
PsiKeyword:int('int')
PsiWhiteSpace(' ')
PsiIdentifier:b('b')
PsiJavaToken:RPARENTH(')')
PsiWhiteSpace(' ')
PsiJavaToken:ARROW('->')
PsiWhiteSpace(' ')
PsiBinaryExpression:a + b
PsiReferenceExpression:a
PsiReferenceParameterList
<empty list>
PsiIdentifier:a('a')
PsiWhiteSpace(' ')
PsiJavaToken:PLUS('+')
PsiWhiteSpace(' ')
PsiReferenceExpression:b
PsiReferenceParameterList
<empty list>
PsiIdentifier:b('b')
@@ -0,0 +1,20 @@
PsiJavaFile:LambdaExpression16.java
PsiLambdaExpression:(final int x) -> x
PsiParameterList:(final int x)
PsiJavaToken:LPARENTH('(')
PsiParameter:x
PsiModifierList:final
PsiKeyword:final('final')
PsiWhiteSpace(' ')
PsiTypeElement:int
PsiKeyword:int('int')
PsiWhiteSpace(' ')
PsiIdentifier:x('x')
PsiJavaToken:RPARENTH(')')
PsiWhiteSpace(' ')
PsiJavaToken:ARROW('->')
PsiWhiteSpace(' ')
PsiReferenceExpression:x
PsiReferenceParameterList
<empty list>
PsiIdentifier:x('x')
@@ -0,0 +1,23 @@
PsiJavaFile:LambdaExpression17.java
PsiLambdaExpression:(String s -> s
PsiParameterList:(String s
PsiJavaToken:LPARENTH('(')
PsiParameter:s
PsiModifierList:
<empty list>
PsiTypeElement:String
PsiJavaCodeReferenceElement:String
PsiIdentifier:String('String')
PsiReferenceParameterList
<empty list>
PsiWhiteSpace(' ')
PsiIdentifier:s('s')
PsiErrorElement:')' expected
<empty list>
PsiWhiteSpace(' ')
PsiJavaToken:ARROW('->')
PsiWhiteSpace(' ')
PsiReferenceExpression:s
PsiReferenceParameterList
<empty list>
PsiIdentifier:s('s')
@@ -0,0 +1,34 @@
PsiJavaFile:LambdaExpression18.java
PsiLambdaExpression:(java.lang.String s, -> s
PsiParameterList:(java.lang.String s,
PsiJavaToken:LPARENTH('(')
PsiParameter:s
PsiModifierList:
<empty list>
PsiTypeElement:java.lang.String
PsiJavaCodeReferenceElement:java.lang.String
PsiJavaCodeReferenceElement:java.lang
PsiJavaCodeReferenceElement:java
PsiIdentifier:java('java')
PsiReferenceParameterList
<empty list>
PsiJavaToken:DOT('.')
PsiIdentifier:lang('lang')
PsiReferenceParameterList
<empty list>
PsiJavaToken:DOT('.')
PsiIdentifier:String('String')
PsiReferenceParameterList
<empty list>
PsiWhiteSpace(' ')
PsiIdentifier:s('s')
PsiJavaToken:COMMA(',')
PsiErrorElement:Parameter expected
<empty list>
PsiWhiteSpace(' ')
PsiJavaToken:ARROW('->')
PsiWhiteSpace(' ')
PsiReferenceExpression:s
PsiReferenceParameterList
<empty list>
PsiIdentifier:s('s')
@@ -0,0 +1,31 @@
PsiJavaFile:LambdaExpression19.java
PsiLambdaExpression:(@A T t) -> (null)
PsiParameterList:(@A T t)
PsiJavaToken:LPARENTH('(')
PsiParameter:t
PsiModifierList:@A
PsiAnnotation
PsiJavaToken:AT('@')
PsiJavaCodeReferenceElement:A
PsiIdentifier:A('A')
PsiReferenceParameterList
<empty list>
PsiAnnotationParameterList
<empty list>
PsiWhiteSpace(' ')
PsiTypeElement:T
PsiJavaCodeReferenceElement:T
PsiIdentifier:T('T')
PsiReferenceParameterList
<empty list>
PsiWhiteSpace(' ')
PsiIdentifier:t('t')
PsiJavaToken:RPARENTH(')')
PsiWhiteSpace(' ')
PsiJavaToken:ARROW('->')
PsiWhiteSpace(' ')
PsiParenthesizedExpression:(null)
PsiJavaToken:LPARENTH('(')
PsiLiteralExpression:null
PsiJavaToken:NULL_KEYWORD('null')
PsiJavaToken:RPARENTH(')')
@@ -134,6 +134,14 @@ public class ExpressionParserTest extends JavaParsingTestCase {
public void testLambdaExpression9() { doParserTest("(I)p -> null"); }
public void testLambdaExpression10() { doParserTest("(I)(p -> null)"); }
public void testLambdaExpression11() { doParserTest("() -> { }"); }
public void testLambdaExpression12() { doParserTest("<T>() -> new C<T>()"); }
public void testLambdaExpression13() { doParserTest("<T>t -> t"); }
public void testLambdaExpression14() { doParserTest("(String t) -> t"); }
public void testLambdaExpression15() { doParserTest("(int a, int b) -> a + b"); }
public void testLambdaExpression16() { doParserTest("(final int x) -> x"); }
public void testLambdaExpression17() { doParserTest("(String s -> s"); }
public void testLambdaExpression18() { doParserTest("(java.lang.String s, -> s"); }
public void testLambdaExpression19() { doParserTest("(@A T t) -> (null)"); }
public void testAmbiguousLambdaExpression() { doParserTest("f( (x) < y , z > (w) -> v )"); }