[lombok] IDEA-255688 Get rid of LombokHighlightFixHook-Filter

GitOrigin-RevId: d050c269d3fcedcdcc21429e83604688c50b6b60
This commit is contained in:
Michail Plushnikov
2023-12-11 21:51:16 +01:00
committed by intellij-monorepo-bot
parent c5257f0fea
commit 6f95b21eeb
9 changed files with 232 additions and 99 deletions

View File

@@ -37,6 +37,7 @@
<extensionPoint qualifiedName="com.intellij.javaLanguageLevelPusherCustomizer" interface="com.intellij.openapi.roots.impl.JavaLanguageLevelPusherCustomizer" dynamic="true"/>
<extensionPoint qualifiedName="com.intellij.propertyAccessorDetector" interface="com.intellij.psi.util.PropertyAccessorDetector" dynamic="true"/>
<extensionPoint qualifiedName="com.intellij.virtualManifestProvider" interface="com.intellij.codeInsight.daemon.impl.analysis.VirtualManifestProvider" dynamic="true"/>
<extensionPoint qualifiedName="com.intellij.unhandledExceptionFixProvider" interface="com.intellij.codeInsight.daemon.impl.analysis.UnhandledExceptionFixProvider" dynamic="true"/>
<extensionPoint qualifiedName="com.intellij.injectedLanguageJavaReferenceSupplier"
beanClass="com.intellij.codeInsight.daemon.impl.analysis.InjectedLanguageJavaReferenceSupplier" dynamic="true"/>

View File

