mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-20 21:41:24 +07:00
[java-highlighting] IDEA-265053 Highlight not-wellformed statements without semicolon as a "not-a-statement"
Also suggest 'introduce local variable' as a quick-fix GitOrigin-RevId: 2a15a1d35ed1755ab043a40a06136ed946c4a516
This commit is contained in:
committed by
intellij-monorepo-bot
parent
8e045b6473
commit
b5f948dd5f
@@ -516,4 +516,6 @@ public abstract class QuickFixFactory {
|
||||
public abstract @NotNull IntentionAction createConvertInterfaceToClassFix(@NotNull PsiClass aClass);
|
||||
|
||||
public abstract @NotNull IntentionAction createUnwrapArrayInitializerMemberValueAction(@NotNull PsiArrayInitializerMemberValue arrayValue);
|
||||
|
||||
public abstract @NotNull IntentionAction createIntroduceVariableAction(@NotNull PsiExpression expression);
|
||||
}
|
||||
@@ -14,8 +14,10 @@ import com.intellij.codeInsight.daemon.impl.quickfix.*;
|
||||
import com.intellij.codeInsight.highlighting.HighlightUsagesDescriptionLocation;
|
||||
import com.intellij.codeInsight.intention.IntentionAction;
|
||||
import com.intellij.codeInsight.intention.QuickFixFactory;
|
||||
import com.intellij.codeInsight.intention.impl.PriorityIntentionActionWrapper;
|
||||
import com.intellij.codeInsight.quickfix.UnresolvedReferenceQuickFixProvider;
|
||||
import com.intellij.codeInspection.LocalQuickFixOnPsiElementAsIntentionAdapter;
|
||||
import com.intellij.core.JavaPsiBundle;
|
||||
import com.intellij.ide.IdeBundle;
|
||||
import com.intellij.java.analysis.JavaAnalysisBundle;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
@@ -1538,7 +1540,20 @@ public final class HighlightUtil {
|
||||
|
||||
|
||||
static HighlightInfo checkNotAStatement(@NotNull PsiStatement statement) {
|
||||
if (!PsiUtil.isStatement(statement) && !PsiUtilCore.hasErrorElementChild(statement)) {
|
||||
if (!PsiUtil.isStatement(statement)) {
|
||||
PsiElement anchor = statement;
|
||||
if (PsiUtilCore.hasErrorElementChild(statement)) {
|
||||
boolean allowedError = false;
|
||||
if (statement instanceof PsiExpressionStatement) {
|
||||
PsiElement[] children = statement.getChildren();
|
||||
if (children[0] instanceof PsiExpression && children[1] instanceof PsiErrorElement &&
|
||||
((PsiErrorElement)children[1]).getErrorDescription().equals(JavaPsiBundle.message("expected.semicolon"))) {
|
||||
allowedError = true;
|
||||
anchor = children[0];
|
||||
}
|
||||
}
|
||||
if (!allowedError) return null;
|
||||
}
|
||||
boolean isDeclarationNotAllowed = false;
|
||||
if (statement instanceof PsiDeclarationStatement) {
|
||||
PsiElement parent = statement.getParent();
|
||||
@@ -1546,8 +1561,13 @@ public final class HighlightUtil {
|
||||
}
|
||||
String description = JavaErrorBundle.message(isDeclarationNotAllowed ? "declaration.not.allowed" : "not.a.statement");
|
||||
HighlightInfo error =
|
||||
HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(statement).descriptionAndTooltip(description).create();
|
||||
HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(anchor).descriptionAndTooltip(description).create();
|
||||
if (statement instanceof PsiExpressionStatement) {
|
||||
PsiExpression expression = ((PsiExpressionStatement)statement).getExpression();
|
||||
if (statement.getParent() instanceof PsiCodeBlock) {
|
||||
QuickFixAction.registerQuickFixAction(error, PriorityIntentionActionWrapper
|
||||
.highPriority(getFixFactory().createIntroduceVariableAction(expression)));
|
||||
}
|
||||
QuickFixAction.registerQuickFixAction(error, getFixFactory().createDeleteSideEffectAwareFix((PsiExpressionStatement)statement));
|
||||
}
|
||||
return error;
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.codeInsight.daemon.impl.analysis;
|
||||
|
||||
import com.intellij.codeInsight.highlighting.HighlightErrorFilter;
|
||||
import com.intellij.core.JavaPsiBundle;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiErrorElement;
|
||||
import com.intellij.psi.PsiExpressionStatement;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class JavaHighlightErrorFilter extends HighlightErrorFilter {
|
||||
@Override
|
||||
public boolean shouldHighlightErrorElement(@NotNull PsiErrorElement element) {
|
||||
String description = element.getErrorDescription();
|
||||
if (description.equals(JavaPsiBundle.message("expected.semicolon"))) {
|
||||
PsiElement parent = element.getParent();
|
||||
if (parent instanceof PsiExpressionStatement && !PsiUtil.isStatement(parent)) {
|
||||
// unterminated expression statement which is not a statement at all:
|
||||
// let's report it as not-a-statement instead
|
||||
// (see HighlightUtil.checkNotAStatement); it's more visible and provides
|
||||
// more useful fixes.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -2101,6 +2101,7 @@
|
||||
</intentionAction>
|
||||
<externalAnnotationsArtifactsResolver implementation="com.intellij.jarRepository.ExternalAnnotationsRepositoryResolver"/>
|
||||
<errorQuickFixProvider implementation="com.intellij.codeInsight.daemon.impl.analysis.JavaErrorQuickFixProvider"/>
|
||||
<highlightErrorFilter implementation="com.intellij.codeInsight.daemon.impl.analysis.JavaHighlightErrorFilter"/>
|
||||
|
||||
<searchEverywhereResultsEqualityProvider implementation="com.intellij.ide.JavaClassAndFileEqualityProvider"/>
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.codeInsight.intention.impl;
|
||||
|
||||
import com.intellij.codeInsight.intention.HighPriorityAction;
|
||||
import com.intellij.codeInspection.LocalQuickFixAndIntentionActionOnPsiElement;
|
||||
import com.intellij.java.JavaBundle;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiExpression;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.refactoring.introduceVariable.IntroduceVariableHandler;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class IntroduceVariableErrorFixAction extends LocalQuickFixAndIntentionActionOnPsiElement implements HighPriorityAction {
|
||||
public IntroduceVariableErrorFixAction(@NotNull PsiExpression expression) {
|
||||
super(expression);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(@NotNull Project project,
|
||||
@NotNull PsiFile file,
|
||||
@Nullable Editor editor,
|
||||
@NotNull PsiElement startElement,
|
||||
@NotNull PsiElement endElement) {
|
||||
new IntroduceVariableHandler().invoke(project, editor, (PsiExpression)startElement);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startInWriteAction() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public @NotNull String getText() {
|
||||
return JavaBundle.message("intention.introduce.variable.text");
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getFamilyName() {
|
||||
return getText();
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.refactoring.BaseRefactoringIntentionAction;
|
||||
import com.intellij.refactoring.introduceVariable.IntroduceEmptyVariableHandler;
|
||||
import com.intellij.refactoring.introduceVariable.IntroduceVariableHandler;
|
||||
@@ -60,6 +61,12 @@ public class IntroduceVariableIntentionAction extends BaseRefactoringIntentionAc
|
||||
return false;
|
||||
}
|
||||
|
||||
if (expression.getParent() instanceof PsiExpressionStatement &&
|
||||
!PsiUtil.isStatement(expression.getParent())) {
|
||||
// Same action is available as an error quick-fix
|
||||
return false;
|
||||
}
|
||||
|
||||
final PsiType expressionType = expression.getType();
|
||||
return expressionType != null && !PsiType.VOID.equals(expressionType) && !(expression instanceof PsiAssignmentExpression);
|
||||
}
|
||||
|
||||
@@ -1009,4 +1009,9 @@ public final class QuickFixFactoryImpl extends QuickFixFactory {
|
||||
public @NotNull IntentionAction createUnwrapArrayInitializerMemberValueAction(@NotNull PsiArrayInitializerMemberValue arrayValue) {
|
||||
return new UnwrapArrayInitializerMemberValueAction(arrayValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull IntentionAction createIntroduceVariableAction(@NotNull PsiExpression expression) {
|
||||
return new IntroduceVariableErrorFixAction(expression);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,6 @@ class C {
|
||||
}
|
||||
|
||||
void f(int x, int y) {
|
||||
if (x == 0 ||<error descr="')' expected"><error descr="Expression expected"><error descr="Illegal character: U+00A0"> </error></error></error>y == 0<error descr="';' expected"><error descr="Unexpected token">)</error></error> { }
|
||||
if (x == 0 ||<error descr="')' expected"><error descr="Expression expected"><error descr="Illegal character: U+00A0"> </error></error></error><error descr="Not a statement">y == 0</error><error descr="Unexpected token">)</error> { }
|
||||
}
|
||||
}
|
||||
@@ -54,7 +54,7 @@ public class a12 {
|
||||
<error descr="Cannot resolve method 'xxxxxx' in 'a12'">xxxxxx</error>(<error descr="Cannot resolve symbol 'xxxxxx'">xxxxxx</error>);
|
||||
|
||||
// incomplete code should not cause 'expr expected'
|
||||
Object<error descr="';' expected"> </error>
|
||||
<error descr="Not a statement">Object</error>
|
||||
|
||||
|
||||
<error descr="Array type expected; found: 'int'">4</error>[1] = 5;
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
class C {
|
||||
void foo(int i, int j) {
|
||||
<error descr="Not a statement">123</error>
|
||||
<error descr="Not a statement">i+j</error> /*oops*/
|
||||
foo(1, 2)<EOLError descr="';' expected"></EOLError>
|
||||
i++<EOLError descr="';' expected"></EOLError>
|
||||
toString()<error descr="';' expected"> </error>/*oops*/
|
||||
}
|
||||
}
|
||||
@@ -397,6 +397,10 @@ public class LightAdvHighlightingTest extends LightDaemonAnalyzerTestCase {
|
||||
}
|
||||
|
||||
public void testUnreachableArrayElementAssignment() { doTest(false); }
|
||||
|
||||
public void testNotWellFormedExpressionStatementWithoutSemicolon() {
|
||||
doTest(false);
|
||||
}
|
||||
|
||||
public void testInsane() {
|
||||
configureFromFileText("x.java", "class X { \nx_x_x_x\n }");
|
||||
|
||||
@@ -35,7 +35,7 @@ class LightJava11HighlightingTest : LightJavaCodeInsightFixtureTestCase() {
|
||||
"""#!/path/to/java
|
||||
|class Main {{
|
||||
|int i = 0;
|
||||
|i*<error descr="';' expected"><error descr="Expression expected"><error descr="Unexpected token">*</error></error></error>;
|
||||
|i*<error descr="Expression expected"><error descr="Unexpected token">*</error></error>;
|
||||
|}}""".trimMargin())
|
||||
myFixture.checkHighlighting()
|
||||
Assert.assertTrue(HighlightClassUtil.isJavaHashBangScript(file))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
class F {
|
||||
{
|
||||
<error descr="Unclosed character literal">'/</error><EOLError descr="';' expected"></EOLError>
|
||||
<error descr="Unclosed character literal">'/</error>
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@ public class ConstantOnRHS
|
||||
class C {
|
||||
void t() {
|
||||
<error descr="Cannot resolve method 'method' in 'C'">method</error>(String.format("", <error descr="Expression expected">StringBuffer</error>)<error descr="',' or ')' expected">"</error>" +<EOLError descr="Expression expected"></EOLError>
|
||||
<<error descr="')' expected"><error descr="Expression expected">/</error></error><error descr="Cannot resolve symbol 'plugin'">plugin</error>><error descr="Illegal line end in string literal">" +</error><EOLError descr="';' expected"></EOLError>
|
||||
""<error descr="';' expected"><error descr="Unexpected token">)</error></error>;
|
||||
<<error descr="')' expected"><error descr="Expression expected">/</error></error><error descr="Cannot resolve symbol 'plugin'">plugin</error>><error descr="Illegal line end in string literal">" +</error>
|
||||
<error descr="Not a statement">""</error><error descr="Unexpected token">)</error>;
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ class A {
|
||||
}
|
||||
class B {
|
||||
B() {
|
||||
<error descr="Cannot resolve symbol 'sup'">sup</error><EOLError descr="';' expected"></EOLError>
|
||||
<error descr="Cannot resolve symbol 'sup'">sup</error>
|
||||
}
|
||||
|
||||
<warning descr="Implicit call to 'super()'">B</warning>(int i) {
|
||||
|
||||
@@ -151,6 +151,6 @@ public class UnnecessaryParenthesesInspection
|
||||
class A{
|
||||
|
||||
A() {
|
||||
((<error descr="Expression expected"><</error><error descr="Cannot resolve symbol 'x'">x</error>><error descr="Expression expected">)</error><EOLError descr="';' expected"></EOLError>
|
||||
((<error descr="Expression expected"><</error><error descr="Cannot resolve symbol 'x'">x</error>><error descr="Expression expected">)</error>
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user