[groovy] parser: don't recover in type argument lists (IDEA-191728)

This commit is contained in:
Daniil Ovchinnikov
2018-05-22 15:25:15 +03:00
parent a0a54aeb79
commit c69cba6c09
8 changed files with 208 additions and 95 deletions
@@ -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
@@ -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)
@@ -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())(')')
@@ -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(})('}')
@@ -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(})('}')