diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/parsing/statements/expressions/ExpressionStatement.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/parsing/statements/expressions/ExpressionStatement.java index 1279744c4c52..6fe5fb90a9ce 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/parsing/statements/expressions/ExpressionStatement.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/parsing/statements/expressions/ExpressionStatement.java @@ -16,16 +16,18 @@ package org.jetbrains.plugins.groovy.lang.parser.parsing.statements.expressions; -import com.intellij.lang.LighterASTNode; import com.intellij.lang.PsiBuilder; import com.intellij.psi.tree.IElementType; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.lang.lexer.TokenSets; import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes; import org.jetbrains.plugins.groovy.lang.parser.GroovyParser; +import org.jetbrains.plugins.groovy.lang.parser.parsing.statements.blocks.OpenOrClosableBlock; import org.jetbrains.plugins.groovy.lang.parser.parsing.statements.expressions.arguments.CommandArguments; import org.jetbrains.plugins.groovy.lang.parser.parsing.statements.expressions.arithmetic.PathExpression; +import org.jetbrains.plugins.groovy.lang.parser.parsing.statements.expressions.arithmetic.UnaryExpressionNotPlusMinus; import org.jetbrains.plugins.groovy.lang.parser.parsing.statements.expressions.primary.PrimaryExpression; +import org.jetbrains.plugins.groovy.lang.parser.parsing.util.ParserUtils; /** * Main classdef for any general expression parsing @@ -36,17 +38,28 @@ public class ExpressionStatement implements GroovyElementTypes { @Nullable private static IElementType parseExpressionStatement(PsiBuilder builder, GroovyParser parser) { - final LighterASTNode firstDoneMarker = builder.getLatestDoneMarker(); + if (checkForTypeCast(builder, parser)) return CAST_EXPRESSION; PsiBuilder.Marker marker = builder.mark(); - if (ConditionalExpression.parse(builder, parser) && + final PathExpression.Result result = PathExpression.parseForExprStatement(builder, parser); + if (result != PathExpression.Result.WRONG_WAY && !TokenSets.SEPARATORS.contains(builder.getTokenType()) && - CommandArguments.parse(builder, parser)) { - marker.done(CALL_EXPRESSION); - return CALL_EXPRESSION; + !TokenSets.BINARY_OP_SET.contains(builder.getTokenType()) && + !TokenSets.UNARY_OP_SET.contains(builder.getTokenType())) { + if (result == PathExpression.Result.CALL_WITH_CLOSURE) { + marker.drop(); + return PATH_METHOD_CALL; + } + else if (CommandArguments.parse(builder, parser)) { + marker.done(CALL_EXPRESSION); + return CALL_EXPRESSION; + } } marker.drop(); - final LighterASTNode latestDoneMarker = builder.getLatestDoneMarker(); - return latestDoneMarker != null && firstDoneMarker != latestDoneMarker ? latestDoneMarker.getTokenType() : WRONGWAY; + return WRONGWAY; + } + + private static boolean checkForTypeCast(PsiBuilder builder, GroovyParser parser) { + return UnaryExpressionNotPlusMinus.parse(builder, parser, false); } /** @@ -63,7 +76,7 @@ public class ExpressionStatement implements GroovyElementTypes { PsiBuilder.Marker marker = builder.mark(); final IElementType result = parseExpressionStatement(builder, parser); - if (result != CALL_EXPRESSION) { + if (result != CALL_EXPRESSION && result != PATH_METHOD_CALL) { marker.drop(); return result != WRONGWAY; } @@ -73,19 +86,36 @@ public class ExpressionStatement implements GroovyElementTypes { marker.drop(); break; } - final PsiBuilder.Marker exprStatement = marker.precede(); + PsiBuilder.Marker exprStatement = marker.precede(); marker.done(REFERENCE_EXPRESSION); if (builder.getTokenType() == mLPAREN) { PrimaryExpression.methodCallArgsParse(builder, parser); exprStatement.done(PATH_METHOD_CALL); } + else if (mLBRACK.equals(builder.getTokenType()) && + !ParserUtils.lookAhead(builder, mLBRACK, mCOLON) && + !ParserUtils.lookAhead(builder, mLBRACK, mNLS, mCOLON)) { + PathExpression.indexPropertyArgsParse(builder, parser); + exprStatement.done(PATH_INDEX_PROPERTY); + if (mLPAREN.equals(builder.getTokenType())) { + PrimaryExpression.methodCallArgsParse(builder, parser); + } + else if (mLCURLY.equals(builder.getTokenType())) { + PsiBuilder.Marker argsMarker = builder.mark(); + argsMarker.done(ARGUMENTS); + } + while (mLCURLY.equals(builder.getTokenType())) { + OpenOrClosableBlock.parseClosableBlock(builder, parser); + } + exprStatement = exprStatement.precede(); + exprStatement.done(PATH_METHOD_CALL); + } else if (CommandArguments.parse(builder, parser)) { exprStatement.done(CALL_EXPRESSION); } else { exprStatement.drop(); - //builder.error(GroovyBundle.message("expression.expected")); break; } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/parsing/statements/expressions/arithmetic/PathExpression.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/parsing/statements/expressions/arithmetic/PathExpression.java index 47b153d77a06..57409390ec7e 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/parsing/statements/expressions/arithmetic/PathExpression.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/parsing/statements/expressions/arithmetic/PathExpression.java @@ -31,33 +31,42 @@ import org.jetbrains.plugins.groovy.lang.parser.parsing.statements.expressions.p import org.jetbrains.plugins.groovy.lang.parser.parsing.types.TypeArguments; import org.jetbrains.plugins.groovy.lang.parser.parsing.util.ParserUtils; +import static org.jetbrains.plugins.groovy.lang.parser.parsing.statements.expressions.arithmetic.PathExpression.Result.*; + /** * @author ilyas */ public class PathExpression implements GroovyElementTypes { public static boolean parse(PsiBuilder builder, GroovyParser parser) { + return parseForExprStatement(builder, parser) != PathExpression.Result.WRONG_WAY; + } + public enum Result{OK, WRONG_WAY, CALL_WITH_CLOSURE} + + public static Result parseForExprStatement(PsiBuilder builder, GroovyParser parser) { PsiBuilder.Marker marker = builder.mark(); final GroovyElementType qualifierType = PrimaryExpression.parse(builder, parser); if (qualifierType != WRONGWAY) { + Result result = OK; if (isPathElementStart(builder)) { PsiBuilder.Marker newMarker = marker.precede(); marker.drop(); - if (mLCURLY.equals(builder.getTokenType())) { + final boolean lCurly = mLCURLY.equals(builder.getTokenType()); + if (lCurly) { PsiBuilder.Marker argsMarker = builder.mark(); argsMarker.done(ARGUMENTS); } - pathElementParse(builder, newMarker, parser, qualifierType); + result = pathElementParse(builder, newMarker, parser, qualifierType, lCurly?CALL_WITH_CLOSURE:OK); } else { marker.drop(); } - return true; + return result; } else { marker.drop(); - return false; + return WRONG_WAY; } } @@ -71,8 +80,11 @@ public class PathExpression implements GroovyElementTypes { private static final TokenSet DOTS = TokenSet.create(mSPREAD_DOT, mOPTIONAL_DOT, mMEMBER_POINTER, mDOT); - private static GroovyElementType pathElementParse(PsiBuilder builder, PsiBuilder.Marker marker, GroovyParser parser, - GroovyElementType qualifierType) { + private static Result pathElementParse(PsiBuilder builder, + PsiBuilder.Marker marker, + GroovyParser parser, + GroovyElementType qualifierType, + Result result) { GroovyElementType res; @@ -100,7 +112,7 @@ public class PathExpression implements GroovyElementTypes { PsiBuilder.Marker argsMarker = builder.mark(); argsMarker.done(ARGUMENTS); } - pathElementParse(builder, newMarker, parser, res); + result = pathElementParse(builder, newMarker, parser, res, OK); } else { builder.error(GroovyBundle.message("path.selector.expected")); @@ -111,41 +123,46 @@ public class PathExpression implements GroovyElementTypes { PrimaryExpression.methodCallArgsParse(builder, parser); if (mLCURLY.equals(builder.getTokenType()) || ParserUtils.lookAhead(builder, mNLS, mLCURLY)) { ParserUtils.getToken(builder, mNLS); - pathElementParse(builder, marker, parser, qualifierType); + result = pathElementParse(builder, marker, parser, qualifierType, OK); } else { PsiBuilder.Marker newMarker = marker.precede(); marker.done(PATH_METHOD_CALL); - pathElementParse(builder, newMarker, parser, qualifierType); + result = pathElementParse(builder, newMarker, parser, qualifierType, OK); } } else if (mLCURLY.equals(builder.getTokenType())) { appendedBlockParse(builder, parser); if (mLCURLY.equals(builder.getTokenType())) { - pathElementParse(builder, marker, parser, qualifierType); + result = pathElementParse(builder, marker, parser, qualifierType, result); } else { PsiBuilder.Marker newMarker = marker.precede(); marker.done(PATH_METHOD_CALL); - pathElementParse(builder, newMarker, parser, PATH_METHOD_CALL); + result = pathElementParse(builder, newMarker, parser, PATH_METHOD_CALL, result); } } - else if (mLBRACK.equals(builder.getTokenType()) && - !ParserUtils.lookAhead(builder, mLBRACK, mCOLON) && - !ParserUtils.lookAhead(builder, mLBRACK, mNLS, mCOLON)) { + else if (checkForArrayAccess(builder)) { indexPropertyArgsParse(builder, parser); PsiBuilder.Marker newMarker = marker.precede(); marker.done(PATH_INDEX_PROPERTY); - if (mLCURLY.equals(builder.getTokenType())) { + final boolean lCurly = mLCURLY.equals(builder.getTokenType()); + if (lCurly) { PsiBuilder.Marker argsMarker = builder.mark(); argsMarker.done(ARGUMENTS); } - pathElementParse(builder, newMarker, parser, PATH_INDEX_PROPERTY); + result = pathElementParse(builder, newMarker, parser, PATH_INDEX_PROPERTY, lCurly?CALL_WITH_CLOSURE : OK); } else { marker.drop(); } - return PATH_EXPRESSION; + return result; + } + + public static boolean checkForArrayAccess(PsiBuilder builder) { + return mLBRACK.equals(builder.getTokenType()) && + !ParserUtils.lookAhead(builder, mLBRACK, mCOLON) && + !ParserUtils.lookAhead(builder, mLBRACK, mNLS, mCOLON); } private static GroovyElementType parseThisSuperExpression(PsiBuilder builder, GroovyElementType qualifierType) { @@ -221,7 +238,7 @@ public class PathExpression implements GroovyElementTypes { * @param builder * @return */ - private static GroovyElementType indexPropertyArgsParse(PsiBuilder builder, GroovyParser parser) { + public static GroovyElementType indexPropertyArgsParse(PsiBuilder builder, GroovyParser parser) { assert mLBRACK.equals(builder.getTokenType()); PsiBuilder.Marker marker = builder.mark(); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/parsing/statements/expressions/arithmetic/UnaryExpressionNotPlusMinus.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/parsing/statements/expressions/arithmetic/UnaryExpressionNotPlusMinus.java index 7a4846e047c4..989cd873d211 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/parsing/statements/expressions/arithmetic/UnaryExpressionNotPlusMinus.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/parsing/statements/expressions/arithmetic/UnaryExpressionNotPlusMinus.java @@ -33,6 +33,10 @@ import static org.jetbrains.plugins.groovy.lang.parser.parsing.statements.typeDe public class UnaryExpressionNotPlusMinus implements GroovyElementTypes { public static boolean parse(PsiBuilder builder, GroovyParser parser) { + return parse(builder, parser, true); + } + + public static boolean parse(PsiBuilder builder, GroovyParser parser, boolean runPostfixIfFail) { PsiBuilder.Marker marker = builder.mark(); if (builder.getTokenType() == mLPAREN) { final ReferenceElement.ReferenceElementResult result = parseTypeCast(builder); @@ -42,18 +46,22 @@ public class UnaryExpressionNotPlusMinus implements GroovyElementTypes { return true; } else { marker.rollbackTo(); - return PostfixExpression.parse(builder, parser); + return runPostfix(builder, parser, runPostfixIfFail); } } else { marker.drop(); - return PostfixExpression.parse(builder, parser); + return runPostfix(builder, parser, runPostfixIfFail); } } else { marker.drop(); - return PostfixExpression.parse(builder, parser); + return runPostfix(builder, parser, runPostfixIfFail); } } + private static boolean runPostfix(PsiBuilder builder, GroovyParser parser, boolean runPostfixIfFail) { + return runPostfixIfFail ? PostfixExpression.parse(builder, parser) : false; + } + private static ReferenceElement.ReferenceElementResult parseTypeCast(PsiBuilder builder) { PsiBuilder.Marker marker = builder.mark(); if (!ParserUtils.getToken(builder, mLPAREN, GroovyBundle.message("lparen.expected"))) { @@ -78,4 +86,4 @@ public class UnaryExpressionNotPlusMinus implements GroovyElementTypes { return fail; } } -} \ No newline at end of file +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/ClosureParameterEnhancer.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/ClosureParameterEnhancer.java index c6aab0ee20c4..d5c3c30655b3 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/ClosureParameterEnhancer.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/ClosureParameterEnhancer.java @@ -7,11 +7,12 @@ import com.intellij.util.containers.hash.HashMap; import com.intellij.util.containers.hash.HashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList; import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrMethodCall; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.arithmetic.GrRangeExpression; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrGdkMethod; import org.jetbrains.plugins.groovy.lang.psi.impl.GrTupleType; import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil; @@ -77,14 +78,15 @@ public class ClosureParameterEnhancer extends AbstractClosureParameterEnhancer { @Override @Nullable protected PsiType getClosureParameterType(GrClosableBlock closure, int index) { - final PsiElement parent = closure.getParent(); - if (!(parent instanceof GrMethodCallExpression)) { + PsiElement parent = closure.getParent(); + if (parent instanceof GrArgumentList) parent = parent.getParent(); + if (!(parent instanceof GrMethodCall)) { return null; } PsiElementFactory factory = JavaPsiFacade.getInstance(closure.getProject()).getElementFactory(); - String methodName = findMethodName((GrMethodCallExpression)parent); + String methodName = findMethodName((GrMethodCall)parent); - GrExpression expression = ((GrMethodCallExpression)parent).getInvokedExpression(); + GrExpression expression = ((GrMethodCall)parent).getInvokedExpression(); if (!(expression instanceof GrReferenceExpression)) return null; final PsiElement resolved = ((GrReferenceExpression)expression).resolve(); if (!(resolved instanceof GrGdkMethod)) return null; @@ -174,7 +176,7 @@ public class ClosureParameterEnhancer extends AbstractClosureParameterEnhancer { } } else if ("withStream".equals(methodName)) { - final PsiMethod method = ((GrMethodCallExpression)parent).resolveMethod(); + final PsiMethod method = ((GrMethodCall)parent).resolveMethod(); if (method != null) { final PsiParameter[] parameters = method.getParameterList().getParameters(); if (parameters.length > 0) { @@ -266,7 +268,7 @@ public class ClosureParameterEnhancer extends AbstractClosureParameterEnhancer { } @Nullable - private static String findMethodName(@NotNull GrMethodCallExpression methodCall) { + private static String findMethodName(@NotNull GrMethodCall methodCall) { GrExpression expression = methodCall.getInvokedExpression(); if (expression instanceof GrReferenceExpression) { return ((GrReferenceExpression)expression).getReferenceName(); diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/parser/ExpressionsParsingTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/parser/ExpressionsParsingTest.groovy index 1f8f3c103e75..504f149c18b7 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/parser/ExpressionsParsingTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/parser/ExpressionsParsingTest.groovy @@ -243,6 +243,10 @@ public class ExpressionsParsingTest extends GroovyParsingTestCase { public void testcommandExpr$multiArgs() {doTest()} public void testcommandExpr$RHS() {doTest()} public void testcommandExpr$oddArgCount() {doTest()} + public void testcommandExpr$indexAccess1() {doTest()} + public void testcommandExpr$indexAccess2() {doTest()} + public void testcommandExpr$indexAccess3() {doTest()} + public void testcommandExpr$closureArg2() {doTest()} /*def testCommandExpr$() {doTest()} def testCommandExpr$() {doTest()} def testCommandExpr$() {doTest()} diff --git a/plugins/groovy/testdata/groovy/refactoring/introduceVariable/inCase.test b/plugins/groovy/testdata/groovy/refactoring/introduceVariable/inCase.test index fc491317cb7b..31480e8dcd0a 100644 --- a/plugins/groovy/testdata/groovy/refactoring/introduceVariable/inCase.test +++ b/plugins/groovy/testdata/groovy/refactoring/introduceVariable/inCase.test @@ -11,6 +11,6 @@ switch (e) { int preved = 4 break; case 2: - preved = 5 - print preved + def preved = 5 + print preved } \ No newline at end of file diff --git a/plugins/groovy/testdata/parsing/groovy/expressions/commandExpr/closureArg2.test b/plugins/groovy/testdata/parsing/groovy/expressions/commandExpr/closureArg2.test new file mode 100644 index 000000000000..d16cd93b9c65 --- /dev/null +++ b/plugins/groovy/testdata/parsing/groovy/expressions/commandExpr/closureArg2.test @@ -0,0 +1,24 @@ +foo {a} bar "x" +----- +Groovy script + Call expression + Reference expression + Method call + Reference expression + PsiElement(identifier)('foo') + PsiWhiteSpace(' ') + Arguments + + Closable block + PsiElement({)('{') + Parameter list + + Reference expression + PsiElement(identifier)('a') + PsiElement(})('}') + PsiWhiteSpace(' ') + PsiElement(identifier)('bar') + PsiWhiteSpace(' ') + Command arguments + Literal + PsiElement(Gstring)('"x"') diff --git a/plugins/groovy/testdata/parsing/groovy/expressions/commandExpr/indexAccess1.test b/plugins/groovy/testdata/parsing/groovy/expressions/commandExpr/indexAccess1.test new file mode 100644 index 000000000000..c2303ccb0759 --- /dev/null +++ b/plugins/groovy/testdata/parsing/groovy/expressions/commandExpr/indexAccess1.test @@ -0,0 +1,36 @@ +foo a1 a2[1](){} a3 a4 +----- +Groovy script + Call expression + Reference expression + Method call + Property by index + Reference expression + Call expression + Reference expression + PsiElement(identifier)('foo') + PsiWhiteSpace(' ') + Command arguments + Reference expression + PsiElement(identifier)('a1') + PsiWhiteSpace(' ') + PsiElement(identifier)('a2') + Arguments + PsiElement([)('[') + Literal + PsiElement(Integer)('1') + PsiElement(])(']') + Arguments + PsiElement(()('(') + PsiElement())(')') + Closable block + PsiElement({)('{') + Parameter list + + PsiElement(})('}') + PsiWhiteSpace(' ') + PsiElement(identifier)('a3') + PsiWhiteSpace(' ') + Command arguments + Reference expression + PsiElement(identifier)('a4') diff --git a/plugins/groovy/testdata/parsing/groovy/expressions/commandExpr/indexAccess2.test b/plugins/groovy/testdata/parsing/groovy/expressions/commandExpr/indexAccess2.test new file mode 100644 index 000000000000..0563f8f1db07 --- /dev/null +++ b/plugins/groovy/testdata/parsing/groovy/expressions/commandExpr/indexAccess2.test @@ -0,0 +1,35 @@ +foo a1 a2[1]{} a3 a4 +----- +Groovy script + Call expression + Reference expression + Method call + Property by index + Reference expression + Call expression + Reference expression + PsiElement(identifier)('foo') + PsiWhiteSpace(' ') + Command arguments + Reference expression + PsiElement(identifier)('a1') + PsiWhiteSpace(' ') + PsiElement(identifier)('a2') + Arguments + PsiElement([)('[') + Literal + PsiElement(Integer)('1') + PsiElement(])(']') + Arguments + + Closable block + PsiElement({)('{') + Parameter list + + PsiElement(})('}') + PsiWhiteSpace(' ') + PsiElement(identifier)('a3') + PsiWhiteSpace(' ') + Command arguments + Reference expression + PsiElement(identifier)('a4') diff --git a/plugins/groovy/testdata/parsing/groovy/expressions/commandExpr/indexAccess3.test b/plugins/groovy/testdata/parsing/groovy/expressions/commandExpr/indexAccess3.test new file mode 100644 index 000000000000..042a26a36b42 --- /dev/null +++ b/plugins/groovy/testdata/parsing/groovy/expressions/commandExpr/indexAccess3.test @@ -0,0 +1,41 @@ +foo a1 a2[1](){}{} a3 a4 +------ +Groovy script + Call expression + Reference expression + Method call + Property by index + Reference expression + Call expression + Reference expression + PsiElement(identifier)('foo') + PsiWhiteSpace(' ') + Command arguments + Reference expression + PsiElement(identifier)('a1') + PsiWhiteSpace(' ') + PsiElement(identifier)('a2') + Arguments + PsiElement([)('[') + Literal + PsiElement(Integer)('1') + PsiElement(])(']') + Arguments + PsiElement(()('(') + PsiElement())(')') + Closable block + PsiElement({)('{') + Parameter list + + PsiElement(})('}') + Closable block + PsiElement({)('{') + Parameter list + + PsiElement(})('}') + PsiWhiteSpace(' ') + PsiElement(identifier)('a3') + PsiWhiteSpace(' ') + Command arguments + Reference expression + PsiElement(identifier)('a4') \ No newline at end of file