@@ -176,26 +176,29 @@ public final class HighlightFixUtil {
}
}
static void registerUnhandledExceptionFixes(@NotNull PsiElement element, @Nullable HighlightInfo.Builder info) {
IntentionAction action4 = QuickFixFactory.getInstance().createAddExceptionFromFieldInitializerToConstructorThrowsFix(element);
static void registerUnhandledExceptionFixes(@NotNull PsiElement element, @Nullable HighlightInfo.Builder info,
@NotNull List<PsiClassType> unhandledExceptions) {
if (info != null) {
final QuickFixFactory quickFixFactory = QuickFixFactory.getInstance();
IntentionAction action4 = quickFixFactory.createAddExceptionFromFieldInitializerToConstructorThrowsFix(element);
info.registerFix(action4, null, null, null, null);
}
IntentionAction action3 = QuickFixFactory.getInstance().createAddExceptionToCatchFix();
if (info != null) {
IntentionAction action3 = quickFixFactory.createAddExceptionToCatchFix();
info.registerFix(action3, null, null, null, null);
}
IntentionAction action2 = QuickFixFactory.getInstance().createAddExceptionToExistingCatch(element);
if (info != null) {
IntentionAction action2 = quickFixFactory.createAddExceptionToExistingCatch(element);
info.registerFix(action2, null, null, null, null);
}
IntentionAction action1 = QuickFixFactory.getInstance().createAddExceptionToThrowsFix(element);
if (info != null) {
IntentionAction action1 = quickFixFactory.createAddExceptionToThrowsFix(element);
info.registerFix(action1, null, null, null, null);
}
IntentionAction action = QuickFixFactory.getInstance().createSurroundWithTryCatchFix(element);
if (info != null) {
IntentionAction action = quickFixFactory.createSurroundWithTryCatchFix(element);
info.registerFix(action, null, null, null, null);
for (UnhandledExceptionFixProvider provider : UnhandledExceptionFixProvider.EP_NAME.getExtensionList()) {
provider.registerUnhandledExceptionFixes(info, element, unhandledExceptions);
}
}
}

View File

@@ -925,7 +925,7 @@ public final class HighlightUtil {
TextRange textRange = computeRange(element);
String description = getUnhandledExceptionsDescriptor(unhandled);
HighlightInfo.Builder info = HighlightInfo.newHighlightInfo(highlightType).range(textRange).descriptionAndTooltip(description);
HighlightFixUtil.registerUnhandledExceptionFixes(element, info);
HighlightFixUtil.registerUnhandledExceptionFixes(element, info, unhandled);
return info;
}
@@ -958,7 +958,7 @@ public final class HighlightUtil {
String description = JavaErrorBundle.message("unhandled.close.exceptions", formatTypes(unhandled), unhandled.size(),
JavaErrorBundle.message("auto.closeable.resource"));
HighlightInfo.Builder highlight = HighlightInfo.newHighlightInfo(highlightType).range(resource).descriptionAndTooltip(description);
HighlightFixUtil.registerUnhandledExceptionFixes(resource, highlight);
HighlightFixUtil.registerUnhandledExceptionFixes(resource, highlight, unhandled);
return highlight;
}

View File

@@ -0,0 +1,29 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.daemon.impl.analysis;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.psi.PsiClassType;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* The UnhandledExceptionFixProvider interface provides a way to register fixes for unhandled exceptions.
*/
public interface UnhandledExceptionFixProvider {
ExtensionPointName<UnhandledExceptionFixProvider> EP_NAME = new ExtensionPointName<>("com.intellij.unhandledExceptionFixProvider");
/**
* Registers fixes for unhandled exceptions.
* <p>
* This method is responsible for registering fixes for unhandled exceptions in the provided {@link HighlightInfo.Builder} instance.
*
* @param info the {@link HighlightInfo.Builder} instance to register the fixes into
* @param element the {@link PsiElement} representing the location of the unhandled exception
* @param unhandledExceptions the list of unhandled exception types
*/
void registerUnhandledExceptionFixes(@NotNull HighlightInfo.Builder info, @NotNull PsiElement element,
@NotNull List<PsiClassType> unhandledExceptions);
}

View File

@@ -1,41 +1,18 @@
package de.plushnikov.intellij.plugin.extension;
import com.intellij.codeInsight.daemon.JavaErrorBundle;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.codeInsight.daemon.impl.HighlightInfoFilter;
import com.intellij.codeInsight.intention.AddAnnotationFix;
import com.intellij.lang.annotation.HighlightSeverity;
import com.intellij.openapi.editor.colors.CodeInsightColors;
import com.intellij.openapi.editor.colors.TextAttributesKey;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import de.plushnikov.intellij.plugin.LombokClassNames;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import de.plushnikov.intellij.plugin.handler.OnXAnnotationHandler;
import de.plushnikov.intellij.plugin.quickfix.PsiQuickFixFactory;
import de.plushnikov.intellij.plugin.util.LombokLibraryUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.regex.Pattern;
public class LombokHighlightErrorFilter implements HighlightInfoFilter {
private static final class Holder {
static final Map<HighlightSeverity, Map<TextAttributesKey, List<LombokHighlightFixHook>>> registeredHooks;
static {
registeredHooks = new HashMap<>();
for (LombokHighlightFixHook highlightFixHook : LombokHighlightFixHook.values()) {
registeredHooks.computeIfAbsent(highlightFixHook.severity, s -> new HashMap<>())
.computeIfAbsent(highlightFixHook.key, k -> new ArrayList<>())
.add(highlightFixHook);
}
}
}
public LombokHighlightErrorFilter() {
}
@@ -63,59 +40,6 @@ public class LombokHighlightErrorFilter implements HighlightInfoFilter {
}
}
// register different quick fix for highlight
Holder.registeredHooks
.getOrDefault(highlightInfo.getSeverity(), Collections.emptyMap())
.getOrDefault(highlightInfo.type.getAttributesKey(), Collections.emptyList())
.stream()
.filter(filter -> filter.descriptionCheck(highlightInfo.getDescription()))
.forEach(filter -> filter.processHook(highlightedElement, highlightInfo));
return true;
}
private enum LombokHighlightFixHook {
UNHANDLED_EXCEPTION(HighlightSeverity.ERROR, CodeInsightColors.ERRORS_ATTRIBUTES) {
private final Pattern pattern = preparePattern(1);
private final Pattern pattern2 = preparePattern(2);
@NotNull
private static Pattern preparePattern(int count) {
return Pattern.compile(JavaErrorBundle.message("unhandled.exceptions", ".*", count));
}
@Override
public boolean descriptionCheck(@Nullable String description) {
return description != null && (pattern.matcher(description).matches() || pattern2.matcher(description).matches());
}
@Override
public void processHook(@NotNull PsiElement highlightedElement, @NotNull HighlightInfo highlightInfo) {
PsiElement importantParent = PsiTreeUtil.getParentOfType(highlightedElement,
PsiMethod.class, PsiLambdaExpression.class,
PsiMethodReferenceExpression.class, PsiClassInitializer.class
);
// applicable only for methods
if (importantParent instanceof PsiMethod) {
AddAnnotationFix fix =
PsiQuickFixFactory.createAddAnnotationFix(LombokClassNames.SNEAKY_THROWS, (PsiModifierListOwner)importantParent);
highlightInfo.registerFix(fix, null, null, null, null);
}
}
};
private final HighlightSeverity severity;
private final TextAttributesKey key;
LombokHighlightFixHook(@NotNull HighlightSeverity severity, @Nullable TextAttributesKey key) {
this.severity = severity;
this.key = key;
}
abstract public boolean descriptionCheck(@Nullable String description);
abstract public void processHook(@NotNull PsiElement highlightedElement, @NotNull HighlightInfo highlightInfo);
}
}

