IDEA-57556 (blame C-style brackets after vararg parameter)

This commit is contained in:
Roman Shevchenko
2010-11-11 11:54:00 +03:00
parent 85b4c00117
commit f1c027f379
8 changed files with 101 additions and 66 deletions
@@ -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<PsiBuilder.Marker, Boolean> 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
@@ -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;
}
@@ -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"));
}
@@ -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);
@@ -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()
}
});
}
@@ -0,0 +1,32 @@
PsiJavaFile:VarArgBrackets.java
PsiJavaToken:LBRACE('{')
PsiWhiteSpace(' ')
PsiMethod:foo
PsiModifierList:
<empty list>
PsiTypeParameterList
<empty list>
PsiTypeElement:void
PsiKeyword:void('void')
PsiWhiteSpace(' ')
PsiIdentifier:foo('foo')
PsiParameterList:(int... x[])
PsiJavaToken:LPARENTH('(')
PsiParameter:x
PsiModifierList:
<empty list>
PsiTypeElement:int...
PsiTypeElement:int
PsiKeyword:int('int')
PsiJavaToken:ELLIPSIS('...')
PsiWhiteSpace(' ')
PsiIdentifier:x('x')
PsiErrorElement:')' expected
PsiJavaToken:LBRACKET('[')
PsiJavaToken:RBRACKET(']')
PsiJavaToken:RPARENTH(')')
PsiReferenceList
<empty list>
PsiJavaToken:SEMICOLON(';')
PsiWhiteSpace(' ')
PsiJavaToken:RBRACE('}')
@@ -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 <E> test();\n" +
" <E> void test1();\n" +
@@ -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<String,List<String>>", 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<String,List<String>>"); }
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("<T>"); }
public void testTypeParams1() { doTypeParamsParserTest("<T, U>"); }
@@ -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);
}
});
}