mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Refactor ExceptionInfo: separate subclasses for exceptions
GitOrigin-RevId: dfd9013385125b55b68ddaa5e8bc11dc2a109241
This commit is contained in:
committed by
intellij-monorepo-bot
parent
f752499188
commit
de4db0fbc2
@@ -7,8 +7,7 @@ import com.intellij.codeInspection.dataFlow.*;
|
||||
import com.intellij.codeInspection.dataFlow.rangeSet.LongRangeSet;
|
||||
import com.intellij.codeInspection.dataFlow.types.*;
|
||||
import com.intellij.codeInspection.dataFlow.value.RelationType;
|
||||
import com.intellij.execution.filters.ExceptionAnalysisProvider;
|
||||
import com.intellij.execution.filters.ExceptionInfo;
|
||||
import com.intellij.execution.filters.*;
|
||||
import com.intellij.java.JavaBundle;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
@@ -37,8 +36,9 @@ public class DataflowExceptionAnalysisProvider implements ExceptionAnalysisProvi
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable AnAction getAnalysisAction(@NotNull PsiElement anchor, @NotNull String exceptionName, @NotNull String exceptionMessage) {
|
||||
Analysis analysis = getAnalysis(anchor, exceptionName, exceptionMessage);
|
||||
public @Nullable AnAction getAnalysisAction(@NotNull PsiElement anchor,
|
||||
@NotNull ExceptionInfo info) {
|
||||
Analysis analysis = getAnalysis(anchor, info);
|
||||
return createAction(analysis);
|
||||
}
|
||||
|
||||
@@ -92,8 +92,7 @@ public class DataflowExceptionAnalysisProvider implements ExceptionAnalysisProvi
|
||||
}
|
||||
|
||||
private @Nullable Analysis getAnalysis(@NotNull PsiElement anchor,
|
||||
@NotNull String exceptionName,
|
||||
@NotNull String exceptionMessage) {
|
||||
@NotNull ExceptionInfo info) {
|
||||
if (anchor instanceof PsiKeyword && anchor.textMatches(PsiKeyword.NEW)) {
|
||||
PsiNewExpression exceptionConstructor = tryCast(anchor.getParent(), PsiNewExpression.class);
|
||||
if (exceptionConstructor != null && !exceptionConstructor.isArrayCreation()) {
|
||||
@@ -103,22 +102,25 @@ public class DataflowExceptionAnalysisProvider implements ExceptionAnalysisProvi
|
||||
return fromThrowStatement(throwStatement);
|
||||
}
|
||||
}
|
||||
switch (exceptionName) {
|
||||
case CommonClassNames.JAVA_LANG_ASSERTION_ERROR:
|
||||
return fromAssertionError(anchor);
|
||||
case "java.lang.ArrayIndexOutOfBoundsException":
|
||||
return fromArrayIndexOutOfBoundsException(anchor, exceptionMessage);
|
||||
case "java.lang.ClassCastException":
|
||||
return fromClassCastException(anchor, exceptionMessage);
|
||||
case CommonClassNames.JAVA_LANG_NULL_POINTER_EXCEPTION:
|
||||
return Analysis.create(DfTypes.NULL, findDereferencedExpression(anchor));
|
||||
case "java.lang.NegativeArraySizeException":
|
||||
return fromNegativeArraySizeException(anchor, exceptionMessage);
|
||||
case "java.lang.ArithmeticException":
|
||||
return fromArithmeticException(anchor);
|
||||
default:
|
||||
return null;
|
||||
if (info instanceof AssertionErrorInfo) {
|
||||
return fromAssertionError(anchor);
|
||||
}
|
||||
else if (info instanceof ArrayIndexOutOfBoundsExceptionInfo) {
|
||||
return fromArrayIndexOutOfBoundsException(anchor, ((ArrayIndexOutOfBoundsExceptionInfo)info).getIndex());
|
||||
}
|
||||
else if (info instanceof ClassCastExceptionInfo) {
|
||||
return fromClassCastException(anchor, ((ClassCastExceptionInfo)info).getActualClass());
|
||||
}
|
||||
else if (info instanceof NullPointerExceptionInfo) {
|
||||
return Analysis.create(DfTypes.NULL, findDereferencedExpression(anchor));
|
||||
}
|
||||
else if (info instanceof NegativeArraySizeExceptionInfo) {
|
||||
return fromNegativeArraySizeException(anchor, ((NegativeArraySizeExceptionInfo)info).getSuppliedSize());
|
||||
}
|
||||
else if (info instanceof ArithmeticExceptionInfo) {
|
||||
return fromArithmeticException(anchor);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Analysis fromArithmeticException(PsiElement anchor) {
|
||||
@@ -261,63 +263,47 @@ public class DataflowExceptionAnalysisProvider implements ExceptionAnalysisProvi
|
||||
return null;
|
||||
}
|
||||
|
||||
private @Nullable Analysis fromClassCastException(@NotNull PsiElement anchor, @NotNull String exceptionMessage) {
|
||||
if (anchor instanceof PsiJavaToken && ((PsiJavaToken)anchor).getTokenType().equals(JavaTokenType.LPARENTH)) {
|
||||
String actualClass = ExceptionInfo.getCastActualClassFromMessage(exceptionMessage);
|
||||
if (actualClass != null) {
|
||||
PsiTypeCastExpression castExpression = tryCast(anchor.getParent(), PsiTypeCastExpression.class);
|
||||
if (castExpression != null) {
|
||||
PsiExpression ref = extractAnchor(castExpression.getOperand());
|
||||
if (ref != null) {
|
||||
// TODO: support arrays, primitive arrays, inner classes
|
||||
PsiClass[] classes = JavaPsiFacade.getInstance(myProject).findClasses(actualClass, GlobalSearchScope.allScope(myProject));
|
||||
if (classes.length == 1) {
|
||||
return new Analysis(
|
||||
DfTypes.typedObject(JavaPsiFacade.getElementFactory(myProject).createType(classes[0]), Nullability.NOT_NULL), ref);
|
||||
}
|
||||
else {
|
||||
PsiType castType = castExpression.getType();
|
||||
if (castType != null) {
|
||||
return tryNegate(new Analysis(DfTypes.typedObject(castType, Nullability.NULLABLE), ref));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private @Nullable Analysis fromClassCastException(@NotNull PsiElement anchor, @Nullable String actualClass) {
|
||||
if (!(anchor instanceof PsiJavaToken) || !((PsiJavaToken)anchor).getTokenType().equals(JavaTokenType.LPARENTH)) {
|
||||
return null;
|
||||
}
|
||||
PsiTypeCastExpression castExpression = tryCast(anchor.getParent(), PsiTypeCastExpression.class);
|
||||
if (castExpression == null) return null;
|
||||
PsiExpression ref = extractAnchor(castExpression.getOperand());
|
||||
if (ref == null) return null;
|
||||
if (actualClass != null) {
|
||||
// TODO: support arrays, primitive arrays, inner classes
|
||||
PsiClass[] classes = JavaPsiFacade.getInstance(myProject).findClasses(actualClass, GlobalSearchScope.allScope(myProject));
|
||||
if (classes.length == 1) {
|
||||
return new Analysis(
|
||||
DfTypes.typedObject(JavaPsiFacade.getElementFactory(myProject).createType(classes[0]), Nullability.NOT_NULL), ref);
|
||||
}
|
||||
}
|
||||
PsiType castType = castExpression.getType();
|
||||
if (castType != null) {
|
||||
return tryNegate(new Analysis(DfTypes.typedObject(castType, Nullability.NULLABLE), ref));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Analysis fromArrayIndexOutOfBoundsException(@NotNull PsiElement anchor, @Nullable Integer index) {
|
||||
if (index != null && anchor instanceof PsiJavaToken && ((PsiJavaToken)anchor).getTokenType().equals(JavaTokenType.LBRACKET)) {
|
||||
PsiArrayAccessExpression access = tryCast(anchor.getParent(), PsiArrayAccessExpression.class);
|
||||
if (access != null) {
|
||||
return Analysis.create(DfTypes.intValue(index), access.getIndexExpression());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Analysis fromArrayIndexOutOfBoundsException(@NotNull PsiElement anchor, @NotNull String exceptionMessage) {
|
||||
if (anchor instanceof PsiJavaToken &&
|
||||
((PsiJavaToken)anchor).getTokenType().equals(JavaTokenType.LBRACKET)) {
|
||||
Integer index = ExceptionInfo.getArrayIndexFromMessage(exceptionMessage);
|
||||
if (index != null) {
|
||||
PsiArrayAccessExpression access = tryCast(anchor.getParent(), PsiArrayAccessExpression.class);
|
||||
if (access != null) {
|
||||
return Analysis.create(DfTypes.intValue(index), access.getIndexExpression());
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Analysis fromNegativeArraySizeException(@NotNull PsiElement anchor, @NotNull String exceptionMessage) {
|
||||
private static Analysis fromNegativeArraySizeException(@NotNull PsiElement anchor, @Nullable Integer size) {
|
||||
if (size == null || size >= 0) return null;
|
||||
if (anchor instanceof PsiKeyword && anchor.textMatches(PsiKeyword.NEW) && anchor.getParent() instanceof PsiNewExpression) {
|
||||
int size;
|
||||
try {
|
||||
size = Integer.parseInt(exceptionMessage);
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
if (size < 0) {
|
||||
PsiExpression[] dimensions = ((PsiNewExpression)anchor.getParent()).getArrayDimensions();
|
||||
if (dimensions.length == 1) {
|
||||
return Analysis.create(DfTypes.intValue(size), dimensions[0]);
|
||||
}
|
||||
PsiExpression[] dimensions = ((PsiNewExpression)anchor.getParent()).getArrayDimensions();
|
||||
if (dimensions.length == 1) {
|
||||
return Analysis.create(DfTypes.intValue(size), dimensions[0]);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
+49
-50
@@ -2,8 +2,8 @@
|
||||
package com.intellij.java.slicer;
|
||||
|
||||
import com.intellij.execution.filters.ExceptionAnalysisProvider;
|
||||
import com.intellij.execution.filters.ExceptionInfo;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.psi.CommonClassNames;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.testFramework.LightJavaCodeInsightTestCase;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -11,163 +11,163 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class DataflowExceptionAnalysisProviderTest extends LightJavaCodeInsightTestCase {
|
||||
public void testArrayIndex() {
|
||||
doTest("java.lang.ArrayIndexOutOfBoundsException", "10",
|
||||
doTest("java.lang.ArrayIndexOutOfBoundsException: 10",
|
||||
"Find why 'idx' could be 10", "class X {static int test(int[] x, int idx) {return x<caret>[idx];}}");
|
||||
}
|
||||
|
||||
public void testClassCast() {
|
||||
doTest("java.lang.ClassCastException", "class X cannot be cast to class java.lang.Number",
|
||||
doTest("java.lang.ClassCastException: class X cannot be cast to class java.lang.Number",
|
||||
"Find why 'obj' could be instanceof X (not-null)",
|
||||
"class X {static void test(Object obj) {System.out.println((<caret>(Number) obj).intValue());}}");
|
||||
}
|
||||
|
||||
public void testClassCastUnknownClass() {
|
||||
doTest("java.lang.ClassCastException", "class XYZ cannot be cast to class java.lang.Number",
|
||||
doTest("java.lang.ClassCastException: class XYZ cannot be cast to class java.lang.Number",
|
||||
"Find why 'obj' could be not instanceof java.lang.Number (not-null)",
|
||||
"class X {static void test(Object obj) {System.out.println((<caret>(Number) obj).intValue());}}");
|
||||
}
|
||||
|
||||
public void testNpe() {
|
||||
doTest(CommonClassNames.JAVA_LANG_NULL_POINTER_EXCEPTION, "Cannot invoke \"Object.hashCode()\" because \"obj\" is null",
|
||||
"Find why 'obj' could be null",
|
||||
doTest("java.lang.NullPointerException: Cannot invoke \"Object.hashCode()\" because \"obj\" is null",
|
||||
"Find why 'obj' could be null",
|
||||
"class X {static void test(Object obj) {System.out.println(obj.<caret>hashCode());}}");
|
||||
}
|
||||
|
||||
public void testNpeArray() {
|
||||
doTest(CommonClassNames.JAVA_LANG_NULL_POINTER_EXCEPTION, "Cannot load from object array because \"obj\" is null",
|
||||
"Find why 'obj' could be null",
|
||||
doTest("java.lang.NullPointerException: Cannot load from object array because \"obj\" is null",
|
||||
"Find why 'obj' could be null",
|
||||
"class X {static void test(Object[] obj) {System.out.println(obj<caret>[0]);}}");
|
||||
}
|
||||
|
||||
public void testNpeSynchronized() {
|
||||
doTest(CommonClassNames.JAVA_LANG_NULL_POINTER_EXCEPTION, "Cannot enter synchronized block because \"obj\" is null",
|
||||
"Find why 'obj' could be null",
|
||||
doTest("java.lang.NullPointerException: Cannot enter synchronized block because \"obj\" is null",
|
||||
"Find why 'obj' could be null",
|
||||
"class X {static void test(Object obj) {<caret>synchronized(obj){}}}");
|
||||
}
|
||||
|
||||
public void testNpeThrow() {
|
||||
doTest(CommonClassNames.JAVA_LANG_NULL_POINTER_EXCEPTION, "Cannot throw exception because \"obj\" is null",
|
||||
doTest("java.lang.NullPointerException: Cannot throw exception because \"obj\" is null",
|
||||
"Find why 'obj' could be null",
|
||||
"class X {static void test(RuntimeException obj) {<caret>throw obj;}}");
|
||||
}
|
||||
|
||||
public void testAssertChar() {
|
||||
doTest("java.lang.AssertionError", "",
|
||||
doTest("java.lang.AssertionError",
|
||||
"Find why 'c' could be 97",
|
||||
"class X {static void test(char c) {<caret>assert c != 'a';}}");
|
||||
}
|
||||
|
||||
public void testAssertDivisibility() {
|
||||
doTest("java.lang.AssertionError", "",
|
||||
doTest("java.lang.AssertionError",
|
||||
"Find why 'i' could be odd",
|
||||
"class X {static void test(int i) {<caret>assert i % 2 == 0;}}");
|
||||
}
|
||||
|
||||
public void testAssertAnd() {
|
||||
doTest("java.lang.AssertionError", "",
|
||||
doTest("java.lang.AssertionError",
|
||||
"Find why 'idx' could be <= 0 or >= 4",
|
||||
"class X {static void test(int idx) {<caret>assert idx > 0 && idx < 4;}}");
|
||||
}
|
||||
|
||||
public void testAssertOr() {
|
||||
doTest("java.lang.AssertionError", "",
|
||||
doTest("java.lang.AssertionError",
|
||||
"Find why 'idx' could be in {0..4}",
|
||||
"class X {static void test(int idx) {<caret>assert idx < 0 || idx > 4;}}");
|
||||
}
|
||||
|
||||
public void testAssertByte() {
|
||||
doTest("java.lang.AssertionError", "",
|
||||
doTest("java.lang.AssertionError",
|
||||
"Find why 'idx' could be <= 0",
|
||||
"class X {static void test(byte idx) {<caret>assert idx > 0;}}");
|
||||
}
|
||||
|
||||
public void testAssertShort() {
|
||||
doTest("java.lang.AssertionError", "",
|
||||
doTest("java.lang.AssertionError",
|
||||
"Find why 'idx' could be <= 0",
|
||||
"class X {static void test(short idx) {<caret>assert idx > 0;}}");
|
||||
}
|
||||
|
||||
public void testAssertLong() {
|
||||
doTest("java.lang.AssertionError", "",
|
||||
doTest("java.lang.AssertionError",
|
||||
"Find why 'idx' could be <= 0",
|
||||
"class X {static void test(long idx) {<caret>assert idx > 0;}}");
|
||||
}
|
||||
|
||||
public void testAssertBoxed() {
|
||||
// boxed types are unsupported
|
||||
doTest("java.lang.AssertionError", "",
|
||||
doTest("java.lang.AssertionError",
|
||||
null,
|
||||
"class X {static void test(Long idx) {<caret>assert idx > 0;}}");
|
||||
}
|
||||
|
||||
public void testInstanceOf() {
|
||||
doTest("java.lang.IllegalArgumentException", "",
|
||||
doTest("java.lang.IllegalArgumentException",
|
||||
"Find why 'obj' could be null or not instanceof java.lang.String",
|
||||
"class X {static void test(Object obj) {if (!(obj instanceof String)) throw <caret>new IllegalArgumentException();}}");
|
||||
}
|
||||
|
||||
public void testStringInEquality() {
|
||||
doTest("java.lang.IllegalArgumentException", "",
|
||||
doTest("java.lang.IllegalArgumentException",
|
||||
"Find why 's' could be != \"hello\" (not-null)",
|
||||
"class X {static void test(String s) {if (!s.equals(\"hello\")) throw <caret>new IllegalArgumentException();}}");
|
||||
}
|
||||
|
||||
public void testStringEqEq() {
|
||||
doTest("java.lang.IllegalArgumentException", "",
|
||||
doTest("java.lang.IllegalArgumentException",
|
||||
null,
|
||||
"class X {static void test(String s) {if (s == \"hello\")) throw <caret>new IllegalArgumentException();}}");
|
||||
}
|
||||
|
||||
public void testClassEquality() {
|
||||
doTest("java.lang.IllegalArgumentException", "",
|
||||
doTest("java.lang.IllegalArgumentException",
|
||||
"Find why 'cls' could be String",
|
||||
"class X {static void test(Class<?> cls) {if (cls.equals(String.class)) throw <caret>new IllegalArgumentException();}}");
|
||||
}
|
||||
|
||||
public void testClassInEquality() {
|
||||
doTest("java.lang.IllegalArgumentException", "",
|
||||
doTest("java.lang.IllegalArgumentException",
|
||||
"Find why 'cls' could be != String (not-null)",
|
||||
"class X {static void test(Class<?> cls) {if (!cls.equals(String.class)) throw <caret>new IllegalArgumentException();}}");
|
||||
}
|
||||
|
||||
public void testClassInEqualityInverted() {
|
||||
doTest("java.lang.IllegalArgumentException", "",
|
||||
doTest("java.lang.IllegalArgumentException",
|
||||
"Find why 'cls' could be null or != String",
|
||||
"class X {static void test(Class<?> cls) {if (!String.class.equals(cls)) {throw <caret>new IllegalArgumentException();}}}");
|
||||
}
|
||||
|
||||
public void testBooleanTrue() {
|
||||
doTest("java.lang.IllegalArgumentException", "",
|
||||
doTest("java.lang.IllegalArgumentException",
|
||||
"Find why 'b' could be true",
|
||||
"class X {static void test(boolean b) {if (b) {throw <caret>new IllegalArgumentException();}}}");
|
||||
}
|
||||
|
||||
public void testBooleanFalse() {
|
||||
doTest("java.lang.IllegalArgumentException", "",
|
||||
doTest("java.lang.IllegalArgumentException",
|
||||
"Find why 'b' could be false",
|
||||
"class X {static void test(boolean b) {if (!b) {throw <caret>new IllegalArgumentException();}}}");
|
||||
}
|
||||
|
||||
public void testIsNull() {
|
||||
doTest("java.lang.IllegalArgumentException", "",
|
||||
doTest("java.lang.IllegalArgumentException",
|
||||
"Find why 'obj' could be null",
|
||||
"class X {static void test(Object obj) {if (obj == null) {throw <caret>new IllegalArgumentException();}}}");
|
||||
}
|
||||
|
||||
public void testIsNotNull() {
|
||||
doTest("java.lang.IllegalArgumentException", "",
|
||||
doTest("java.lang.IllegalArgumentException",
|
||||
"Find why 'obj' could be not-null",
|
||||
"class X {static void test(Object obj) {if (null != obj) {throw <caret>new IllegalArgumentException();}}}");
|
||||
}
|
||||
|
||||
public void testEnumEquality() {
|
||||
doTest("java.lang.IllegalArgumentException", "",
|
||||
doTest("java.lang.IllegalArgumentException",
|
||||
"Find why 'x' could be X.A",
|
||||
"enum X {A,B,C;static void test(X x) {if (x == X.A) throw <caret>new IllegalArgumentException();}}");
|
||||
}
|
||||
|
||||
public void testInSwitch() {
|
||||
doTest("java.lang.IllegalArgumentException", "",
|
||||
doTest("java.lang.IllegalArgumentException",
|
||||
"Find why 'x' could be 5",
|
||||
"class X {" +
|
||||
" static void test(int x) {\n" +
|
||||
@@ -183,13 +183,13 @@ public class DataflowExceptionAnalysisProviderTest extends LightJavaCodeInsightT
|
||||
}
|
||||
|
||||
public void testInSwitchRule() {
|
||||
doTest("java.lang.IllegalArgumentException", "",
|
||||
doTest("java.lang.IllegalArgumentException",
|
||||
"Find why 'x' could be in {2, 3, 5}",
|
||||
"class X {static void test(int x) { switch (x) { case 2,3,5 -> throw <caret>new RuntimeException();default -> {} } }}");
|
||||
}
|
||||
|
||||
public void testInSwitchDefault() {
|
||||
doTest("java.lang.IllegalArgumentException", "",
|
||||
doTest("java.lang.IllegalArgumentException",
|
||||
"Find why 'x' could be <= 0 or >= 5",
|
||||
"class X {static void test(int x) { switch (x) { " +
|
||||
"case 1: break; case 2: break; case 3, 4: break; " +
|
||||
@@ -197,7 +197,7 @@ public class DataflowExceptionAnalysisProviderTest extends LightJavaCodeInsightT
|
||||
}
|
||||
|
||||
public void testInSwitchDefaultString() {
|
||||
doTest("java.lang.IllegalArgumentException", "",
|
||||
doTest("java.lang.IllegalArgumentException",
|
||||
"Find why 's' could be != \"BAR\", \"FOO\" (not-null)",
|
||||
"class X {static void test(String s) { switch (s) { " +
|
||||
"case \"FOO\": break; case \"BAR\": break;" +
|
||||
@@ -205,7 +205,7 @@ public class DataflowExceptionAnalysisProviderTest extends LightJavaCodeInsightT
|
||||
}
|
||||
|
||||
public void testIfExits() {
|
||||
doTest("java.lang.IllegalArgumentException", "",
|
||||
doTest("java.lang.IllegalArgumentException",
|
||||
"Find why 'x' could be >= 0",
|
||||
"class X {" +
|
||||
" static void test(int x) {\n" +
|
||||
@@ -219,60 +219,59 @@ public class DataflowExceptionAnalysisProviderTest extends LightJavaCodeInsightT
|
||||
}
|
||||
|
||||
public void testNoInfo() {
|
||||
doTest("java.lang.IllegalArgumentException", "",
|
||||
doTest("java.lang.IllegalArgumentException",
|
||||
null,
|
||||
"class X {static void test(X x) {throw <caret>new IllegalArgumentException();}}");
|
||||
}
|
||||
|
||||
public void testNegativeArraySizeException() {
|
||||
doTest("java.lang.NegativeArraySizeException", "-2",
|
||||
doTest("java.lang.NegativeArraySizeException: -2",
|
||||
"Find why 'x' could be -2",
|
||||
"class X {static void test(int x) {int[] data = <caret>new int[x];}}");
|
||||
}
|
||||
|
||||
public void testDivisionByZero() {
|
||||
doTest("java.lang.ArithmeticException", "/ by zero",
|
||||
doTest("java.lang.ArithmeticException: / by zero",
|
||||
"Find why 'y' could be 0",
|
||||
"class X {static void test(int x, int y) {int[] data = new int[x<caret>/y];}}");
|
||||
}
|
||||
|
||||
public void testModByZero() {
|
||||
doTest("java.lang.ArithmeticException", "/ by zero",
|
||||
doTest("java.lang.ArithmeticException: / by zero",
|
||||
"Find why 'y' could be 0",
|
||||
"class X {static void test(int x, long y) {long res = x <caret>% y;}}");
|
||||
}
|
||||
|
||||
public void testRequireNonNull() {
|
||||
doTest(null, null, "Find why 'str' could be null",
|
||||
doTest(null, "Find why 'str' could be null",
|
||||
"class X {static void test(String str, String msg) {java.util.Objects.<caret>requireNonNull(str, msg);}}");
|
||||
}
|
||||
|
||||
public void testAssertNull() {
|
||||
doTest(null, null, "Find why 'str' could be not-null",
|
||||
doTest(null, "Find why 'str' could be not-null",
|
||||
"class X {static void test(String str) {<caret>assertNull(str);}" +
|
||||
"static void assertNull(Object obj) {if(obj != null) throw new AssertionError();}}");
|
||||
}
|
||||
|
||||
public void testAssertTrue() {
|
||||
doTest(null, null, "Find why 'x' could be <= 0",
|
||||
doTest(null, "Find why 'x' could be <= 0",
|
||||
"class X {static void test(int x) {<caret>assertTrue(x > 0);}" +
|
||||
"static void assertTrue(boolean flag) {if(!flag) throw new AssertionError();}}");
|
||||
}
|
||||
|
||||
public void testAssertFalse() {
|
||||
doTest(null, null, "Find why 'x' could be >= 1",
|
||||
doTest(null, "Find why 'x' could be >= 1",
|
||||
"class X {static void test(int x) {<caret>assertFalse(x > 0);}" +
|
||||
"static void assertFalse(boolean flag) {if(flag) throw new AssertionError();}}");
|
||||
}
|
||||
|
||||
public void testOptionalGet() {
|
||||
// Not supported
|
||||
doTest(null, null, null,
|
||||
doTest(null, null,
|
||||
"class X {static void test(java.util.Optional x) {x.<caret>get();}}");
|
||||
}
|
||||
|
||||
private void doTest(@Nullable("If intermediate line action should be tested") String exceptionName,
|
||||
@Nullable("If exceptionName is null") String exceptionMessage,
|
||||
private void doTest(@Nullable("If intermediate line action should be tested") String exceptionLine,
|
||||
@Nullable("If no action is expected") String expectedActionTitle,
|
||||
@NotNull String source) {
|
||||
configureFromFileText("Test.java", source);
|
||||
@@ -281,11 +280,11 @@ public class DataflowExceptionAnalysisProviderTest extends LightJavaCodeInsightT
|
||||
PsiElement leaf = getFile().findElementAt(offset);
|
||||
AnAction action;
|
||||
ExceptionAnalysisProvider analysisProvider = getProject().getService(ExceptionAnalysisProvider.class);
|
||||
if (exceptionName != null) {
|
||||
assertNotNull(exceptionMessage);
|
||||
action = analysisProvider.getAnalysisAction(leaf, exceptionName, exceptionMessage);
|
||||
if (exceptionLine != null) {
|
||||
ExceptionInfo info = ExceptionInfo.parseMessage(exceptionLine, 0);
|
||||
assertNotNull(info);
|
||||
action = analysisProvider.getAnalysisAction(leaf, info);
|
||||
} else {
|
||||
assertNull(exceptionMessage);
|
||||
action = analysisProvider.getIntermediateRowAnalysisAction(leaf);
|
||||
}
|
||||
if (expectedActionTitle == null) {
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright 2000-2020 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.execution.filters;
|
||||
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.psi.util.TypeConversionUtil;
|
||||
|
||||
public class ArithmeticExceptionInfo extends ExceptionInfo {
|
||||
ArithmeticExceptionInfo(int offset, String message) {
|
||||
super(offset, "java.lang.ArithmeticException", message);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isSpecificExceptionElement(PsiElement e) {
|
||||
if (e instanceof PsiJavaToken && (e.textMatches("%") || e.textMatches("/")) &&
|
||||
e.getParent() instanceof PsiPolyadicExpression) {
|
||||
PsiExpression prevOperand = PsiTreeUtil.getPrevSiblingOfType(e, PsiExpression.class);
|
||||
PsiExpression nextOperand = PsiUtil.skipParenthesizedExprDown(PsiTreeUtil.getNextSiblingOfType(e, PsiExpression.class));
|
||||
if (prevOperand != null && TypeConversionUtil.isIntegralNumberType(prevOperand.getType()) &&
|
||||
nextOperand != null && TypeConversionUtil.isIntegralNumberType(nextOperand.getType())) {
|
||||
while (nextOperand instanceof PsiUnaryExpression && ((PsiUnaryExpression)nextOperand).getOperationTokenType().equals(
|
||||
JavaTokenType.MINUS)) {
|
||||
nextOperand = PsiUtil.skipParenthesizedExprDown(((PsiUnaryExpression)nextOperand).getOperand());
|
||||
}
|
||||
if (nextOperand instanceof PsiLiteral) {
|
||||
Object value = ((PsiLiteral)nextOperand).getValue();
|
||||
if (value instanceof Number && ((Number)value).longValue() != 0) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
// Copyright 2000-2020 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.execution.filters;
|
||||
|
||||
import com.intellij.psi.PsiArrayAccessExpression;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiJavaToken;
|
||||
import com.intellij.psi.PsiLiteralExpression;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class ArrayIndexOutOfBoundsExceptionInfo extends ExceptionInfo {
|
||||
private static final Pattern AIOOBE_MESSAGE = Pattern.compile("(?:Index )?(-?\\d{1,9})(?: out of bounds for length \\d+)?");
|
||||
|
||||
private final @Nullable Integer myIndex;
|
||||
|
||||
ArrayIndexOutOfBoundsExceptionInfo(int offset, String message) {
|
||||
super(offset, "java.lang.ArrayIndexOutOfBoundsException", message);
|
||||
Matcher matcher = AIOOBE_MESSAGE.matcher(message);
|
||||
if (matcher.matches()) {
|
||||
myIndex = Integer.valueOf(matcher.group(1) == null ? matcher.group(2) : matcher.group(1));
|
||||
} else {
|
||||
myIndex = null;
|
||||
}
|
||||
}
|
||||
|
||||
public @Nullable Integer getIndex() {
|
||||
return myIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isSpecificExceptionElement(PsiElement e) {
|
||||
if (!(e instanceof PsiJavaToken && e.textMatches("[") && e.getParent() instanceof PsiArrayAccessExpression)) {
|
||||
return false;
|
||||
}
|
||||
if (myIndex != null) {
|
||||
PsiLiteralExpression next = ObjectUtils.tryCast(PsiTreeUtil.skipWhitespacesAndCommentsForward(e), PsiLiteralExpression.class);
|
||||
return next == null || myIndex.equals(next.getValue());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright 2000-2020 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.execution.filters;
|
||||
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
|
||||
public class ArrayStoreExceptionInfo extends ExceptionInfo {
|
||||
ArrayStoreExceptionInfo(int offset, String message) {
|
||||
super(offset, "java.lang.ArrayStoreException", message);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isSpecificExceptionElement(PsiElement e) {
|
||||
if (e instanceof PsiJavaToken && e.textMatches("=") && e.getParent() instanceof PsiAssignmentExpression) {
|
||||
PsiExpression lExpression = ((PsiAssignmentExpression)e.getParent()).getLExpression();
|
||||
return PsiUtil.skipParenthesizedExprDown(lExpression) instanceof PsiArrayAccessExpression;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright 2000-2020 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.execution.filters;
|
||||
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiKeyword;
|
||||
|
||||
public class AssertionErrorInfo extends ExceptionInfo {
|
||||
AssertionErrorInfo(int offset, String message) {
|
||||
super(offset, "java.lang.ArrayStoreException", message);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isSpecificExceptionElement(PsiElement e) {
|
||||
return e instanceof PsiKeyword && e.textMatches(PsiKeyword.ASSERT);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
// Copyright 2000-2020 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.execution.filters;
|
||||
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import gnu.trove.THashSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class ClassCastExceptionInfo extends ExceptionInfo {
|
||||
private static final Pattern CCE_MESSAGE = Pattern.compile("(?:class )?(\\S+) cannot be cast to (?:class )?(\\S+)(?: \\(.+\\))?");
|
||||
private final @Nullable String myTargetClass;
|
||||
private final @Nullable String myActualClass;
|
||||
|
||||
ClassCastExceptionInfo(int offset, @NotNull String exceptionMessage) {
|
||||
super(offset, "java.lang.ClassCastException", exceptionMessage);
|
||||
Matcher matcher = CCE_MESSAGE.matcher(exceptionMessage);
|
||||
if (matcher.matches()) {
|
||||
myTargetClass = matcher.group(2);
|
||||
myActualClass = matcher.group(1);
|
||||
} else {
|
||||
myTargetClass = null;
|
||||
myActualClass = null;
|
||||
}
|
||||
}
|
||||
|
||||
public @Nullable String getActualClass() {
|
||||
return myActualClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isSpecificExceptionElement(PsiElement e) {
|
||||
if (myTargetClass == null) return false;
|
||||
if (e instanceof PsiJavaToken && e.textMatches("(") && e.getParent() instanceof PsiTypeCastExpression) {
|
||||
PsiTypeElement typeElement = ((PsiTypeCastExpression)e.getParent()).getCastType();
|
||||
if (typeElement == null) return true;
|
||||
return castClassMatches(typeElement.getType(), myTargetClass);
|
||||
}
|
||||
if (e instanceof PsiIdentifier && e.getParent() instanceof PsiReferenceExpression) {
|
||||
PsiReferenceExpression ref = (PsiReferenceExpression)e.getParent();
|
||||
PsiElement target = ref.resolve();
|
||||
PsiType type;
|
||||
if (target instanceof PsiMethod) {
|
||||
type = ((PsiMethod)target).getReturnType();
|
||||
}
|
||||
else if (target instanceof PsiVariable) {
|
||||
type = ((PsiVariable)target).getType();
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
PsiClass psiClass = PsiUtil.resolveClassInType(type);
|
||||
if (!(psiClass instanceof PsiTypeParameter)) return false;
|
||||
// Implicit cast added by compiler
|
||||
return castClassMatches(ref.getType(), myTargetClass);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean castClassMatches(PsiType type, String className) {
|
||||
if (type instanceof PsiPrimitiveType) {
|
||||
return className.equals(((PsiPrimitiveType)type).getBoxedTypeName());
|
||||
}
|
||||
if (type instanceof PsiIntersectionType) {
|
||||
for (PsiType conjunct : ((PsiIntersectionType)type).getConjuncts()) {
|
||||
if (castClassMatches(conjunct, className)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (type instanceof PsiArrayType) {
|
||||
if (className.startsWith("[") && className.length() > 1) {
|
||||
PsiType componentType = ((PsiArrayType)type).getComponentType();
|
||||
switch (className.charAt(1)) {
|
||||
case '[':
|
||||
return castClassMatches(componentType, className.substring(1));
|
||||
case 'B':
|
||||
return componentType.equals(PsiType.BYTE);
|
||||
case 'C':
|
||||
return componentType.equals(PsiType.CHAR);
|
||||
case 'D':
|
||||
return componentType.equals(PsiType.DOUBLE);
|
||||
case 'F':
|
||||
return componentType.equals(PsiType.FLOAT);
|
||||
case 'Z':
|
||||
return componentType.equals(PsiType.BOOLEAN);
|
||||
case 'I':
|
||||
return componentType.equals(PsiType.INT);
|
||||
case 'J':
|
||||
return componentType.equals(PsiType.LONG);
|
||||
case 'S':
|
||||
return componentType.equals(PsiType.SHORT);
|
||||
case 'L':
|
||||
if (className.charAt(className.length() - 1) == ';') {
|
||||
return castClassMatches(componentType, className.substring(2, className.length() - 1));
|
||||
}
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (type instanceof PsiClassType) {
|
||||
return classTypeMatches(className, (PsiClassType)type, new THashSet<>());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean classTypeMatches(String className, PsiClassType classType, Set<PsiClass> visited) {
|
||||
PsiClass psiClass = PsiUtil.resolveClassInType(classType);
|
||||
if (!visited.add(psiClass)) {
|
||||
return true;
|
||||
}
|
||||
if (psiClass instanceof PsiTypeParameter) {
|
||||
for (PsiClassType bound : ((PsiTypeParameter)psiClass).getExtendsList().getReferencedTypes()) {
|
||||
if (classTypeMatches(className, bound, visited)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
String name = classType.getClassName();
|
||||
if (name == null) return true;
|
||||
if (!name.equals(StringUtil.substringAfterLast(className, ".")) &&
|
||||
!name.equals(StringUtil.substringAfterLast(className, "$"))) {
|
||||
return false;
|
||||
}
|
||||
if (psiClass != null) {
|
||||
if (className.equals(psiClass.getQualifiedName())) return true;
|
||||
String packageName = StringUtil.getPackageName(className);
|
||||
PsiFile psiFile = psiClass.getContainingFile();
|
||||
return psiFile instanceof PsiClassOwner && packageName.equals(((PsiClassOwner)psiFile).getPackageName());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -11,13 +11,10 @@ import org.jetbrains.annotations.Nullable;
|
||||
public interface ExceptionAnalysisProvider {
|
||||
/**
|
||||
* @param anchor a place (leaf element) in the source code where exception occurred
|
||||
* @param exceptionClassName exception class name (fully-qualified)
|
||||
* @param exceptionMessage exception message (fully-qualified)
|
||||
* @param info exception info
|
||||
* @return an action to provide additional analysis for given exception; null if not available.
|
||||
*/
|
||||
@Nullable AnAction getAnalysisAction(@NotNull PsiElement anchor,
|
||||
@NotNull String exceptionClassName,
|
||||
@NotNull String exceptionMessage);
|
||||
@Nullable AnAction getAnalysisAction(@NotNull PsiElement anchor, @NotNull ExceptionInfo info);
|
||||
|
||||
/**
|
||||
* @param anchor a place (method name identifier) in the source code where next stack frame row is invoked
|
||||
|
||||
@@ -1,38 +1,17 @@
|
||||
// Copyright 2000-2020 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.execution.filters;
|
||||
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.psi.util.TypeConversionUtil;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiKeyword;
|
||||
import com.intellij.psi.PsiNewExpression;
|
||||
import com.intellij.psi.PsiType;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import gnu.trove.THashSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class ExceptionInfo {
|
||||
private static final Pattern AIOOBE_MESSAGE = Pattern.compile("(?:Index )?(-?\\d{1,9})(?: out of bounds for length \\d+)?");
|
||||
private static final Pattern CCE_MESSAGE = Pattern.compile("(?:class )?(\\S+) cannot be cast to (?:class )?(\\S+)(?: \\(.+\\))?");
|
||||
// See JEP 358 Helpful NullPointerExceptions for details
|
||||
private static final Pattern NPE_MESSAGE = Pattern.compile("Cannot (?:invoke \"(?<invoke>.+)\\(\\)\"|" +
|
||||
"assign field \"(?<putfield>.+)\"|" +
|
||||
"read field \"(?<getfield>.+)\"|" +
|
||||
"store to (?<xastore>[a-z]+) array|" +
|
||||
"load from (?<xaload>[a-z]+) array|" +
|
||||
"read the array (?<arraylength>length)|" +
|
||||
"enter (?<monitor>synchronized) block|" +
|
||||
"throw (?<athrow>exception))(?: because .+)?");
|
||||
// Methods that could be added by compiler implicitly: either unboxing or getClass (implicit NPE check used for method-refs, etc.)
|
||||
private static final Set<String> IGNORED_METHODS = ContainerUtil.immutableSet("intValue", "longValue", "doubleValue", "floatValue",
|
||||
"shortValue", "byteValue", "booleanValue", "charValue",
|
||||
"getClass");
|
||||
private static final String EXCEPTION_IN_THREAD = "Exception in thread \"";
|
||||
private static final String CAUSED_BY = "Caused by: ";
|
||||
private final int myClassNameOffset;
|
||||
@@ -45,16 +24,14 @@ public class ExceptionInfo {
|
||||
myExceptionMessage = exceptionMessage;
|
||||
}
|
||||
|
||||
@NotNull ExceptionInfo adjust(@Nullable String message, int offset) {
|
||||
message = StringUtil.notNullize(message);
|
||||
return new ExceptionInfo(myClassNameOffset + offset, myExceptionClassName, message);
|
||||
boolean isSpecificExceptionElement(PsiElement element) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a predicate that matches an element within the source line that is likely an exception source
|
||||
*/
|
||||
@NotNull ExceptionLineRefiner getPositionRefiner() {
|
||||
Predicate<PsiElement> specificFilter = getExceptionSpecificFilter();
|
||||
return new ExceptionLineRefiner() {
|
||||
@Override
|
||||
public boolean test(PsiElement element) {
|
||||
@@ -66,7 +43,7 @@ public class ExceptionInfo {
|
||||
if (type != null && type.equalsToText(getExceptionClassName())) return true;
|
||||
}
|
||||
}
|
||||
return specificFilter != null && specificFilter.test(element);
|
||||
return isSpecificExceptionElement(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -76,66 +53,6 @@ public class ExceptionInfo {
|
||||
};
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Predicate<PsiElement> getExceptionSpecificFilter() {
|
||||
switch (getExceptionClassName()) {
|
||||
case "java.lang.ArrayIndexOutOfBoundsException":
|
||||
return e -> isArrayIndexOutOfBoundsSource(getExceptionMessage(), e);
|
||||
case "java.lang.ArrayStoreException":
|
||||
return e -> {
|
||||
if (e instanceof PsiJavaToken && e.textMatches("=") && e.getParent() instanceof PsiAssignmentExpression) {
|
||||
PsiExpression lExpression = ((PsiAssignmentExpression)e.getParent()).getLExpression();
|
||||
return PsiUtil.skipParenthesizedExprDown(lExpression) instanceof PsiArrayAccessExpression;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
case "java.lang.ClassCastException":
|
||||
return getClassCastPredicate(getExceptionMessage());
|
||||
case "java.lang.NullPointerException":
|
||||
return getNullPointerPredicate(getExceptionMessage());
|
||||
case "java.lang.AssertionError":
|
||||
return e -> e instanceof PsiKeyword && e.textMatches(PsiKeyword.ASSERT);
|
||||
case "java.lang.ArithmeticException":
|
||||
return e -> {
|
||||
if (e instanceof PsiJavaToken && (e.textMatches("%") || e.textMatches("/")) &&
|
||||
e.getParent() instanceof PsiPolyadicExpression) {
|
||||
PsiExpression prevOperand = PsiTreeUtil.getPrevSiblingOfType(e, PsiExpression.class);
|
||||
PsiExpression nextOperand = PsiUtil.skipParenthesizedExprDown(PsiTreeUtil.getNextSiblingOfType(e, PsiExpression.class));
|
||||
if (prevOperand != null && TypeConversionUtil.isIntegralNumberType(prevOperand.getType()) &&
|
||||
nextOperand != null && TypeConversionUtil.isIntegralNumberType(nextOperand.getType())) {
|
||||
while (nextOperand instanceof PsiUnaryExpression && ((PsiUnaryExpression)nextOperand).getOperationTokenType().equals(
|
||||
JavaTokenType.MINUS)) {
|
||||
nextOperand = PsiUtil.skipParenthesizedExprDown(((PsiUnaryExpression)nextOperand).getOperand());
|
||||
}
|
||||
if (nextOperand instanceof PsiLiteral) {
|
||||
Object value = ((PsiLiteral)nextOperand).getValue();
|
||||
if (value instanceof Number && ((Number)value).longValue() != 0) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
case "java.lang.NegativeArraySizeException":
|
||||
return e -> {
|
||||
if (e instanceof PsiKeyword && e.textMatches(PsiKeyword.NEW) && e.getParent() instanceof PsiNewExpression) {
|
||||
PsiExpression[] dimensions = ((PsiNewExpression)e.getParent()).getArrayDimensions();
|
||||
for (PsiExpression dimension : dimensions) {
|
||||
if (dimension != null) {
|
||||
PsiLiteral literal = ObjectUtils.tryCast(PsiUtil.skipParenthesizedExprDown(dimension), PsiLiteral.class);
|
||||
// Explicit negative number like -1 cannot be literal, it's unary expression
|
||||
if (literal != null && literal.getValue() instanceof Integer) continue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return offset of the class name within the parsed line
|
||||
*/
|
||||
@@ -157,255 +74,61 @@ public class ExceptionInfo {
|
||||
return myExceptionMessage;
|
||||
}
|
||||
|
||||
private static Predicate<PsiElement> getNullPointerPredicate(String message) {
|
||||
if (!message.startsWith("Cannot ")) return null;
|
||||
Matcher matcher = NPE_MESSAGE.matcher(message);
|
||||
if (!matcher.matches()) return null;
|
||||
if (matcher.group("athrow") != null) {
|
||||
return e -> e instanceof PsiKeyword && ((PsiKeyword)e).getTokenType().equals(JavaTokenType.THROW_KEYWORD);
|
||||
}
|
||||
if (matcher.group("monitor") != null) {
|
||||
return e -> e instanceof PsiKeyword && ((PsiKeyword)e).getTokenType().equals(JavaTokenType.SYNCHRONIZED_KEYWORD);
|
||||
}
|
||||
if (matcher.group("arraylength") != null) {
|
||||
return e -> {
|
||||
PsiExpression qualifier = getFieldReferenceQualifier(e, "length");
|
||||
return qualifier != null && qualifier.getType() instanceof PsiArrayType;
|
||||
};
|
||||
}
|
||||
String getField = matcher.group("getfield");
|
||||
String putField = matcher.group("putfield");
|
||||
String field = getField == null ? putField : getField;
|
||||
if (field != null) {
|
||||
return e -> {
|
||||
PsiExpression qualifier = getFieldReferenceQualifier(e, field);
|
||||
return qualifier != null && !(qualifier.getType() instanceof PsiArrayType) &&
|
||||
storeMatches(e.getParent(), getField == null);
|
||||
};
|
||||
}
|
||||
boolean arrayLoad = matcher.group("xaload") != null;
|
||||
boolean arrayStore = matcher.group("xastore") != null;
|
||||
if (arrayLoad || arrayStore) {
|
||||
return e -> {
|
||||
if (!(e instanceof PsiJavaToken) || !e.textMatches("[")) return false;
|
||||
PsiElement parent = e.getParent();
|
||||
if (!(parent instanceof PsiArrayAccessExpression)) return false;
|
||||
return storeMatches(parent, arrayStore);
|
||||
};
|
||||
}
|
||||
String method = matcher.group("invoke");
|
||||
if (method != null) {
|
||||
int dotPos = method.lastIndexOf('.');
|
||||
if (dotPos != -1) {
|
||||
String methodName = method.substring(dotPos + 1);
|
||||
if (!IGNORED_METHODS.contains(methodName)) {
|
||||
return e -> {
|
||||
if (!(e instanceof PsiIdentifier) || !e.textMatches(methodName)) return false;
|
||||
PsiElement parent = e.getParent();
|
||||
if (!(parent instanceof PsiReferenceExpression)) return false;
|
||||
if (!(parent.getParent() instanceof PsiMethodCallExpression)) return false;
|
||||
PsiExpression qualifier = PsiUtil.skipParenthesizedExprDown(((PsiReferenceExpression)parent).getQualifierExpression());
|
||||
if (qualifier == null || qualifier instanceof PsiNewExpression ||
|
||||
qualifier instanceof PsiLiteralExpression || qualifier instanceof PsiPolyadicExpression) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static @Nullable PsiExpression getFieldReferenceQualifier(PsiElement e, String fieldName) {
|
||||
if (!(e instanceof PsiIdentifier) || !e.textMatches(fieldName)) return null;
|
||||
PsiElement parent = e.getParent();
|
||||
if (!(parent instanceof PsiReferenceExpression)) return null;
|
||||
if (parent.getParent() instanceof PsiMethodCallExpression) return null;
|
||||
PsiExpression qualifier = PsiUtil.skipParenthesizedExprDown(((PsiReferenceExpression)parent).getQualifierExpression());
|
||||
if (qualifier == null || qualifier instanceof PsiNewExpression) return null;
|
||||
return qualifier;
|
||||
}
|
||||
|
||||
private static boolean storeMatches(PsiElement element, boolean mustBeStore) {
|
||||
PsiElement parent = element.getParent();
|
||||
while (parent instanceof PsiParenthesizedExpression) {
|
||||
element = parent;
|
||||
parent = element.getParent();
|
||||
}
|
||||
if (parent instanceof PsiAssignmentExpression) {
|
||||
PsiAssignmentExpression assignment = (PsiAssignmentExpression)parent;
|
||||
boolean isStore = assignment.getOperationTokenType().equals(JavaTokenType.EQ) &&
|
||||
assignment.getLExpression() == element;
|
||||
return isStore == mustBeStore;
|
||||
}
|
||||
return !mustBeStore;
|
||||
}
|
||||
|
||||
private static Predicate<PsiElement> getClassCastPredicate(@NotNull String message) {
|
||||
Matcher matcher = CCE_MESSAGE.matcher(message);
|
||||
if (!matcher.matches()) return null;
|
||||
String targetClass = matcher.group(2);
|
||||
return e -> {
|
||||
if (e instanceof PsiJavaToken && e.textMatches("(") && e.getParent() instanceof PsiTypeCastExpression) {
|
||||
PsiTypeElement typeElement = ((PsiTypeCastExpression)e.getParent()).getCastType();
|
||||
if (typeElement == null) return true;
|
||||
return castClassMatches(typeElement.getType(), targetClass);
|
||||
}
|
||||
if (e instanceof PsiIdentifier && e.getParent() instanceof PsiReferenceExpression) {
|
||||
PsiReferenceExpression ref = (PsiReferenceExpression)e.getParent();
|
||||
PsiElement target = ref.resolve();
|
||||
PsiType type;
|
||||
if (target instanceof PsiMethod) {
|
||||
type = ((PsiMethod)target).getReturnType();
|
||||
}
|
||||
else if (target instanceof PsiVariable) {
|
||||
type = ((PsiVariable)target).getType();
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
PsiClass psiClass = PsiUtil.resolveClassInType(type);
|
||||
if (!(psiClass instanceof PsiTypeParameter)) return false;
|
||||
// Implicit cast added by compiler
|
||||
return castClassMatches(ref.getType(), targetClass);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
private static boolean castClassMatches(PsiType type, String className) {
|
||||
if (type instanceof PsiPrimitiveType) {
|
||||
return className.equals(((PsiPrimitiveType)type).getBoxedTypeName());
|
||||
}
|
||||
if (type instanceof PsiIntersectionType) {
|
||||
for (PsiType conjunct : ((PsiIntersectionType)type).getConjuncts()) {
|
||||
if (castClassMatches(conjunct, className)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (type instanceof PsiArrayType) {
|
||||
if (className.startsWith("[") && className.length() > 1) {
|
||||
PsiType componentType = ((PsiArrayType)type).getComponentType();
|
||||
switch (className.charAt(1)) {
|
||||
case '[':
|
||||
return castClassMatches(componentType, className.substring(1));
|
||||
case 'B':
|
||||
return componentType.equals(PsiType.BYTE);
|
||||
case 'C':
|
||||
return componentType.equals(PsiType.CHAR);
|
||||
case 'D':
|
||||
return componentType.equals(PsiType.DOUBLE);
|
||||
case 'F':
|
||||
return componentType.equals(PsiType.FLOAT);
|
||||
case 'Z':
|
||||
return componentType.equals(PsiType.BOOLEAN);
|
||||
case 'I':
|
||||
return componentType.equals(PsiType.INT);
|
||||
case 'J':
|
||||
return componentType.equals(PsiType.LONG);
|
||||
case 'S':
|
||||
return componentType.equals(PsiType.SHORT);
|
||||
case 'L':
|
||||
if (className.charAt(className.length() - 1) == ';') {
|
||||
return castClassMatches(componentType, className.substring(2, className.length() - 1));
|
||||
}
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (type instanceof PsiClassType) {
|
||||
return classTypeMatches(className, (PsiClassType)type, new THashSet<>());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean classTypeMatches(String className, PsiClassType classType, Set<PsiClass> visited) {
|
||||
PsiClass psiClass = PsiUtil.resolveClassInType(classType);
|
||||
if (!visited.add(psiClass)) {
|
||||
return true;
|
||||
}
|
||||
if (psiClass instanceof PsiTypeParameter) {
|
||||
for (PsiClassType bound : ((PsiTypeParameter)psiClass).getExtendsList().getReferencedTypes()) {
|
||||
if (classTypeMatches(className, bound, visited)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
String name = classType.getClassName();
|
||||
if (name == null) return true;
|
||||
if (!name.equals(StringUtil.substringAfterLast(className, ".")) &&
|
||||
!name.equals(StringUtil.substringAfterLast(className, "$"))) {
|
||||
return false;
|
||||
}
|
||||
if (psiClass != null) {
|
||||
if (className.equals(psiClass.getQualifiedName())) return true;
|
||||
String packageName = StringUtil.getPackageName(className);
|
||||
PsiFile psiFile = psiClass.getContainingFile();
|
||||
return psiFile instanceof PsiClassOwner && packageName.equals(((PsiClassOwner)psiFile).getPackageName());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean isArrayIndexOutOfBoundsSource(@NotNull String message, PsiElement e) {
|
||||
if (!(e instanceof PsiJavaToken && e.textMatches("[") && e.getParent() instanceof PsiArrayAccessExpression)) {
|
||||
return false;
|
||||
}
|
||||
Integer index = getArrayIndexFromMessage(message);
|
||||
if (index != null) {
|
||||
PsiLiteralExpression next = ObjectUtils.tryCast(PsiTreeUtil.skipWhitespacesAndCommentsForward(e), PsiLiteralExpression.class);
|
||||
return next == null || index.equals(next.getValue());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static @Nullable Integer getArrayIndexFromMessage(@NotNull String message) {
|
||||
Matcher matcher = AIOOBE_MESSAGE.matcher(message);
|
||||
if (matcher.matches()) {
|
||||
return Integer.valueOf(matcher.group(1) == null ? matcher.group(2) : matcher.group(1));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static @Nullable String getCastActualClassFromMessage(@NotNull String message) {
|
||||
Matcher matcher = CCE_MESSAGE.matcher(message);
|
||||
if (matcher.matches()) {
|
||||
return matcher.group(1);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static ExceptionInfo parseMessage(String line, int textEndOffset) {
|
||||
int firstSpace = line.indexOf(' ');
|
||||
int colonPos = -1;
|
||||
ExceptionInfo info = null;
|
||||
TextRange classRange = null;
|
||||
if (firstSpace == -1) {
|
||||
info = getExceptionClassFromMessage(line, 0, getLength(line));
|
||||
classRange = getExceptionClassFromMessage(line, 0, getLength(line));
|
||||
}
|
||||
else if (firstSpace == "Caused".length() && line.startsWith(CAUSED_BY)) {
|
||||
colonPos = line.indexOf(':', CAUSED_BY.length());
|
||||
info = getExceptionClassFromMessage(line, CAUSED_BY.length(), colonPos == -1 ? getLength(line) : colonPos);
|
||||
classRange = getExceptionClassFromMessage(line, CAUSED_BY.length(), colonPos == -1 ? getLength(line) : colonPos);
|
||||
}
|
||||
else if (firstSpace == "Exception".length() && line.startsWith(EXCEPTION_IN_THREAD)) {
|
||||
int nextQuotePos = line.indexOf("\" ", EXCEPTION_IN_THREAD.length());
|
||||
if (nextQuotePos == -1) return null;
|
||||
int start = nextQuotePos + "\" ".length();
|
||||
colonPos = line.indexOf(':', start);
|
||||
info = getExceptionClassFromMessage(line, start, colonPos == -1 ? getLength(line) : colonPos);
|
||||
classRange = getExceptionClassFromMessage(line, start, colonPos == -1 ? getLength(line) : colonPos);
|
||||
}
|
||||
else if (firstSpace > 2 && line.charAt(firstSpace - 1) == ':') {
|
||||
colonPos = firstSpace - 1;
|
||||
info = getExceptionClassFromMessage(line, 0, firstSpace - 1);
|
||||
classRange = getExceptionClassFromMessage(line, 0, firstSpace - 1);
|
||||
}
|
||||
else if (firstSpace > 3 && line.charAt(0) == '[' && line.charAt(firstSpace - 1) == ':' && line.charAt(firstSpace - 2) == ']') {
|
||||
colonPos = line.indexOf(':', firstSpace);
|
||||
info = getExceptionClassFromMessage(line, firstSpace + 1, colonPos == -1 ? getLength(line) : colonPos);
|
||||
classRange = getExceptionClassFromMessage(line, firstSpace + 1, colonPos == -1 ? getLength(line) : colonPos);
|
||||
}
|
||||
if (info == null) return null;
|
||||
if (classRange == null) return null;
|
||||
String message = colonPos == -1 ? null : line.substring(colonPos + 1).trim();
|
||||
return info.adjust(message, textEndOffset - line.length());
|
||||
message = StringUtil.notNullize(message);
|
||||
int absoluteOffset = textEndOffset - line.length();
|
||||
String exceptionName = line.substring(classRange.getStartOffset(), classRange.getEndOffset());
|
||||
int startOffset = classRange.getStartOffset() + absoluteOffset;
|
||||
return createExceptionInfo(message, exceptionName, startOffset);
|
||||
}
|
||||
|
||||
private static @NotNull ExceptionInfo createExceptionInfo(String message, String exceptionName, int startOffset) {
|
||||
switch (exceptionName) {
|
||||
case "java.lang.ArrayIndexOutOfBoundsException":
|
||||
return new ArrayIndexOutOfBoundsExceptionInfo(startOffset, message);
|
||||
case "java.lang.ArrayStoreException":
|
||||
return new ArrayStoreExceptionInfo(startOffset, message);
|
||||
case "java.lang.ClassCastException":
|
||||
return new ClassCastExceptionInfo(startOffset, message);
|
||||
case "java.lang.NullPointerException":
|
||||
return new NullPointerExceptionInfo(startOffset, message);
|
||||
case "java.lang.AssertionError":
|
||||
return new AssertionErrorInfo(startOffset, message);
|
||||
case "java.lang.ArithmeticException":
|
||||
return new ArithmeticExceptionInfo(startOffset, message);
|
||||
case "java.lang.NegativeArraySizeException":
|
||||
return new NegativeArraySizeExceptionInfo(startOffset, message);
|
||||
default:
|
||||
return new ExceptionInfo(startOffset, exceptionName, message);
|
||||
}
|
||||
}
|
||||
|
||||
private static int getLength(String line) {
|
||||
@@ -426,7 +149,7 @@ public class ExceptionInfo {
|
||||
* @param to end index (exclusive)
|
||||
* @return a substring between from and to or null if it doesn't look like an exception name.
|
||||
*/
|
||||
private static ExceptionInfo getExceptionClassFromMessage(String line, int from, int to) {
|
||||
private static TextRange getExceptionClassFromMessage(String line, int from, int to) {
|
||||
if (to - from > 200) return null;
|
||||
boolean hasDot = false;
|
||||
for (int i = from; i < to; i++) {
|
||||
@@ -437,6 +160,6 @@ public class ExceptionInfo {
|
||||
hasDot |= c == '.';
|
||||
}
|
||||
if (!hasDot) return null;
|
||||
return new ExceptionInfo(from, line.substring(from, to), "");
|
||||
return new TextRange(from, to);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -424,7 +424,7 @@ public class ExceptionWorker {
|
||||
if (info == null) {
|
||||
action = exceptionAnalysisProvider.getIntermediateRowAnalysisAction(element);
|
||||
} else {
|
||||
action = exceptionAnalysisProvider.getAnalysisAction(element, info.getExceptionClassName(), info.getExceptionMessage());
|
||||
action = exceptionAnalysisProvider.getAnalysisAction(element, info);
|
||||
}
|
||||
if (action == null) return;
|
||||
String actionName = Objects.requireNonNull(action.getTemplatePresentation().getDescription());
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright 2000-2020 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.execution.filters;
|
||||
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class NegativeArraySizeExceptionInfo extends ExceptionInfo {
|
||||
NegativeArraySizeExceptionInfo(int offset, String message) {
|
||||
super(offset, "java.lang.NegativeArraySizeException", message);
|
||||
}
|
||||
|
||||
public @Nullable Integer getSuppliedSize() {
|
||||
try {
|
||||
return Integer.valueOf(getExceptionMessage());
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isSpecificExceptionElement(PsiElement e) {
|
||||
if (e instanceof PsiKeyword && e.textMatches(PsiKeyword.NEW) && e.getParent() instanceof PsiNewExpression) {
|
||||
PsiExpression[] dimensions = ((PsiNewExpression)e.getParent()).getArrayDimensions();
|
||||
for (PsiExpression dimension : dimensions) {
|
||||
if (dimension != null) {
|
||||
PsiLiteral literal = ObjectUtils.tryCast(PsiUtil.skipParenthesizedExprDown(dimension), PsiLiteral.class);
|
||||
// Explicit negative number like -1 cannot be literal, it's unary expression
|
||||
if (literal != null && literal.getValue() instanceof Integer) continue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
// Copyright 2000-2020 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.execution.filters;
|
||||
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class NullPointerExceptionInfo extends ExceptionInfo {
|
||||
// See JEP 358 Helpful NullPointerExceptions for details
|
||||
private static final Pattern NPE_MESSAGE = Pattern.compile("Cannot (?:invoke \"(?<invoke>.+)\\(\\)\"|" +
|
||||
"assign field \"(?<putfield>.+)\"|" +
|
||||
"read field \"(?<getfield>.+)\"|" +
|
||||
"store to (?<xastore>[a-z]+) array|" +
|
||||
"load from (?<xaload>[a-z]+) array|" +
|
||||
"read the array (?<arraylength>length)|" +
|
||||
"enter (?<monitor>synchronized) block|" +
|
||||
"throw (?<athrow>exception))(?: because .+)?");
|
||||
// Methods that could be added by compiler implicitly: either unboxing or getClass (implicit NPE check used for method-refs, etc.)
|
||||
private static final Set<String> IGNORED_METHODS = ContainerUtil.immutableSet("intValue", "longValue", "doubleValue", "floatValue",
|
||||
"shortValue", "byteValue", "booleanValue", "charValue",
|
||||
"getClass");
|
||||
final Predicate<PsiElement> myPredicate;
|
||||
|
||||
NullPointerExceptionInfo(int offset, String message) {
|
||||
super(offset, CommonClassNames.JAVA_LANG_NULL_POINTER_EXCEPTION, message);
|
||||
myPredicate = getPredicate(message);
|
||||
}
|
||||
|
||||
private static Predicate<PsiElement> getPredicate(String message) {
|
||||
if (!message.startsWith("Cannot ")) return null;
|
||||
Matcher matcher = NPE_MESSAGE.matcher(message);
|
||||
if (!matcher.matches()) return null;
|
||||
if (matcher.group("athrow") != null) {
|
||||
return e -> e instanceof PsiKeyword && ((PsiKeyword)e).getTokenType().equals(JavaTokenType.THROW_KEYWORD);
|
||||
}
|
||||
if (matcher.group("monitor") != null) {
|
||||
return e -> e instanceof PsiKeyword && ((PsiKeyword)e).getTokenType().equals(JavaTokenType.SYNCHRONIZED_KEYWORD);
|
||||
}
|
||||
if (matcher.group("arraylength") != null) {
|
||||
return e -> {
|
||||
PsiExpression qualifier = getFieldReferenceQualifier(e, "length");
|
||||
return qualifier != null && qualifier.getType() instanceof PsiArrayType;
|
||||
};
|
||||
}
|
||||
String getField = matcher.group("getfield");
|
||||
String putField = matcher.group("putfield");
|
||||
String field = getField == null ? putField : getField;
|
||||
if (field != null) {
|
||||
return e -> {
|
||||
PsiExpression qualifier = getFieldReferenceQualifier(e, field);
|
||||
return qualifier != null && !(qualifier.getType() instanceof PsiArrayType) &&
|
||||
storeMatches(e.getParent(), getField == null);
|
||||
};
|
||||
}
|
||||
boolean arrayLoad = matcher.group("xaload") != null;
|
||||
boolean arrayStore = matcher.group("xastore") != null;
|
||||
if (arrayLoad || arrayStore) {
|
||||
return e -> {
|
||||
if (!(e instanceof PsiJavaToken) || !e.textMatches("[")) return false;
|
||||
PsiElement parent = e.getParent();
|
||||
if (!(parent instanceof PsiArrayAccessExpression)) return false;
|
||||
return storeMatches(parent, arrayStore);
|
||||
};
|
||||
}
|
||||
String method = matcher.group("invoke");
|
||||
if (method != null) {
|
||||
int dotPos = method.lastIndexOf('.');
|
||||
if (dotPos != -1) {
|
||||
String methodName = method.substring(dotPos + 1);
|
||||
if (!IGNORED_METHODS.contains(methodName)) {
|
||||
return e -> {
|
||||
if (!(e instanceof PsiIdentifier) || !e.textMatches(methodName)) return false;
|
||||
PsiElement parent = e.getParent();
|
||||
if (!(parent instanceof PsiReferenceExpression)) return false;
|
||||
if (!(parent.getParent() instanceof PsiMethodCallExpression)) return false;
|
||||
PsiExpression qualifier = PsiUtil.skipParenthesizedExprDown(((PsiReferenceExpression)parent).getQualifierExpression());
|
||||
if (qualifier == null || qualifier instanceof PsiNewExpression ||
|
||||
qualifier instanceof PsiLiteralExpression || qualifier instanceof PsiPolyadicExpression) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static @Nullable PsiExpression getFieldReferenceQualifier(PsiElement e, String fieldName) {
|
||||
if (!(e instanceof PsiIdentifier) || !e.textMatches(fieldName)) return null;
|
||||
PsiElement parent = e.getParent();
|
||||
if (!(parent instanceof PsiReferenceExpression)) return null;
|
||||
if (parent.getParent() instanceof PsiMethodCallExpression) return null;
|
||||
PsiExpression qualifier = PsiUtil.skipParenthesizedExprDown(((PsiReferenceExpression)parent).getQualifierExpression());
|
||||
if (qualifier == null || qualifier instanceof PsiNewExpression) return null;
|
||||
return qualifier;
|
||||
}
|
||||
|
||||
private static boolean storeMatches(PsiElement element, boolean mustBeStore) {
|
||||
PsiElement parent = element.getParent();
|
||||
while (parent instanceof PsiParenthesizedExpression) {
|
||||
element = parent;
|
||||
parent = element.getParent();
|
||||
}
|
||||
if (parent instanceof PsiAssignmentExpression) {
|
||||
PsiAssignmentExpression assignment = (PsiAssignmentExpression)parent;
|
||||
boolean isStore = assignment.getOperationTokenType().equals(JavaTokenType.EQ) &&
|
||||
assignment.getLExpression() == element;
|
||||
return isStore == mustBeStore;
|
||||
}
|
||||
return !mustBeStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isSpecificExceptionElement(PsiElement e) {
|
||||
return myPredicate != null && myPredicate.test(e);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user