mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-19 13:02:30 +07:00
[java-highlighting] IDEA-369375 A syntax error (PsiErrorElement) should suppress the surrounding error
Also: allow a custom highlighter to supersede the default one GitOrigin-RevId: c7d7a8be3ef762c9516ed3b637d467d4b544af00
This commit is contained in:
committed by
intellij-monorepo-bot
parent
daba57cf6e
commit
b6806f1e7b
@@ -82,6 +82,17 @@ public final class JavaErrorKinds {
|
||||
});
|
||||
public static final Simple<PsiErrorElement> SYNTAX_ERROR =
|
||||
error(PsiErrorElement.class, "syntax.error")
|
||||
.withRange(e -> {
|
||||
TextRange range = e.getTextRange();
|
||||
if (range.getLength() == 0) {
|
||||
PsiFile file = e.getContainingFile();
|
||||
int endOffset = range.getEndOffset();
|
||||
if (endOffset < file.getTextLength() && file.getFileDocument().getCharsSequence().charAt(endOffset) != '\n') {
|
||||
return TextRange.from(0, 1);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.withDescription(e -> message("syntax.error", e.getErrorDescription()));
|
||||
public static final Parameterized<PsiElement, JavaPreviewFeatureUtil.PreviewFeatureUsage> PREVIEW_API_USAGE =
|
||||
parameterized(PsiElement.class, JavaPreviewFeatureUtil.PreviewFeatureUsage.class, "preview.api.usage")
|
||||
|
||||
@@ -4,6 +4,7 @@ package com.intellij.codeInsight.daemon.impl.analysis;
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightVisitor;
|
||||
import com.intellij.codeInsight.highlighting.HighlightErrorFilter;
|
||||
import com.intellij.codeInsight.intention.CommonIntentionAction;
|
||||
import com.intellij.codeInsight.quickfix.UnresolvedReferenceQuickFixProvider;
|
||||
import com.intellij.codeInspection.ex.GlobalInspectionContextBase;
|
||||
@@ -88,6 +89,11 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
return file instanceof PsiImportHolder && !InjectedLanguageManager.getInstance(file.getProject()).isInjectedFragment(file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supersedesDefaultHighlighter() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(@NotNull PsiElement element) {
|
||||
element.accept(this);
|
||||
@@ -122,9 +128,12 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
myCollector = new JavaErrorCollector(file, error -> reportError(error, holder));
|
||||
}
|
||||
|
||||
private void reportError(@NotNull JavaCompilationError<?, ?> error,
|
||||
@NotNull HighlightInfoHolder holder) {
|
||||
if (error.kind() == SYNTAX_ERROR) return; // reported by DefaultHighlightVisitor
|
||||
private void reportError(@NotNull JavaCompilationError<?, ?> error, @NotNull HighlightInfoHolder holder) {
|
||||
if (error.psiForKind(SYNTAX_ERROR)
|
||||
.filter(e -> HighlightErrorFilter.EP_NAME.findFirstSafe(e.getProject(), filter -> !filter.shouldHighlightErrorElement(e)) != null)
|
||||
.isPresent()) {
|
||||
return;
|
||||
}
|
||||
JavaErrorHighlightType javaHighlightType = error.highlightType();
|
||||
HighlightInfoType type = switch (javaHighlightType) {
|
||||
case ERROR, FILE_LEVEL_ERROR -> HighlightInfoType.ERROR;
|
||||
|
||||
@@ -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 descr="Unexpected token"> </error></error></error></error><error descr="Not a statement">y == 0</error><error descr="Unexpected token">)</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> { }
|
||||
}
|
||||
}
|
||||
@@ -122,7 +122,7 @@ class array {
|
||||
{
|
||||
int[] a1 =<error descr="Expression expected"> </error><error descr="Unexpected token">.</error>new <error descr="Cannot resolve symbol 'C'">C</error>[0];
|
||||
int[] a2 = {}.new <error descr="Cannot resolve symbol 'D'">D</error>[0];
|
||||
int[] a3 = <error descr="Lambda expressions are not supported at language level '1.4'">t -></error><error descr="'{' expected"> </error>.new <error descr="Cannot resolve symbol 'E'">E</error>[0];
|
||||
int[] a3 = t -><error descr="'{' expected"> </error>.new <error descr="Cannot resolve symbol 'E'">E</error>[0];
|
||||
int[] a4 = <error descr="Cannot resolve symbol 'a'">a</error>::<error descr="';' expected"><error descr="Identifier expected"><error descr="Unexpected token">.</error></error></error>new <error descr="Cannot resolve symbol 'F'">F</error>[0];
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ class Test {
|
||||
public static void main(String[] args) {
|
||||
Box<String> stringBox = new Box<String>("123");
|
||||
|
||||
stringBox.transform(new <error descr="Class 'Anonymous class derived from Fn' must implement abstract method 'apply(A)' in 'Fn'">Fn<String,<error descr="Identifier expected"> </error>></error>() {});
|
||||
stringBox.transform(new Fn<String,<error descr="Identifier expected"> </error>>() {});
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -54,10 +54,10 @@ class X {
|
||||
}
|
||||
|
||||
int instanceofTest(Object obj) {
|
||||
if (obj instanceof<error descr="')' expected"><error descr="Type expected"> </error></error><error descr="Not a statement">(Integer</error><error descr="')' expected"> </error><error descr="Cannot resolve symbol 'i'">i</error> && predicate()<error descr="Unexpected token">)</error><error descr="Unexpected token">)</error> {
|
||||
if (obj instanceof<error descr="')' expected"><error descr="Type expected"> </error></error>(Integer<error descr="')' expected"> </error><error descr="Cannot resolve symbol 'i'">i</error> && predicate()<error descr="Unexpected token">)</error><error descr="Unexpected token">)</error> {
|
||||
return 1;
|
||||
}
|
||||
if (obj instanceof<error descr="')' expected"><error descr="Type expected"> </error></error><error descr="Not a statement">(String</error><error descr="')' expected"> </error><error descr="Cannot resolve symbol 's'">s</error><error descr="Unexpected token">)</error><error descr="Unexpected token">)</error> {
|
||||
if (obj instanceof<error descr="')' expected"><error descr="Type expected"> </error></error>(String<error descr="')' expected"> </error><error descr="Cannot resolve symbol 's'">s</error><error descr="Unexpected token">)</error><error descr="Unexpected token">)</error> {
|
||||
return 3;
|
||||
}
|
||||
return 2;
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
// IDEA-369310
|
||||
@SuppressWarnings(<error descr="Attribute value must be constant">"unused".</error><error descr="Identifier expected">)</error>
|
||||
@SuppressWarnings("unused".<error descr="Identifier expected">)</error>
|
||||
class X {}
|
||||
@@ -13,7 +13,7 @@ public class LombokDumbModeApplication {
|
||||
.name("2")
|
||||
.surname("3")
|
||||
.email("4")
|
||||
.<error descr="Incompatible types. Found: 'capture<?>', required: 'UserDao'">name</error>("1")<error descr="';' expected">a</error>
|
||||
.name("1")<error descr="';' expected">a</error>
|
||||
.<info descr="Not resolved until the project is fully loaded">id</info>(1)
|
||||
.<info descr="Not resolved until the project is fully loaded">build</info>();
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ class IncompleteSwitch {
|
||||
int i = switch (o) {
|
||||
case '2':
|
||||
yield 2;
|
||||
case <error descr="Primitive types in patterns, instanceof and switch are not supported at language level '21'">char a</error> when a == '1'<EOLError descr="':' or '->' expected"></EOLError>
|
||||
case char a when a == '1'<EOLError descr="':' or '->' expected"></EOLError>
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,8 +149,8 @@ public class ConditionCoveredByFurtherCondition {
|
||||
}
|
||||
|
||||
void testErrorElement(Object obj) {
|
||||
if(!(obj instanceof Integer) && !(obj instanceof Long) && <error descr="Operator '!' cannot be applied to 'java.lang.Object'">!(obj</error><error descr="')' expected"><error descr="')' expected"> </error></error><error descr="Not a statement">Number</error><error descr="Unexpected token">)</error><error descr="Unexpected token">)</error> {}
|
||||
if(<warning descr="Condition '!(obj instanceof Integer)' covered by subsequent condition '!(obj instanceof Number)'">!(obj instanceof Integer)</warning> && !(obj instanceof Number) && <error descr="Operator '!' cannot be applied to 'java.lang.Object'">!(obj</error><error descr="')' expected"><error descr="')' expected"> </error></error><error descr="Not a statement">Number</error><error descr="Unexpected token">)</error><error descr="Unexpected token">)</error> {}
|
||||
if(!(obj instanceof Integer) && !(obj instanceof Long) && !(obj<error descr="')' expected"><error descr="')' expected"> </error></error><error descr="Not a statement">Number</error><error descr="Unexpected token">)</error><error descr="Unexpected token">)</error> {}
|
||||
if(<warning descr="Condition '!(obj instanceof Integer)' covered by subsequent condition '!(obj instanceof Number)'">!(obj instanceof Integer)</warning> && !(obj instanceof Number) && !(obj<error descr="')' expected"><error descr="')' expected"> </error></error><error descr="Not a statement">Number</error><error descr="Unexpected token">)</error><error descr="Unexpected token">)</error> {}
|
||||
}
|
||||
|
||||
void testErrorElement2(char ch) {
|
||||
@@ -167,7 +167,7 @@ public class ConditionCoveredByFurtherCondition {
|
||||
}
|
||||
|
||||
void testIncompleteLambda2(Object x) {
|
||||
if (x != null && <error descr="Unexpected lambda expression">() -> x instanceof</error><error descr="')' expected"><error descr="Type expected"> </error></error>
|
||||
if (x != null && () -> x instanceof<error descr="')' expected"><error descr="Type expected"> </error></error>
|
||||
}
|
||||
|
||||
void testBooleanChain(boolean b1, boolean b2) {
|
||||
|
||||
@@ -4,7 +4,7 @@ class UnnecessaryUnicodeEscape {
|
||||
// <warning descr="Unicode escape sequence '\uuuuuu0061' can be replaced with 'a'">\uuuuuu0061</warning><warning descr="Unicode escape sequence '\u0062' can be replaced with 'b'">\u0062</warning>
|
||||
// control char & not representable char: \u0010 \u00e4
|
||||
|
||||
char[] surrogates = new char[]{'\ud800','\udc00'};<warning descr="Unicode escape sequence '\u0021' can be replaced with '!'"><error descr="Illegal character: \ (U+005C)"><error descr="Unexpected token">\</error></error><error descr="Cannot resolve symbol 'u0021'">u0021</error></warning><EOLError descr="Identifier expected"></EOLError>
|
||||
char[] surrogates = new char[]{'\ud800','\udc00'};<warning descr="Unicode escape sequence '\u0021' can be replaced with '!'"><error descr="Illegal character: \ (U+005C)">\</error><error descr="Cannot resolve symbol 'u0021'">u0021</error></warning><EOLError descr="Identifier expected"></EOLError>
|
||||
|
||||
String t = "<warning descr="Unicode escape sequence '\u0020' can be replaced with ' '">\u0020</warning>";
|
||||
String u = "\u200B\u200E\u00A0\u200F";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
class Test {
|
||||
int testIncomplete(Object obj) {
|
||||
return switch(<error descr="'switch' expression does not cover all possible input values">obj</error>) {
|
||||
return switch(obj) {
|
||||
case String s when<EOLError descr="Expression expected"></EOLError><EOLError descr="':' or '->' expected"></EOLError>
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
class Test {
|
||||
int testIncomplete(Object obj) {
|
||||
return switch(<error descr="'switch' expression does not cover all possible input values">obj</error>) {
|
||||
return switch(obj) {
|
||||
case String s when<EOLError descr="Expression expected"></EOLError><EOLError descr="':' or '->' expected"></EOLError>
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1996,7 +1996,7 @@ public class DaemonRespondToChangesTest extends DaemonAnalyzerTestCase {
|
||||
void f() {
|
||||
//XXX
|
||||
|
||||
<caret> # // Unexpected token
|
||||
<caret> : // Unexpected token
|
||||
|
||||
}
|
||||
}""";
|
||||
|
||||
@@ -18,5 +18,5 @@ public class CachedNumberConstructorCallInspectionTest extends LightJavaInspecti
|
||||
public void testSimple() { doStatementTest("new /*Number constructor call with primitive argument*/Integer/**/(1);"); }
|
||||
public void testStringArgument() { doStatementTest("new /*Number constructor call with primitive argument*/Byte/**/(\"1\");"); }
|
||||
public void testNoWarn() { doStatementTest("Long.valueOf(1L);"); }
|
||||
public void testNoAssertionError() { doStatementTest("Integer i = new /*!Cannot inherit from final class 'java.lang.Integer'*/Integer/*!*/(new String/*!'(' or '[' expected*//*!',' or ')' expected*/{/*!*//*!*/}/*!';' expected*//*!Unexpected token*/)/*!*//*!*/;"); }
|
||||
public void testNoAssertionError() { doStatementTest("Integer i = new Integer(new String/*!'(' or '[' expected*//*!',' or ')' expected*/{/*!*//*!*/}/*!';' expected*//*!Unexpected token*/)/*!*//*!*/;"); }
|
||||
}
|
||||
|
||||
@@ -738,6 +738,7 @@ com.intellij.codeInsight.daemon.impl.HighlightVisitor
|
||||
- a:clone():com.intellij.codeInsight.daemon.impl.HighlightVisitor
|
||||
- order():I
|
||||
- a:suitableForFile(com.intellij.psi.PsiFile):Z
|
||||
- supersedesDefaultHighlighter():Z
|
||||
- a:visit(com.intellij.psi.PsiElement):V
|
||||
com.intellij.codeInsight.daemon.impl.HighlightingSession
|
||||
- a:getColorsScheme():com.intellij.openapi.editor.colors.EditorColorsScheme
|
||||
|
||||
@@ -21,6 +21,13 @@ public interface HighlightVisitor extends PossiblyDumbAware {
|
||||
|
||||
boolean suitableForFile(@NotNull PsiFile file);
|
||||
|
||||
/**
|
||||
* @return true if this highlighter covers the errors reported by {@link DefaultHighlightVisitor}, so the latter should be turned off.
|
||||
*/
|
||||
default boolean supersedesDefaultHighlighter() {
|
||||
return false;
|
||||
}
|
||||
|
||||
void visit(@NotNull PsiElement element);
|
||||
|
||||
boolean analyze(@NotNull PsiFile file, boolean updateWholeFile, @NotNull HighlightInfoHolder holder, @NotNull Runnable action);
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.intellij.concurrency.JobLauncher;
|
||||
import com.intellij.lang.annotation.HighlightSeverity;
|
||||
import com.intellij.openapi.editor.colors.TextAttributesScheme;
|
||||
import com.intellij.openapi.progress.ProcessCanceledException;
|
||||
import com.intellij.openapi.progress.ProgressIndicatorProvider;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.project.DumbService;
|
||||
import com.intellij.openapi.project.IndexNotReadyException;
|
||||
@@ -20,7 +21,6 @@ import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.intellij.util.ExceptionUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||
import it.unimi.dsi.fastutil.longs.LongList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -67,6 +67,13 @@ class HighlightVisitorRunner {
|
||||
// or we can just clone the visitor, which is not expensive, given that all overrides are just a single new() call.
|
||||
cloned = visitor.clone();
|
||||
assert cloned.getClass() == visitor.getClass() : visitor.getClass()+".clone() must return a copy of "+visitor.getClass()+"; but got: "+cloned+" ("+cloned.getClass()+")";
|
||||
if (cloned.supersedesDefaultHighlighter()) {
|
||||
int index = ContainerUtil.indexOf(clones, e -> e instanceof DefaultHighlightVisitor);
|
||||
if (index >= 0) {
|
||||
clones[index] = cloned;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
clones[o++] = cloned;
|
||||
}
|
||||
}
|
||||
@@ -97,17 +104,14 @@ class HighlightVisitorRunner {
|
||||
boolean myUpdateAll,
|
||||
@NotNull Supplier<? extends HighlightInfoHolder> infoHolderProducer,
|
||||
@NotNull ResultSink resultSink) {
|
||||
List<? extends VisitorInfo> visitorInfos = ContainerUtil.map(visitors, v -> new VisitorInfo(v, new HashSet<>(), infoHolderProducer.get()));
|
||||
List<VisitorInfo> visitorInfos = ContainerUtil.map(visitors, v -> new VisitorInfo(v, new HashSet<>(), infoHolderProducer.get()));
|
||||
// first, run all visitors in parallel on all visible elements, then run all visitors in parallel on all invisible elements
|
||||
List<PsiElement> elements = ContainerUtil.concat(elements1, elements2);
|
||||
LongList ranges = new LongArrayList();
|
||||
ranges.addAll(ranges1);
|
||||
ranges.addAll(ranges2);
|
||||
if (GeneralHighlightingPass.LOG.isDebugEnabled()) {
|
||||
GeneralHighlightingPass.LOG.debug("HighlightVisitorRunner: visitors: " + Arrays.toString(visitors)+"; myRestrictRange="+myRestrictRange+"; psiFile="+psiFile);
|
||||
}
|
||||
boolean res =
|
||||
JobLauncher.getInstance().invokeConcurrentlyUnderProgress(visitorInfos, ProgressManager.getGlobalProgressIndicator(), visitorInfo -> {
|
||||
JobLauncher.getInstance().invokeConcurrentlyUnderProgress(visitorInfos, ProgressIndicatorProvider.getGlobalProgressIndicator(), visitorInfo -> {
|
||||
HighlightVisitor visitor = visitorInfo.visitor();
|
||||
if (GeneralHighlightingPass.LOG.isDebugEnabled()) {
|
||||
GeneralHighlightingPass.LOG.debug("HighlightVisitorRunner: running visitor: " + visitor+"("+visitor.getClass()+"); psiFile="+psiFile+"; "+Thread.currentThread());
|
||||
@@ -117,7 +121,7 @@ class HighlightVisitorRunner {
|
||||
HighlightInfoHolder holder = visitorInfo.holder();
|
||||
boolean result = visitor.analyze(psiFile, myUpdateAll, holder, () -> {
|
||||
reportOutOfRunVisitorInfos(0, ANALYZE_BEFORE_RUN_VISITOR_FAKE_PSI_ELEMENT, holder, visitor, resultSink);
|
||||
runVisitor(psiFile, myRestrictRange, elements, ranges, chunkSize, visitorInfo.skipParentsSet(), holder, forceHighlightParents, visitor, resultSink);
|
||||
runVisitor(psiFile, myRestrictRange, elements, chunkSize, visitorInfo.skipParentsSet(), holder, forceHighlightParents, visitor, resultSink);
|
||||
sizeAfterRunVisitor[0] = holder.size();
|
||||
});
|
||||
reportOutOfRunVisitorInfos(sizeAfterRunVisitor[0], ANALYZE_AFTER_RUN_VISITOR_FAKE_PSI_ELEMENT, holder, visitor, resultSink);
|
||||
@@ -167,16 +171,15 @@ class HighlightVisitorRunner {
|
||||
resultSink.accept(visitor.getClass(), fakePsiElement, newInfos);
|
||||
}
|
||||
|
||||
private static void runVisitor(@NotNull PsiFile psiFile,
|
||||
@NotNull TextRange myRestrictRange,
|
||||
@NotNull List<? extends PsiElement> elements,
|
||||
@NotNull LongList ranges,
|
||||
int chunkSize,
|
||||
@NotNull Set<? super PsiElement> skipParentsSet,
|
||||
@NotNull HighlightInfoHolder holder,
|
||||
boolean forceHighlightParents,
|
||||
@NotNull HighlightVisitor visitor,
|
||||
@NotNull ResultSink resultSink) {
|
||||
private static void runVisitor(@NotNull PsiFile psiFile,
|
||||
@NotNull TextRange myRestrictRange,
|
||||
@NotNull List<? extends PsiElement> elements,
|
||||
int chunkSize,
|
||||
@NotNull Set<? super PsiElement> skipParentsSet,
|
||||
@NotNull HighlightInfoHolder holder,
|
||||
boolean forceHighlightParents,
|
||||
@NotNull HighlightVisitor visitor,
|
||||
@NotNull ResultSink resultSink) {
|
||||
boolean failed = false;
|
||||
int nextLimit = chunkSize;
|
||||
List<HighlightInfo> infos = new ArrayList<>();
|
||||
|
||||
Reference in New Issue
Block a user