diff --git a/plugins/groovy/groovy-psi/gen/org/jetbrains/plugins/groovy/lang/parser/GroovyBnfParser.java b/plugins/groovy/groovy-psi/gen/org/jetbrains/plugins/groovy/lang/parser/GroovyBnfParser.java index 6505778145f9..34c35897f05d 100644 --- a/plugins/groovy/groovy-psi/gen/org/jetbrains/plugins/groovy/lang/parser/GroovyBnfParser.java +++ b/plugins/groovy/groovy-psi/gen/org/jetbrains/plugins/groovy/lang/parser/GroovyBnfParser.java @@ -434,50 +434,6 @@ public class GroovyBnfParser implements PsiParser, LightPsiParser { return r || p; } - /* ********************************************************** */ - // '<' <>>>>> '>' - static boolean angle_list(PsiBuilder b, int l, Parser _item) { - if (!recursion_guard_(b, l, "angle_list")) return false; - if (!nextTokenIsFast(b, T_LT)) return false; - boolean r, p; - Marker m = enter_section_(b, l, _NONE_); - r = consumeTokenFast(b, T_LT); - p = r; // pin = 1 - r = r && report_error_(b, comma_list_p(b, l + 1, new Parser() { - public boolean parse(PsiBuilder b, int l) { - return angle_list_item(b, l + 1, _item); - } - })); - r = p && consumeToken(b, T_GT) && r; - exit_section_(b, l, m, r, p, null); - return r || p; - } - - /* ********************************************************** */ - // <> | angle_list_item_recovered - static boolean angle_list_item(PsiBuilder b, int l, Parser _item) { - if (!recursion_guard_(b, l, "angle_list_item")) return false; - boolean r; - Marker m = enter_section_(b); - r = _item.parse(b, l); - if (!r) r = angle_list_item_recovered(b, l + 1); - exit_section_(b, m, null, r); - return r; - } - - /* ********************************************************** */ - // empty fail - static boolean angle_list_item_recovered(PsiBuilder b, int l) { - if (!recursion_guard_(b, l, "angle_list_item_recovered")) return false; - boolean r, p; - Marker m = enter_section_(b, l, _NONE_); - r = empty(b, l + 1); - p = r; // pin = 1 - r = r && noMatch(b, l + 1); - exit_section_(b, l, m, r, p, angle_list_item_recovery_parser_); - return r || p; - } - /* ********************************************************** */ // '@' annotation_reference after_annotation_reference public static boolean annotation(PsiBuilder b, int l) { @@ -2742,6 +2698,19 @@ public class GroovyBnfParser implements PsiParser, LightPsiParser { return r || p; } + /* ********************************************************** */ + // <> fast_l_angle '>' + static boolean diamond_type_argument_list(PsiBuilder b, int l) { + if (!recursion_guard_(b, l, "diamond_type_argument_list")) return false; + boolean r; + Marker m = enter_section_(b); + r = isDiamondAllowed(b, l + 1); + r = r && fast_l_angle(b, l + 1); + r = r && consumeToken(b, T_GT); + exit_section_(b, m, null, r); + return r; + } + /* ********************************************************** */ // disjunction_type_element_part+ public static boolean disjunction_type_element(PsiBuilder b, int l) { @@ -3343,6 +3312,12 @@ public class GroovyBnfParser implements PsiParser, LightPsiParser { return consumeTokenFast(b, DOLLAR_SLASHY_CONTENT); } + /* ********************************************************** */ + // '<' + static boolean fast_l_angle(PsiBuilder b, int l) { + return consumeTokenFast(b, T_LT); + } + /* ********************************************************** */ // SLASHY_CONTENT static boolean fast_slashy_content(PsiBuilder b, int l) { @@ -3903,20 +3878,24 @@ public class GroovyBnfParser implements PsiParser, LightPsiParser { } /* ********************************************************** */ - // <> | nl <> + // <> | <>)>> static boolean mb_nl_group(PsiBuilder b, int l, Parser _something) { if (!recursion_guard_(b, l, "mb_nl_group")) return false; boolean r; Marker m = enter_section_(b); r = _something.parse(b, l); - if (!r) r = mb_nl_group_1(b, l + 1, _something); + if (!r) r = withProtectedLastVariantPos(b, l + 1, new Parser() { + public boolean parse(PsiBuilder b, int l) { + return mb_nl_group_1_0(b, l + 1, _something); + } + }); exit_section_(b, m, null, r); return r; } // nl <> - private static boolean mb_nl_group_1(PsiBuilder b, int l, Parser _something) { - if (!recursion_guard_(b, l, "mb_nl_group_1")) return false; + private static boolean mb_nl_group_1_0(PsiBuilder b, int l, Parser _something) { + if (!recursion_guard_(b, l, "mb_nl_group_1_0")) return false; boolean r; Marker m = enter_section_(b); r = nl(b, l + 1); @@ -4435,6 +4414,21 @@ public class GroovyBnfParser implements PsiParser, LightPsiParser { return r || p; } + /* ********************************************************** */ + // '<' <> type_argument_list_end + static boolean non_empty_type_argument_list(PsiBuilder b, int l) { + if (!recursion_guard_(b, l, "non_empty_type_argument_list")) return false; + if (!nextTokenIsFast(b, T_LT)) return false; + boolean r, p; + Marker m = enter_section_(b, l, _NONE_); + r = consumeTokenFast(b, T_LT); + p = r; // pin = 1 + r = r && report_error_(b, comma_list_p(b, l + 1, type_argument_list_item_parser_)); + r = p && type_argument_list_end(b, l + 1) && r; + exit_section_(b, l, m, r, p, null); + return r || p; + } + /* ********************************************************** */ // '{' mb_separators block_levels '}' public static boolean open_block(PsiBuilder b, int l) { @@ -6085,47 +6079,31 @@ public class GroovyBnfParser implements PsiParser, LightPsiParser { } /* ********************************************************** */ - // <> ('<') '>' | <> + // diamond_type_argument_list | non_empty_type_argument_list public static boolean type_argument_list(PsiBuilder b, int l) { if (!recursion_guard_(b, l, "type_argument_list")) return false; boolean r; Marker m = enter_section_(b, l, _NONE_, TYPE_ARGUMENT_LIST, ""); - r = type_argument_list_0(b, l + 1); - if (!r) r = angle_list(b, l + 1, type_argument_list_1_0_parser_); + r = diamond_type_argument_list(b, l + 1); + if (!r) r = non_empty_type_argument_list(b, l + 1); exit_section_(b, l, m, r, false, null); return r; } - // <> ('<') '>' - private static boolean type_argument_list_0(PsiBuilder b, int l) { - if (!recursion_guard_(b, l, "type_argument_list_0")) return false; - boolean r; - Marker m = enter_section_(b); - r = isDiamondAllowed(b, l + 1); - r = r && type_argument_list_0_1(b, l + 1); - r = r && consumeToken(b, T_GT); - exit_section_(b, m, null, r); - return r; + /* ********************************************************** */ + // <'>> + static boolean type_argument_list_end(PsiBuilder b, int l) { + return mb_nl_group(b, l + 1, T_GT_parser_); } - // ('<') - private static boolean type_argument_list_0_1(PsiBuilder b, int l) { - if (!recursion_guard_(b, l, "type_argument_list_0_1")) return false; - boolean r; - Marker m = enter_section_(b); - r = consumeTokenFast(b, T_LT); - exit_section_(b, m, null, r); - return r; - } - - // mb_nl type_argument mb_nl - private static boolean type_argument_list_1_0(PsiBuilder b, int l) { - if (!recursion_guard_(b, l, "type_argument_list_1_0")) return false; + /* ********************************************************** */ + // mb_nl type_argument + static boolean type_argument_list_item(PsiBuilder b, int l) { + if (!recursion_guard_(b, l, "type_argument_list_item")) return false; boolean r; Marker m = enter_section_(b); r = mb_nl(b, l + 1); r = r && type_argument(b, l + 1); - r = r && mb_nl(b, l + 1); exit_section_(b, m, null, r); return r; } @@ -7245,6 +7223,11 @@ public class GroovyBnfParser implements PsiParser, LightPsiParser { return consumeToken(b, SLASHY_END); } }; + final static Parser T_GT_parser_ = new Parser() { + public boolean parse(PsiBuilder b, int l) { + return consumeToken(b, T_GT); + } + }; final static Parser T_LBRACE_parser_ = new Parser() { public boolean parse(PsiBuilder b, int l) { return consumeTokenFast(b, T_LBRACE); @@ -7260,11 +7243,6 @@ public class GroovyBnfParser implements PsiParser, LightPsiParser { return consumeToken(b, T_RPAREN); } }; - final static Parser angle_list_item_recovery_parser_ = new Parser() { - public boolean parse(PsiBuilder b, int l) { - return commaAngleRecovery(b, l + 1); - } - }; final static Parser annotation_level_parser_ = new Parser() { public boolean parse(PsiBuilder b, int l) { return annotation_level(b, l + 1); @@ -7565,9 +7543,9 @@ public class GroovyBnfParser implements PsiParser, LightPsiParser { return top_level_start(b, l + 1); } }; - final static Parser type_argument_list_1_0_parser_ = new Parser() { + final static Parser type_argument_list_item_parser_ = new Parser() { public boolean parse(PsiBuilder b, int l) { - return type_argument_list_1_0(b, l + 1); + return type_argument_list_item(b, l + 1); } }; final static Parser type_argument_list_parser_ = new Parser() { diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/parser/groovy.bnf b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/parser/groovy.bnf index ed9bde72a6fd..9b3e260d14cf 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/parser/groovy.bnf +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/parser/groovy.bnf @@ -519,11 +519,15 @@ empty_type_parameter_bounds ::= { elementType = type_parameter_bounds } private type_parameter_bounds_list ::= type_code_reference type_parameter_bounds_list_tail* private type_parameter_bounds_list_tail ::= '&' type_code_reference { pin = 1 } -type_argument_list ::= <> ('<') '>' | <> +type_argument_list ::= diamond_type_argument_list | non_empty_type_argument_list +private diamond_type_argument_list ::= <> fast_l_angle '>' +private non_empty_type_argument_list ::= '<' <> type_argument_list_end { pin = 1 consumeTokenMethod = 'consumeTokenFast' } +private type_argument_list_item ::= mb_nl type_argument +private type_argument_list_end ::= <'>> private type_argument ::= type_element | wildcard_type_element { name = 'type argument' @@ -1210,10 +1214,12 @@ private mb_initializer ::= ['=' mb_nl expression_or_application] // region Other private mb_nl ::= nl? private nl ::= NL { consumeTokenMethod = 'consumeTokenFast' } -private meta mb_nl_group ::= <> | nl <> // allows error reporting in position before the new line +// FIXME this rule allows error reporting in position before the new line +private meta mb_nl_group ::= <> | <>)>> private empty ::= () private fast_comma ::= ',' +private fast_l_angle ::= '<' // endregion @@ -1228,11 +1234,6 @@ private meta paren_list_inner ::= <>>> <> { pin = 1 recoverWhile = paren_list_item_recovery } private meta paren_list_tail ::= ',' <>>> { pin = 1 } private external paren_list_item_recovery ::= commaParenRecovery - -private meta angle_list ::= '<' <>>>>> '>' { pin = 1 consumeTokenMethod = 'consumeTokenFast' } -private meta angle_list_item ::= <> | angle_list_item_recovered -private angle_list_item_recovered ::= empty fail { pin = 1 recoverWhile = angle_list_item_recovery } -private external angle_list_item_recovery ::= commaAngleRecovery // endregion diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/parser/parserUtils.kt b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/parser/parserUtils.kt index 87160d1a55ea..5ac7ad5e41d7 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/parser/parserUtils.kt +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/parser/parserUtils.kt @@ -36,7 +36,7 @@ fun extendedStatement(builder: PsiBuilder, level: Int): Boolean = builder.groovy fun extendedSeparator(builder: PsiBuilder, level: Int): Boolean = builder.advanceIf { builder.groovyParser.isExtendedSeparator(tokenType) } private val currentClassName: Key = Key.create("groovy.parse.class.name") -private val parseDiamonds: Key = Key.create("groovy.parse.diamons") +private val parseDiamonds: Key = Key.create("groovy.parse.diamonds") private val parseArguments: Key = Key.create("groovy.parse.arguments") private val parseApplicationArguments: Key = Key.create("groovy.parse.application.arguments") private val parseNoTypeArgumentsCodeReference: Key = Key.create("groovy.parse.no.type.arguments") @@ -233,8 +233,6 @@ fun parseAssignment(builder: PsiBuilder, level: Int): Boolean = builder.advanceI fun commaParenRecovery(builder: PsiBuilder, level: Int): Boolean = builder.tokenType.let { it != T_COMMA && it != T_RPAREN } -fun commaAngleRecovery(builder: PsiBuilder, level: Int): Boolean = builder.tokenType.let { it != T_COMMA && it != T_GT } - fun error(builder: PsiBuilder, level: Int, key: String): Boolean { val marker = builder.latestDoneMarker ?: return false val elementType = marker.tokenType @@ -305,7 +303,7 @@ fun addVariant(builder: PsiBuilder, level: Int, variant: String): Boolean { } fun clearVariants(builder: PsiBuilder, level: Int): Boolean { - val state = ErrorState.get(builder) + val state = builder.state state.clearVariants(state.currentFrame) return true } @@ -315,6 +313,21 @@ fun replaceVariants(builder: PsiBuilder, level: Int, variant: String): Boolean { } fun clearError(builder: PsiBuilder, level: Int): Boolean { - ErrorState.get(builder).currentFrame.errorReportedAt = -1 + builder.state.currentFrame.errorReportedAt = -1 return true } + +fun withProtectedLastVariantPos(builder: PsiBuilder, level: Int, parser: Parser): Boolean { + val state = builder.state + val prev = state.currentFrame.lastVariantAt + @Suppress("LiftReturnOrAssignment") + if (parser.parse(builder, level)) { + return true + } + else { + state.currentFrame.lastVariantAt = prev + return false + } +} + +private val PsiBuilder.state: ErrorState get() = ErrorState.get(this) 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 adc893c51d0c..9c89d0d3c954 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 @@ -585,7 +585,6 @@ class ExpressionsParsingTest extends GroovyParsingTestCase { void testDiamond() { doTest() } - @Bombed(month = 6, day = 10, user = "daniil") void testDiamondErrors() { doTest() } void testpath$stringMethodCall1() { doTest() } diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/parser/TypesParsingTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/parser/TypesParsingTest.groovy index 03f7e3e08600..447ca826246e 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/parser/TypesParsingTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/parser/TypesParsingTest.groovy @@ -70,6 +70,10 @@ class TypesParsingTest extends GroovyParsingTestCase { void testNewLineBeforeRAngleInTypeArgumentList() { doTest() } + void testNewLineBetweenTypeArguments() { doTest() } + + void testNewLineBetweenTypeArgumentsError() { doTest() } + void testNewLineBetweenExtendsImplements() { doTest() } void testStaticInitializer() throws Throwable { doTest() } diff --git a/plugins/groovy/testdata/parsing/groovy/expressions/diamondErrors.test b/plugins/groovy/testdata/parsing/groovy/expressions/diamondErrors.test index ffcbed0389d5..6132154b7854 100644 --- a/plugins/groovy/testdata/parsing/groovy/expressions/diamondErrors.test +++ b/plugins/groovy/testdata/parsing/groovy/expressions/diamondErrors.test @@ -1,3 +1,49 @@ def f2 = new ArrayList<() def f2 = new ArrayList>() ----- +Groovy script + Variable definitions + Modifiers + PsiElement(def)('def') + PsiWhiteSpace(' ') + Variable + PsiElement(identifier)('f2') + PsiWhiteSpace(' ') + PsiElement(=)('=') + PsiWhiteSpace(' ') + NEW expression + PsiElement(new)('new') + PsiWhiteSpace(' ') + Reference element + PsiElement(identifier)('ArrayList') + Type arguments + PsiElement(<)('<') + PsiErrorElement: or '>' expected, got '(' + + Arguments + PsiElement(()('(') + PsiElement())(')') + PsiElement(new line)('\n') + Variable definitions + Modifiers + PsiElement(def)('def') + PsiWhiteSpace(' ') + Variable + PsiElement(identifier)('f2') + PsiWhiteSpace(' ') + PsiElement(=)('=') + PsiWhiteSpace(' ') + Relational expression + NEW expression + PsiElement(new)('new') + PsiWhiteSpace(' ') + Reference element + PsiElement(identifier)('ArrayList') + PsiErrorElement:'(' expected, got '>' + + PsiElement(>)('>') + Parenthesized expression + PsiElement(()('(') + PsiErrorElement: expected, got ')' + + PsiElement())(')') \ No newline at end of file diff --git a/plugins/groovy/testdata/parsing/groovy/types/newLineBetweenTypeArguments.test b/plugins/groovy/testdata/parsing/groovy/types/newLineBetweenTypeArguments.test new file mode 100644 index 000000000000..7e2f3aa797e9 --- /dev/null +++ b/plugins/groovy/testdata/parsing/groovy/types/newLineBetweenTypeArguments.test @@ -0,0 +1,33 @@ +class MyClass implements Roo {} +----- +Groovy script + Class definition + Modifiers + + PsiElement(class)('class') + PsiWhiteSpace(' ') + PsiElement(identifier)('MyClass') + Extends clause + + PsiWhiteSpace(' ') + Implements clause + PsiElement(implements)('implements') + PsiWhiteSpace(' ') + Reference element + PsiElement(identifier)('Roo') + Type arguments + PsiElement(<)('<') + Type element + Reference element + PsiElement(identifier)('String') + PsiElement(,)(',') + PsiElement(new line)('\n') + Type element + Reference element + PsiElement(identifier)('Object') + PsiElement(>)('>') + PsiWhiteSpace(' ') + Type definition body + PsiElement({)('{') + PsiElement(})('}') \ No newline at end of file diff --git a/plugins/groovy/testdata/parsing/groovy/types/newLineBetweenTypeArgumentsError.test b/plugins/groovy/testdata/parsing/groovy/types/newLineBetweenTypeArgumentsError.test new file mode 100644 index 000000000000..8e05ccd49222 --- /dev/null +++ b/plugins/groovy/testdata/parsing/groovy/types/newLineBetweenTypeArgumentsError.test @@ -0,0 +1,39 @@ +class MyClass implements Roo {} +----- +Groovy script + Class definition + Modifiers + + PsiElement(class)('class') + PsiWhiteSpace(' ') + PsiElement(identifier)('MyClass') + Extends clause + + PsiWhiteSpace(' ') + Implements clause + PsiElement(implements)('implements') + PsiWhiteSpace(' ') + Reference element + PsiElement(identifier)('Roo') + Type arguments + PsiElement(<)('<') + Type element + Reference element + PsiElement(identifier)('String') + PsiErrorElement:'>' expected + + PsiElement(new line)('\n') + PsiErrorElement:'>' or '{' expected, got ',' + + PsiElement(,)(',') + Relational expression + Reference expression + PsiElement(identifier)('Object') + PsiElement(>)('>') + PsiWhiteSpace(' ') + Closable block + PsiElement({)('{') + Parameter list + + PsiElement(})('}') \ No newline at end of file