From f1c027f3790fbd75875274be55173c9882e47114 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Wed, 10 Nov 2010 13:59:06 +0300 Subject: [PATCH] IDEA-57556 (blame C-style brackets after vararg parameter) --- .../lang/java/parser/DeclarationParser.java | 48 ++++++++++-------- .../lang/java/parser/ExpressionParser.java | 8 ++- .../lang/java/parser/ReferenceParser.java | 49 ++++++++++--------- .../lang/java/parser/StatementParser.java | 2 +- .../psi/impl/source/tree/JavaElementType.java | 4 +- .../declarations/VarArgBrackets.txt | 32 ++++++++++++ .../parser/partial/DeclarationParserTest.java | 1 + .../parser/partial/ReferenceParserTest.java | 23 ++++----- 8 files changed, 101 insertions(+), 66 deletions(-) create mode 100644 java/java-tests/testData/psi/parser-partial/declarations/VarArgBrackets.txt diff --git a/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java b/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java index 47b60ef7d599..f99d8355c309 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java @@ -357,9 +357,9 @@ public class DeclarationParser { @NotNull private static PsiBuilder.Marker parseTypeNotNull(final PsiBuilder builder) { - final ReferenceParser.TypeInfo typeInfo = ReferenceParser.parseType(builder); - assert typeInfo != null : builder.getOriginalText(); - return typeInfo.marker; + final PsiBuilder.Marker type = ReferenceParser.parseType(builder, ReferenceParser.EAT_LAST_DOT | ReferenceParser.WILDCARD); + assert type != null : builder.getOriginalText(); + return type; } @NotNull @@ -399,14 +399,7 @@ public class DeclarationParser { final boolean anno, final boolean constructor) { parseParameterList(builder); - if (constructor && builder.getTokenType() == JavaTokenType.LBRACKET) { - final PsiBuilder.Marker marker = builder.mark(); - eatBrackets(builder, false); - marker.error(JavaErrorMessages.message("expected.semicolon")); - } - else { - eatBrackets(builder, true); - } + eatBrackets(builder, constructor, JavaErrorMessages.message("expected.semicolon")); if (areTypeAnnotationsSupported(builder)) { final PsiBuilder.Marker receiver = builder.mark(); @@ -544,21 +537,23 @@ public class DeclarationParser { final PsiBuilder.Marker param = builder.mark(); final Pair modListInfo = parseModifierList(builder); - final PsiBuilder.Marker type = ellipsis ? ReferenceParser.parseTypeWithEllipsis(builder, true, true) : - ReferenceParser.parseType(builder, true, true, false); - if (type == null && modListInfo.second) { + int flags = ReferenceParser.EAT_LAST_DOT | ReferenceParser.WILDCARD; + if (ellipsis) flags |= ReferenceParser.ELLIPSIS; + final ReferenceParser.TypeInfo typeInfo = ReferenceParser.parseTypeInfo(builder, flags); + + if (typeInfo == null && modListInfo.second) { param.rollbackTo(); return null; } - if (type == null) { + if (typeInfo == null) { error(builder, JavaErrorMessages.message("expected.type")); emptyElement(builder, JavaElementType.TYPE); } if (expect(builder, JavaTokenType.IDENTIFIER)) { - eatBrackets(builder, true); + eatBrackets(builder, typeInfo != null && typeInfo.isVarArg, JavaErrorMessages.message("expected.rparen")); done(param, JavaElementType.PARAMETER); return param; } @@ -593,7 +588,7 @@ public class DeclarationParser { while (true) { shouldRollback = true; - if (!eatBrackets(builder, true)) { + if (!eatBrackets(builder, false, null)) { unclosed = true; } @@ -655,14 +650,25 @@ public class DeclarationParser { return declaration; } - private static boolean eatBrackets(final PsiBuilder builder, final boolean withError) { + private static boolean eatBrackets(final PsiBuilder builder, final boolean isError, @Nullable final String error) { + if (builder.getTokenType() != JavaTokenType.LBRACKET) return true; + + final PsiBuilder.Marker marker = isError ? builder.mark() : null; + + boolean result = true; while (expect(builder, JavaTokenType.LBRACKET)) { if (!expect(builder, JavaTokenType.RBRACKET)) { - if (withError) error(builder, JavaErrorMessages.message("expected.rbracket")); - return false; + if (!isError) error(builder, JavaErrorMessages.message("expected.rbracket")); + result = false; + break; } } - return true; + + if (marker != null) { + marker.error(error); + } + + return result; } @Nullable diff --git a/java/java-impl/src/com/intellij/lang/java/parser/ExpressionParser.java b/java/java-impl/src/com/intellij/lang/java/parser/ExpressionParser.java index a7d00b29c069..c04f0e698962 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/ExpressionParser.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/ExpressionParser.java @@ -161,8 +161,7 @@ public class ExpressionParser { return parseUnary(builder); case TYPE: - final ReferenceParser.TypeInfo typeInfo = ReferenceParser.parseType(builder); - return typeInfo != null ? typeInfo.marker : null; + return ReferenceParser.parseType(builder, ReferenceParser.EAT_LAST_DOT | ReferenceParser.WILDCARD); default: assert false : "Unexpected type: " + type; @@ -255,7 +254,7 @@ public class ExpressionParser { final PsiBuilder.Marker typeCast = builder.mark(); builder.advanceLexer(); - final ReferenceParser.TypeInfo typeInfo = ReferenceParser.parseType(builder); + final ReferenceParser.TypeInfo typeInfo = ReferenceParser.parseTypeInfo(builder, ReferenceParser.EAT_LAST_DOT | ReferenceParser.WILDCARD); if (typeInfo == null || builder.getTokenType() != JavaTokenType.RPARENTH) { typeCast.rollbackTo(); @@ -733,8 +732,7 @@ public class ExpressionParser { private static PsiBuilder.Marker parseClassObjectAccess(final PsiBuilder builder) { final PsiBuilder.Marker expr = builder.mark(); - final PsiBuilder.Marker type = ReferenceParser.parseType(builder, false, false, false); - if (type == null) { + if (ReferenceParser.parseType(builder, 0) == null) { expr.drop(); return null; } diff --git a/java/java-impl/src/com/intellij/lang/java/parser/ReferenceParser.java b/java/java-impl/src/com/intellij/lang/java/parser/ReferenceParser.java index baf996675c0e..fb87ee3275ef 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/ReferenceParser.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/ReferenceParser.java @@ -32,10 +32,16 @@ import static com.intellij.lang.java.parser.JavaParserUtil.emptyElement; public class ReferenceParser { + public static final int EAT_LAST_DOT = 0x01; + public static final int ELLIPSIS = 0x02; + public static final int WILDCARD = 0x04; + public static final int DIAMONDS = 0x08; + public static class TypeInfo { public boolean isPrimitive = false; public boolean isParameterized = false; public boolean isArray = false; + public boolean isVarArg = false; public boolean hasErrors = false; public PsiBuilder.Marker marker = null; } @@ -45,33 +51,19 @@ public class ReferenceParser { private ReferenceParser() { } @Nullable - public static TypeInfo parseType(final PsiBuilder builder) { - return parseTypeWithInfo(builder, true, true, false); - } - - @Nullable - public static PsiBuilder.Marker parseType(final PsiBuilder builder, final boolean eatLastDot, final boolean wildcard, final boolean diamonds) { - final TypeInfo typeInfo = parseTypeWithInfo(builder, eatLastDot, wildcard, diamonds); + public static PsiBuilder.Marker parseType(final PsiBuilder builder, final int flags) { + final TypeInfo typeInfo = parseTypeInfo(builder, flags); return typeInfo != null ? typeInfo.marker : null; } @Nullable - public static PsiBuilder.Marker parseTypeWithEllipsis(final PsiBuilder builder, final boolean eatLastDot, final boolean wildcard) { - final TypeInfo typeInfo = parseTypeWithInfo(builder, eatLastDot, wildcard, false); - if (typeInfo == null) return null; - - PsiBuilder.Marker type = typeInfo.marker; - if (builder.getTokenType() == JavaTokenType.ELLIPSIS) { - type = typeInfo.marker.precede(); - builder.advanceLexer(); - type.done(JavaElementType.TYPE); - } - - return type; + public static TypeInfo parseTypeInfo(final PsiBuilder builder, final int flags) { + return parseTypeInfo(builder, isSet(flags, EAT_LAST_DOT), isSet(flags, WILDCARD), isSet(flags, DIAMONDS), isSet(flags, ELLIPSIS)); } @Nullable - private static TypeInfo parseTypeWithInfo(final PsiBuilder builder, final boolean eatLastDot, final boolean wildcard, final boolean diamonds) { + private static TypeInfo parseTypeInfo(final PsiBuilder builder, + final boolean eatLastDot, final boolean wildcard, final boolean diamonds, final boolean ellipsis) { if (builder.getTokenType() == null) return null; final TypeInfo typeInfo = new TypeInfo(); @@ -126,18 +118,28 @@ public class ReferenceParser { type = type.precede(); } + if (ellipsis && builder.getTokenType() == JavaTokenType.ELLIPSIS) { + type = type.precede(); + builder.advanceLexer(); + type.done(JavaElementType.TYPE); + typeInfo.isVarArg = true; + } + typeInfo.marker = type; return typeInfo; } + private static boolean isSet(final int mask, final int flag) { + return (mask & flag) != 0; + } + @NotNull private static PsiBuilder.Marker parseWildcardType(final PsiBuilder builder) { final PsiBuilder.Marker type = builder.mark(); builder.advanceLexer(); if (expect(builder, WILDCARD_KEYWORD_SET)) { - final PsiBuilder.Marker boundType = parseType(builder, true, false, false); - if (boundType == null) { + if (parseTypeInfo(builder, EAT_LAST_DOT) == null) { error(builder, JavaErrorMessages.message("expected.type")); } } @@ -248,8 +250,7 @@ public class ReferenceParser { boolean isOk = true; while (true) { - final PsiBuilder.Marker type = parseType(builder, true, wildcard, diamonds); - if (type == null) { + if (parseTypeInfo(builder, true, wildcard, diamonds, false) == null) { error(builder, JavaErrorMessages.message("expected.identifier")); } diff --git a/java/java-impl/src/com/intellij/lang/java/parser/StatementParser.java b/java/java-impl/src/com/intellij/lang/java/parser/StatementParser.java index 6ccee7a13939..561e249289cb 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/StatementParser.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/StatementParser.java @@ -232,7 +232,7 @@ public class StatementParser { final PsiBuilder.Marker declStatement = builder.mark(); final PsiBuilder.Marker decl = DeclarationParser.parse(builder, DeclarationParser.Context.CODE_BLOCK); if (decl == null) { - ReferenceParser.parseType(builder, false, false, false); + ReferenceParser.parseType(builder, 0); error(builder, JavaErrorMessages.message("expected.identifier")); } done(declStatement, JavaElementType.DECLARATION_STATEMENT); diff --git a/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java b/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java index 4ce85e1d9090..d8d36729a3eb 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java @@ -253,8 +253,8 @@ public interface JavaElementType { return JavaParserUtil.parseFragment(chameleon, new JavaParserUtil.ParserWrapper() { public void parse(final PsiBuilder builder) { - ReferenceParser.parseType(builder); - expect(builder, JavaTokenType.ELLIPSIS); + ReferenceParser.parseType(builder, ReferenceParser.EAT_LAST_DOT | ReferenceParser.WILDCARD); + expect(builder, JavaTokenType.ELLIPSIS); // todo[r.sh] parse ellipsis and fix PsiTypeCodeFragmentImpl.getType() } }); } diff --git a/java/java-tests/testData/psi/parser-partial/declarations/VarArgBrackets.txt b/java/java-tests/testData/psi/parser-partial/declarations/VarArgBrackets.txt new file mode 100644 index 000000000000..da117679ea18 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/VarArgBrackets.txt @@ -0,0 +1,32 @@ +PsiJavaFile:VarArgBrackets.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiMethod:foo + PsiModifierList: + + PsiTypeParameterList + + PsiTypeElement:void + PsiKeyword:void('void') + PsiWhiteSpace(' ') + PsiIdentifier:foo('foo') + PsiParameterList:(int... x[]) + PsiJavaToken:LPARENTH('(') + PsiParameter:x + PsiModifierList: + + PsiTypeElement:int... + PsiTypeElement:int + PsiKeyword:int('int') + PsiJavaToken:ELLIPSIS('...') + PsiWhiteSpace(' ') + PsiIdentifier:x('x') + PsiErrorElement:')' expected + PsiJavaToken:LBRACKET('[') + PsiJavaToken:RBRACKET(']') + PsiJavaToken:RPARENTH(')') + PsiReferenceList + + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/DeclarationParserTest.java b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/DeclarationParserTest.java index 0638d948b230..be3eb7d94aa5 100644 --- a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/DeclarationParserTest.java +++ b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/DeclarationParserTest.java @@ -90,6 +90,7 @@ public class DeclarationParserTest extends JavaParsingTestCase { public void testUnclosed4() { doParserTest("{ void f(int a,, }", false, false); } public void testUnclosed5() { doParserTest("{ void f(int a,); }", false, false); } public void testConstructorBrackets() { doParserTest("{ A() [] { } }", false, false); } + public void testVarArgBrackets() { doParserTest("{ void foo(int... x[]); }", false, false); } public void testGenericMethod() { doParserTest("{ public static test();\n" + " void test1();\n" + diff --git a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ReferenceParserTest.java b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ReferenceParserTest.java index 6cd12d1baad5..805970e0b8bd 100644 --- a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ReferenceParserTest.java +++ b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ReferenceParserTest.java @@ -30,18 +30,18 @@ public class ReferenceParserTest extends JavaParsingTestCase { public void testReference1() { doRefParserTest("a.", true); } public void testReference2() { doRefParserTest("a.b", false); } - public void testType0() { doTypeParserTest("int", false); } - public void testType1() { doTypeParserTest("a.b", false); } - public void testType2() { doTypeParserTest("int[]", false); } - public void testType3() { doTypeParserTest("int[][", false); } - public void testType4() { doTypeParserTest("Map>", false); } - public void testType5() { doTypeParserTest("Object[]...", false); } + public void testType0() { doTypeParserTest("int"); } + public void testType1() { doTypeParserTest("a.b"); } + public void testType2() { doTypeParserTest("int[]"); } + public void testType3() { doTypeParserTest("int[]["); } + public void testType4() { doTypeParserTest("Map>"); } + public void testType5() { doTypeParserTest("Object[]..."); } public void testType6() { withLevel(LanguageLevel.JDK_1_7, new Runnable() { @Override - public void run() { doTypeParserTest("@English String @NonEmpty []", false); } }); + public void run() { doTypeParserTest("@English String @NonEmpty []"); } }); } - public void testType7() { doTypeParserTest("Diamond<>", true); } + public void testType7() { doTypeParserTest("Diamond<>"); } public void testTypeParams0() { doTypeParamsParserTest(""); } public void testTypeParams1() { doTypeParamsParserTest(""); } @@ -61,14 +61,11 @@ public class ReferenceParserTest extends JavaParsingTestCase { }); } - private void doTypeParserTest(final String text, final boolean diamonds) { + private void doTypeParserTest(final String text) { doParserTest(text, new TestParser() { @Override public void parse(final PsiBuilder builder) { - if (diamonds) - ReferenceParser.parseType(builder, false, false, true); - else - ReferenceParser.parseTypeWithEllipsis(builder, false, false); + ReferenceParser.parseType(builder, ReferenceParser.DIAMONDS | ReferenceParser.ELLIPSIS); } }); }