mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[groovy] parser: don't recover in type argument lists (IDEA-191728)
This commit is contained in:
+60
-82
@@ -434,50 +434,6 @@ public class GroovyBnfParser implements PsiParser, LightPsiParser {
|
||||
return r || p;
|
||||
}
|
||||
|
||||
/* ********************************************************** */
|
||||
// '<' <<comma_list_p <<angle_list_item <<item>>>>>> '>'
|
||||
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;
|
||||
}
|
||||
|
||||
/* ********************************************************** */
|
||||
// <<item>> | 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;
|
||||
}
|
||||
|
||||
/* ********************************************************** */
|
||||
// <<isDiamondAllowed>> 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 {
|
||||
}
|
||||
|
||||
/* ********************************************************** */
|
||||
// <<something>> | nl <<something>>
|
||||
// <<something>> | <<withProtectedLastVariantPos (nl <<something>>)>>
|
||||
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 <<something>>
|
||||
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;
|
||||
}
|
||||
|
||||
/* ********************************************************** */
|
||||
// '<' <<comma_list_p type_argument_list_item>> 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 {
|
||||
}
|
||||
|
||||
/* ********************************************************** */
|
||||
// <<isDiamondAllowed>> ('<') '>' | <<angle_list (mb_nl type_argument mb_nl)>>
|
||||
// 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, "<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;
|
||||
}
|
||||
|
||||
// <<isDiamondAllowed>> ('<') '>'
|
||||
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;
|
||||
/* ********************************************************** */
|
||||
// <<mb_nl_group '>'>>
|
||||
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() {
|
||||
|
||||
@@ -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 ::= <<isDiamondAllowed>> ('<') '>' | <<angle_list (mb_nl type_argument mb_nl)>>
|
||||
type_argument_list ::= diamond_type_argument_list | non_empty_type_argument_list
|
||||
private diamond_type_argument_list ::= <<isDiamondAllowed>> fast_l_angle '>'
|
||||
private non_empty_type_argument_list ::= '<' <<comma_list_p type_argument_list_item>> type_argument_list_end
|
||||
{
|
||||
pin = 1
|
||||
consumeTokenMethod = 'consumeTokenFast'
|
||||
}
|
||||
private type_argument_list_item ::= mb_nl type_argument
|
||||
private type_argument_list_end ::= <<mb_nl_group '>'>>
|
||||
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 ::= <<something>> | nl <<something>> // 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 ::= <<something>> | <<withProtectedLastVariantPos (nl <<something>>)>>
|
||||
private empty ::= ()
|
||||
|
||||
private fast_comma ::= ','
|
||||
private fast_l_angle ::= '<'
|
||||
// endregion
|
||||
|
||||
|
||||
@@ -1228,11 +1234,6 @@ private meta paren_list_inner ::= <<paren_list_item <<item>>>> <<paren_list_tail
|
||||
private meta paren_list_item ::= empty <<item>> { pin = 1 recoverWhile = paren_list_item_recovery }
|
||||
private meta paren_list_tail ::= ',' <<paren_list_item <<item>>>> { pin = 1 }
|
||||
private external paren_list_item_recovery ::= commaParenRecovery
|
||||
|
||||
private meta angle_list ::= '<' <<comma_list_p <<angle_list_item <<item>>>>>> '>' { pin = 1 consumeTokenMethod = 'consumeTokenFast' }
|
||||
private meta angle_list_item ::= <<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
|
||||
|
||||
|
||||
|
||||
+18
-5
@@ -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<String> = Key.create("groovy.parse.class.name")
|
||||
private val parseDiamonds: Key<Boolean> = Key.create("groovy.parse.diamons")
|
||||
private val parseDiamonds: Key<Boolean> = Key.create("groovy.parse.diamonds")
|
||||
private val parseArguments: Key<Boolean> = Key.create("groovy.parse.arguments")
|
||||
private val parseApplicationArguments: Key<Boolean> = Key.create("groovy.parse.application.arguments")
|
||||
private val parseNoTypeArgumentsCodeReference: Key<Boolean> = 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)
|
||||
|
||||
-1
@@ -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() }
|
||||
|
||||
@@ -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() }
|
||||
|
||||
@@ -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:<type argument> or '>' expected, got '('
|
||||
<empty list>
|
||||
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 '>'
|
||||
<empty list>
|
||||
PsiElement(>)('>')
|
||||
Parenthesized expression
|
||||
PsiElement(()('(')
|
||||
PsiErrorElement:<expression> expected, got ')'
|
||||
<empty list>
|
||||
PsiElement())(')')
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
class MyClass implements Roo<String,
|
||||
Object> {}
|
||||
-----
|
||||
Groovy script
|
||||
Class definition
|
||||
Modifiers
|
||||
<empty list>
|
||||
PsiElement(class)('class')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(identifier)('MyClass')
|
||||
Extends clause
|
||||
<empty list>
|
||||
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(})('}')
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
class MyClass implements Roo<String
|
||||
,Object> {}
|
||||
-----
|
||||
Groovy script
|
||||
Class definition
|
||||
Modifiers
|
||||
<empty list>
|
||||
PsiElement(class)('class')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(identifier)('MyClass')
|
||||
Extends clause
|
||||
<empty list>
|
||||
PsiWhiteSpace(' ')
|
||||
Implements clause
|
||||
PsiElement(implements)('implements')
|
||||
PsiWhiteSpace(' ')
|
||||
Reference element
|
||||
PsiElement(identifier)('Roo')
|
||||
Type arguments
|
||||
PsiElement(<)('<')
|
||||
Type element
|
||||
Reference element
|
||||
PsiElement(identifier)('String')
|
||||
PsiErrorElement:'>' expected
|
||||
<empty list>
|
||||
PsiElement(new line)('\n')
|
||||
PsiErrorElement:'>' or '{' expected, got ','
|
||||
<empty list>
|
||||
PsiElement(,)(',')
|
||||
Relational expression
|
||||
Reference expression
|
||||
PsiElement(identifier)('Object')
|
||||
PsiElement(>)('>')
|
||||
PsiWhiteSpace(' ')
|
||||
Closable block
|
||||
PsiElement({)('{')
|
||||
Parameter list
|
||||
<empty list>
|
||||
PsiElement(})('}')
|
||||
Reference in New Issue
Block a user