[java] invalid "for" statements highlighting (IDEA-167353)

This commit is contained in:
Roman Shevchenko
2017-02-06 19:01:38 +01:00
parent b97defb6e2
commit 66728e4f71
6 changed files with 102 additions and 21 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -2929,14 +2929,16 @@ public class HighlightUtil extends HighlightUtilBase {
@Nullable
static HighlightInfo checkForStatement(@NotNull PsiForStatement statement) {
PsiStatement init = statement.getInitialization();
if (!(init == null || init instanceof PsiEmptyStatement ||
init instanceof PsiDeclarationStatement ||
init instanceof PsiExpressionStatement || init instanceof PsiExpressionListStatement)) {
String message = JavaErrorMessages.message("invalid.statement");
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(init).descriptionAndTooltip(message).create();
if (init == null ||
init instanceof PsiEmptyStatement ||
init instanceof PsiDeclarationStatement && ArrayUtil.getFirstElement(((PsiDeclarationStatement)init).getDeclaredElements()) instanceof PsiLocalVariable ||
init instanceof PsiExpressionStatement ||
init instanceof PsiExpressionListStatement) {
return null;
}
return null;
String message = JavaErrorMessages.message("invalid.statement");
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(init).descriptionAndTooltip(message).create();
}
private static void registerChangeParameterClassFix(PsiType lType, PsiType rType, HighlightInfo info) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -381,7 +381,7 @@ public class StatementParser {
final PsiBuilder.Marker param = myParser.getDeclarationParser().parseParameter(builder, false, false);
if (param == null || exprType(param) != JavaElementType.PARAMETER || builder.getTokenType() != JavaTokenType.COLON) {
afterParenth.rollbackTo();
return parseForLoopFromInitialization(builder, statement);
return parseForLoopFromInitializer(builder, statement);
}
else {
afterParenth.drop();
@@ -390,9 +390,9 @@ public class StatementParser {
}
@NotNull
private PsiBuilder.Marker parseForLoopFromInitialization(final PsiBuilder builder, final PsiBuilder.Marker statement) {
final PsiBuilder.Marker init = parseStatement(builder);
if (init == null){
private PsiBuilder.Marker parseForLoopFromInitializer(PsiBuilder builder, PsiBuilder.Marker statement) {
PsiBuilder.Marker init = parseStatement(builder);
if (init == null) {
error(builder, JavaErrorMessages.message("expected.statement"));
if (!expect(builder, JavaTokenType.RPARENTH)) {
done(statement, JavaElementType.FOR_STATEMENT);
@@ -400,9 +400,18 @@ public class StatementParser {
}
}
else {
myParser.getExpressionParser().parse(builder);
boolean missingSemicolon = false;
if (getLastToken(builder) != JavaTokenType.SEMICOLON) {
missingSemicolon = !expectOrError(builder, JavaTokenType.SEMICOLON, "expected.semicolon");
}
PsiBuilder.Marker expr = myParser.getExpressionParser().parse(builder);
missingSemicolon &= expr == null;
if (!expect(builder, JavaTokenType.SEMICOLON)) {
error(builder, JavaErrorMessages.message("expected.semicolon"));
if (!missingSemicolon) {
error(builder, JavaErrorMessages.message("expected.semicolon"));
}
if (!expect(builder, JavaTokenType.RPARENTH)) {
done(statement, JavaElementType.FOR_STATEMENT);
return statement;
@@ -418,7 +427,7 @@ public class StatementParser {
}
}
final PsiBuilder.Marker bodyStatement = parseStatement(builder);
PsiBuilder.Marker bodyStatement = parseStatement(builder);
if (bodyStatement == null) {
error(builder, JavaErrorMessages.message("expected.statement"));
}
@@ -427,6 +436,13 @@ public class StatementParser {
return statement;
}
private static IElementType getLastToken(PsiBuilder builder) {
IElementType token;
int offset = -1;
while (ElementType.JAVA_COMMENT_OR_WHITESPACE_BIT_SET.contains((token = builder.rawLookup(offset)))) offset--;
return token;
}
private void parseExpressionOrExpressionList(final PsiBuilder builder) {
final PsiBuilder.Marker expr = myParser.getExpressionParser().parse(builder);
if (expr == null) return;

View File

@@ -1,5 +1,5 @@
class a {
class ff { }
static class ff { }
void f() {
<error descr="Continue outside of loop">continue;</error>
@@ -61,5 +61,6 @@ class a {
for (<error descr="Not a statement">i==0?7:8;</error> ; ) ;
for (<error descr="Invalid statement">if (i<0) i++;</error> ; ) ;
for (new ff(), new ff(); ; ) ;
for (<error descr="Invalid statement">class C { }</error>; true; ) ;
}
}
}

View File

@@ -0,0 +1,32 @@
PsiJavaFile:ForInvalid0.java
PsiForStatement
PsiKeyword:for('for')
PsiJavaToken:LPARENTH('(')
PsiIfStatement
PsiKeyword:if('if')
PsiWhiteSpace(' ')
PsiJavaToken:LPARENTH('(')
PsiBinaryExpression:i<0
PsiReferenceExpression:i
PsiReferenceParameterList
<empty list>
PsiIdentifier:i('i')
PsiJavaToken:LT('<')
PsiLiteralExpression:0
PsiJavaToken:INTEGER_LITERAL('0')
PsiJavaToken:RPARENTH(')')
PsiWhiteSpace(' ')
PsiExpressionStatement
PsiPostfixExpression:i++
PsiReferenceExpression:i
PsiReferenceParameterList
<empty list>
PsiIdentifier:i('i')
PsiJavaToken:PLUSPLUS('++')
PsiJavaToken:SEMICOLON(';')
PsiWhiteSpace(' ')
PsiJavaToken:SEMICOLON(';')
PsiJavaToken:RPARENTH(')')
PsiWhiteSpace(' ')
PsiEmptyStatement
PsiJavaToken:SEMICOLON(';')

View File

@@ -0,0 +1,28 @@
PsiJavaFile:ForInvalid1.java
PsiForStatement
PsiKeyword:for('for')
PsiJavaToken:LPARENTH('(')
PsiDeclarationStatement
PsiClass:C
PsiModifierList:
<empty list>
PsiKeyword:class('class')
PsiWhiteSpace(' ')
PsiIdentifier:C('C')
PsiTypeParameterList
<empty list>
PsiReferenceList
<empty list>
PsiReferenceList
<empty list>
PsiWhiteSpace(' ')
PsiJavaToken:LBRACE('{')
PsiWhiteSpace(' ')
PsiJavaToken:RBRACE('}')
PsiJavaToken:SEMICOLON(';')
PsiWhiteSpace(' ')
PsiJavaToken:SEMICOLON(';')
PsiJavaToken:RPARENTH(')')
PsiWhiteSpace(' ')
PsiEmptyStatement
PsiJavaToken:SEMICOLON(';')

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -46,8 +46,6 @@ public class StatementParserTest extends JavaParsingTestCase {
public void testLocalVar0() { doParserTest("List<Integer> list;"); }
public void testLocalVar1() { doParserTest("p.@A T<P> x;"); }
public void testFor() { doParserTest("for(Iterator<String> it = null; it.hasNext();) { String s = it.next(); }"); }
public void testDoNormal() { doParserTest("do{}while(true);"); }
public void testDoIncomplete0() { doParserTest("do"); }
public void testDoIncomplete1() { doParserTest("do foo();"); }
@@ -57,9 +55,9 @@ public class StatementParserTest extends JavaParsingTestCase {
public void testDoIncomplete5() { doParserTest("do foo(); while(\n g();"); }
public void testDoIncomplete6() { doParserTest("do foo(); while(cond)"); }
public void testFor() { doParserTest("for(Iterator<String> it = null; it.hasNext();) { String s = it.next(); }"); }
public void testForNormal0() { doParserTest("for(int i = 0; i < 10; i++)\n ;"); }
public void testForNormal1() { doParserTest("for( ; ; ) foo();"); }
public void testForEach() { doParserTest("for(Object o : map.entrySet()) ;"); }
public void testForIncomplete0() { doParserTest("for"); }
public void testForIncomplete1() { doParserTest("for("); }
public void testForIncomplete2() { doParserTest("for(int i = 0;"); }
@@ -70,6 +68,10 @@ public class StatementParserTest extends JavaParsingTestCase {
public void testForIncomplete7() { doParserTest("for() foo();"); }
public void testForIncomplete8() { doParserTest("for(int i = 0;) foo();"); }
public void testForIncomplete9() { doParserTest("for(int i = 0; i < 0) foo();"); }
public void testForInvalid0() { doParserTest("for(if (i<0) i++; ;) ;"); }
public void testForInvalid1() { doParserTest("for(class C { }; ;) ;"); }
public void testForEach() { doParserTest("for(Object o : map.entrySet()) ;"); }
public void testForEachIncomplete0() { doParserTest("for(Object : list) ;"); }
public void testIfNormalWithElse() { doParserTest("if (a){ f1(); } else{ f2(); }"); }