EP_NAME = new ExtensionPointName<>("com.intellij.unhandledExceptionFixProvider");
+
+ /**
+ * Registers fixes for unhandled exceptions.
+ *
+ * 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 unhandledExceptions);
+}
diff --git a/plugins/lombok/src/main/java/de/plushnikov/intellij/plugin/extension/LombokHighlightErrorFilter.java b/plugins/lombok/src/main/java/de/plushnikov/intellij/plugin/extension/LombokHighlightErrorFilter.java
index 49292f768685..19fe3d843f34 100644
--- a/plugins/lombok/src/main/java/de/plushnikov/intellij/plugin/extension/LombokHighlightErrorFilter.java
+++ b/plugins/lombok/src/main/java/de/plushnikov/intellij/plugin/extension/LombokHighlightErrorFilter.java
@@ -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>> 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);
- }
}
diff --git a/plugins/lombok/src/main/java/de/plushnikov/intellij/plugin/handler/SneakyThrowsExceptionHandler.java b/plugins/lombok/src/main/java/de/plushnikov/intellij/plugin/handler/SneakyThrowsExceptionHandler.java
index ff442c36c902..582b3a88f7c5 100644
--- a/plugins/lombok/src/main/java/de/plushnikov/intellij/plugin/handler/SneakyThrowsExceptionHandler.java
+++ b/plugins/lombok/src/main/java/de/plushnikov/intellij/plugin/handler/SneakyThrowsExceptionHandler.java
@@ -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 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 exceptionTypes) {
+ ExceptionTypesCollector visitor = new ExceptionTypesCollector();
+ thisOrSuperCallInConstructor.accept(visitor);
+ return ContainerUtil.intersects(visitor.exceptionTypes, exceptionTypes);
+ }
+
private static class ExceptionTypesCollector extends JavaRecursiveElementWalkingVisitor {
private final Collection exceptionTypes = new HashSet<>();
diff --git a/plugins/lombok/src/main/java/de/plushnikov/intellij/plugin/handler/SneakyThrowsUnhandledExceptionFixProvider.java b/plugins/lombok/src/main/java/de/plushnikov/intellij/plugin/handler/SneakyThrowsUnhandledExceptionFixProvider.java
new file mode 100644
index 000000000000..dbf28a50baeb
--- /dev/null
+++ b/plugins/lombok/src/main/java/de/plushnikov/intellij/plugin/handler/SneakyThrowsUnhandledExceptionFixProvider.java
@@ -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 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);
+ }
+ }
+ }
+}
diff --git a/plugins/lombok/src/main/resources/META-INF/plugin.xml b/plugins/lombok/src/main/resources/META-INF/plugin.xml
index 36e086ced0f8..3a1b8c3e2f39 100644
--- a/plugins/lombok/src/main/resources/META-INF/plugin.xml
+++ b/plugins/lombok/src/main/resources/META-INF/plugin.xml
@@ -83,6 +83,7 @@
+
diff --git a/plugins/lombok/src/test/java/de/plushnikov/intellij/plugin/highlights/SneakyThrowsQuickFixTest.java b/plugins/lombok/src/test/java/de/plushnikov/intellij/plugin/highlights/SneakyThrowsQuickFixTest.java
new file mode 100644
index 000000000000..8975b4298300
--- /dev/null
+++ b/plugins/lombok/src/test/java/de/plushnikov/intellij/plugin/highlights/SneakyThrowsQuickFixTest.java
@@ -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
+ 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(throwException());
+ }
+
+ public TestSneakyThrow(int i) {
+ }
+
+ private static int throwException() throws IOException {
+ throw new IOException();
+ }
+ }
+ """;
+ myFixture.configureByText(JavaFileType.INSTANCE, text);
+
+ List 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 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);
+ }
+}