View File

@@ -23,7 +23,7 @@ public class SneakyThrowsExceptionHandler extends CustomExceptionHandler {
@Override
public boolean isHandled(@Nullable PsiElement element, @NotNull PsiClassType exceptionType, PsiElement topElement) {
final PsiCodeBlock containingCodeBlock = PsiTreeUtil.getParentOfType(element, PsiCodeBlock.class, false);
if (isCodeBlockWithExceptionInConstructorCall(containingCodeBlock, exceptionType)) {
if (isCodeBlockWithExceptionInConstructorCall(containingCodeBlock, Collections.singleton(exceptionType))) {
// call to a sibling or super constructor is excluded from the @SneakyThrows treatment
return false;
}
@@ -50,20 +50,25 @@ public class SneakyThrowsExceptionHandler extends CustomExceptionHandler {
}
private static boolean isCodeBlockWithExceptionInConstructorCall(@Nullable PsiCodeBlock codeBlock,
@NotNull PsiClassType exceptionType) {
@NotNull Collection<PsiClassType> exceptionTypes) {
final PsiMethod containingMethod = PsiTreeUtil.getParentOfType(codeBlock, PsiMethod.class);
if (null != containingMethod) {
final PsiMethodCallExpression thisOrSuperCallInConstructor =
JavaPsiConstructorUtil.findThisOrSuperCallInConstructor(containingMethod);
if (null != thisOrSuperCallInConstructor) {
ExceptionTypesCollector visitor = new ExceptionTypesCollector();
thisOrSuperCallInConstructor.accept(visitor);
return visitor.exceptionTypes.contains(exceptionType);
return throwsExceptionsTypes(thisOrSuperCallInConstructor, exceptionTypes);
}
}
return false;
}
static boolean throwsExceptionsTypes(@NotNull PsiMethodCallExpression thisOrSuperCallInConstructor,
@NotNull Collection<PsiClassType> exceptionTypes) {
ExceptionTypesCollector visitor = new ExceptionTypesCollector();
thisOrSuperCallInConstructor.accept(visitor);
return ContainerUtil.intersects(visitor.exceptionTypes, exceptionTypes);
}
private static class ExceptionTypesCollector extends JavaRecursiveElementWalkingVisitor {
private final Collection<PsiClassType> exceptionTypes = new HashSet<>();

View File

@@ -0,0 +1,37 @@
package de.plushnikov.intellij.plugin.handler;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.codeInsight.daemon.impl.analysis.UnhandledExceptionFixProvider;
import com.intellij.codeInsight.intention.AddAnnotationFix;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.JavaPsiConstructorUtil;
import de.plushnikov.intellij.plugin.LombokClassNames;
import de.plushnikov.intellij.plugin.quickfix.PsiQuickFixFactory;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* Adds @SneakyThrows Annotation as Fix to handle unhandled exceptions
*/
public class SneakyThrowsUnhandledExceptionFixProvider implements UnhandledExceptionFixProvider {
@Override
public void registerUnhandledExceptionFixes(@NotNull HighlightInfo.Builder info,
@NotNull PsiElement element,
@NotNull List<PsiClassType> unhandledExceptions) {
PsiElement importantParent = PsiTreeUtil.getParentOfType(element, PsiMethod.class, PsiLambdaExpression.class,
PsiMethodReferenceExpression.class, PsiClassInitializer.class);
// applicable only for methods
if (importantParent instanceof PsiMethod psiMethod) {
final PsiMethodCallExpression thisOrSuperCallInConstructor = JavaPsiConstructorUtil.findThisOrSuperCallInConstructor(psiMethod);
if (null == thisOrSuperCallInConstructor ||
!SneakyThrowsExceptionHandler.throwsExceptionsTypes(thisOrSuperCallInConstructor, unhandledExceptions)) {
AddAnnotationFix fix = PsiQuickFixFactory.createAddAnnotationFix(LombokClassNames.SNEAKY_THROWS, psiMethod);
info.registerFix(fix, null, null, null, null);
}
}
}
}

View File

@@ -83,6 +83,7 @@
<fileBasedIndex implementation="de.plushnikov.intellij.plugin.lombokconfig.LombokConfigIndex"/>
<custom.exception.handler implementation="de.plushnikov.intellij.plugin.handler.SneakyThrowsExceptionHandler"/>
<unhandledExceptionFixProvider implementation="de.plushnikov.intellij.plugin.handler.SneakyThrowsUnhandledExceptionFixProvider"/>
<implicit.resource.closer
implementation="de.plushnikov.intellij.plugin.extension.LombokCleanUpImplicitResourceCloser"/>

View File

@@ -0,0 +1,133 @@
package de.plushnikov.intellij.plugin.highlights;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.ide.highlighter.JavaFileType;
import de.plushnikov.intellij.plugin.AbstractLombokLightCodeInsightTestCase;
import de.plushnikov.intellij.plugin.inspection.LombokInspection;
import org.intellij.lang.annotations.Language;
import java.util.List;
public class SneakyThrowsQuickFixTest extends AbstractLombokLightCodeInsightTestCase {
public void testSneakyThrowsInvalidQuickFix() {
myFixture.enableInspections(LombokInspection.class);
@Language("JAVA") final String text = """
import lombok.SneakyThrows;
import java.io.IOException;
public class TestSneakyThrow {
@SneakyThrows<caret>
public TestSneakyThrow(String s) {
this(throwException());
}
public TestSneakyThrow(int i) {
}
private static int throwException() throws IOException {
throw new IOException();
}
}
""";
myFixture.configureByText(JavaFileType.INSTANCE, text);
myFixture.launchAction("Remove annotation");
@Language("JAVA") final String expectedText = """
import java.io.IOException;
public class TestSneakyThrow {
public TestSneakyThrow(String s) {
this(throwException());
}
public TestSneakyThrow(int i) {
}
private static int throwException() throws IOException {
throw new IOException();
}
}
""";
myFixture.checkResult(expectedText, true);
}
public void testSneakyThrowsUnhandledExceptionWithoutQuickFix() {
@Language("JAVA") final String text = """
import java.io.IOException;
public class TestSneakyThrow {
public TestSneakyThrow(String s) {
this(<caret>throwException());
}
public TestSneakyThrow(int i) {
}
private static int throwException() throws IOException {
throw new IOException();
}
}
""";
myFixture.configureByText(JavaFileType.INSTANCE, text);
List<IntentionAction> actions = myFixture.filterAvailableIntentions("Annotate constructor 'TestSneakyThrow()' as '@SneakyThrows'");
assertEmpty(actions);
}
public void testSneakyThrowsUnhandledExceptionWithQuickFix() {
@Language("JAVA") final String text = """
import java.io.IOException;
public class TestSneakyThrow {
public TestSneakyThrow(String s) {
this(throwException());
if (1 == 1) {
throw new <caret>Exception("123");
}
}
public TestSneakyThrow(int i) {
}
private static int throwException() throws IOException {
throw new IOException();
}
}
""";
myFixture.configureByText(JavaFileType.INSTANCE, text);
myFixture.launchAction("Annotate constructor 'TestSneakyThrow()' as '@SneakyThrows'");
@Language("JAVA") final String expectedText = """
import lombok.SneakyThrows;
import java.io.IOException;
public class TestSneakyThrow {
@SneakyThrows
public TestSneakyThrow(String s) {
this(throwException());
if (1 == 1) {
throw new Exception("123");
}
}
public TestSneakyThrow(int i) {
}
private static int throwException() throws IOException {
throw new IOException();
}
}
""";
myFixture.checkResult(expectedText, true);
}
}