[java-analysis] clean up: simplify getConstructorSideEffect, relying on MutationSignature only

GitOrigin-RevId: f919f8fd00fb2f3b2de2c385e75010c6ca70a1b4
This commit is contained in:
Tagir Valeev
2025-10-16 22:02:23 +00:00
committed by intellij-monorepo-bot
parent da68b424b9
commit 2eb0f4d07c
5 changed files with 214 additions and 52 deletions
@@ -329,52 +329,12 @@ public final class SideEffectChecker {
}
private static @NotNull ThreeState getConstructorSideEffect(@NotNull PsiNewExpression newExpression) {
if (newExpression.isArrayCreation()) return ThreeState.NO;
PsiAnonymousClass anonymousClass = newExpression.getAnonymousClass();
if (anonymousClass != null && anonymousClass.getInitializers().length == 0) {
PsiClass baseClass = anonymousClass.getBaseClassType().resolve();
if (baseClass != null && baseClass.isInterface()) {
return ThreeState.NO;
}
MutationSignature signature = MutationSignature.fromCall(newExpression);
if (signature.isPure()) {
return ThreeState.NO;
}
PsiJavaCodeReferenceElement classReference = newExpression.getClassReference();
PsiClass aClass = classReference == null ? null : tryCast(classReference.resolve(), PsiClass.class);
String qualifiedName = aClass == null ? null : aClass.getQualifiedName();
if (qualifiedName == null) return ThreeState.UNSURE;
if (ourSideEffectFreeClasses.contains(qualifiedName)) return ThreeState.NO;
PsiMethod method = newExpression.resolveConstructor();
if (method != null && !method.isDefaultConstructor()) {
MutationSignature signature = MutationSignature.fromMethod(method);
if (signature.isPure()) {
return ThreeState.NO;
}
if (signature.mutatesAnything()) {
return ThreeState.YES;
}
}
PsiFile file = aClass.getContainingFile();
PsiDirectory directory = file.getContainingDirectory();
PsiPackage classPackage = directory == null ? null : JavaDirectoryService.getInstance().getPackage(directory);
String packageName = classPackage == null ? null : classPackage.getQualifiedName();
// all Throwable descendants from java.lang are side effects free
if (CommonClassNames.DEFAULT_PACKAGE.equals(packageName) || "java.io".equals(packageName)) {
if (PsiClassUtil.isThrowable(aClass)) {
return ThreeState.NO;
}
}
if (method == null || method.isDefaultConstructor()) {
PsiClass superClass = aClass.getSuperClass();
if (superClass != null && CommonClassNames.JAVA_LANG_OBJECT.equals(superClass.getQualifiedName())) {
for (PsiClassInitializer initializer : aClass.getInitializers()) {
if (!initializer.hasModifierProperty(PsiModifier.STATIC)) return ThreeState.UNSURE;
}
for (PsiField field : aClass.getFields()) {
if (!field.hasModifierProperty(PsiModifier.STATIC) && field.hasInitializer()) return ThreeState.UNSURE;
}
return ThreeState.NO;
}
if (signature.mutatesAnything()) {
return ThreeState.YES;
}
return ThreeState.UNSURE;
}
@@ -3,6 +3,8 @@ package com.siyeh.igtest.controlflow.pointless_null_check;
public class PointlessNullCheck {
String arg1 = "foo";
double sideEffect = Math.random();
public void testMethods(Object obj, Object obj1, Object obj2) {
if(<warning descr="Unnecessary 'null' check before 'check()' call">obj != null</warning> && check(obj)) {
@@ -1,10 +1,5 @@
class Foo {
void test() {
new Object() {
void foo(String s) {
System.out.println(s);
}
};
System.out.println("hello");
}
}
+92 -2
View File
@@ -5,6 +5,11 @@
<val name="pure" val="true"/>
</annotation>
</item>
<item name='java.lang.Boolean Boolean(java.lang.String)'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
</annotation>
</item>
<item name='java.lang.Boolean boolean getBoolean(java.lang.String)'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
@@ -44,11 +49,21 @@
<item name='java.lang.Boolean java.lang.Boolean valueOf(java.lang.String)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item name='java.lang.Byte Byte(java.lang.String)'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
</annotation>
</item>
<item name='java.lang.Byte byte parseByte(java.lang.String)'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
</annotation>
</item>
<item name='java.lang.Byte byte parseByte(java.lang.String, int)'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
</annotation>
</item>
<item name='java.lang.Byte int intValue()'>
<annotation name='org.jetbrains.annotations.Range'>
<val name="from" val="java.lang.Byte.MIN_VALUE"/>
@@ -651,6 +666,11 @@
<item name="java.lang.Comparable int compareTo(T) 0">
<annotation name="org.jetbrains.annotations.NotNull" />
</item>
<item name='java.lang.Double Double(java.lang.String)'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
</annotation>
</item>
<item name='java.lang.Double double parseDouble(java.lang.String)'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
@@ -680,6 +700,11 @@
<item name='java.lang.Enum java.lang.String name()'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item name='java.lang.Float Float(java.lang.String)'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
</annotation>
</item>
<item name='java.lang.Float float parseFloat(java.lang.String)'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
@@ -688,6 +713,16 @@
<item name='java.lang.Float java.lang.Float valueOf(float)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item name='java.lang.Integer Integer(int)'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
</annotation>
</item>
<item name='java.lang.Integer Integer(java.lang.String)'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
</annotation>
</item>
<item name='java.lang.Integer int bitCount(int)'>
<annotation name='org.jetbrains.annotations.Range'>
<val name="from" val="0"/>
@@ -706,12 +741,22 @@
<val name="to" val="32"/>
</annotation>
</item>
<item name='java.lang.Integer int parseInt(java.lang.String)'>
<item name='java.lang.Integer int parseInt(java.lang.CharSequence, int, int, int)'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
</annotation>
</item>
<item name='java.lang.Integer int parseInt(java.lang.String)'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
</annotation>
</item>
<item name='java.lang.Integer java.lang.Integer decode(java.lang.String)'>
<item name='java.lang.Integer int parseInt(java.lang.String, int)'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
</annotation>
</item>
<item name='java.lang.Integer java.lang.Integer decode(java.lang.String)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
@@ -759,6 +804,11 @@
<item name='java.lang.Iterable java.util.Iterator&lt;T&gt; iterator()'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item name='java.lang.Long Long(java.lang.String)'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
</annotation>
</item>
<item name='java.lang.Long int bitCount(long)'>
<annotation name='org.jetbrains.annotations.Range'>
<val name="from" val="0"/>
@@ -807,11 +857,21 @@
<item name='java.lang.Long java.lang.Long valueOf(long)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item name='java.lang.Long long parseLong(java.lang.CharSequence, int, int, int)'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
</annotation>
</item>
<item name='java.lang.Long long parseLong(java.lang.String)'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
</annotation>
</item>
<item name='java.lang.Long long parseLong(java.lang.String, int)'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
</annotation>
</item>
<item name='java.lang.Math double IEEEremainder(double, double)'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
@@ -1058,6 +1118,11 @@
<item name="java.lang.RuntimeException RuntimeException(java.lang.String) 0">
<annotation name="org.jetbrains.annotations.NonNls" />
</item>
<item name='java.lang.Short Short(java.lang.String)'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
</annotation>
</item>
<item name='java.lang.Short int intValue()'>
<annotation name='org.jetbrains.annotations.Range'>
<val name="from" val="java.lang.Short.MIN_VALUE"/>
@@ -1099,6 +1164,11 @@
<val name="pure" val="true"/>
</annotation>
</item>
<item name='java.lang.Short short parseShort(java.lang.String, int)'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
</annotation>
</item>
<item name='java.lang.StackTraceElement java.lang.String getClassName()'>
<annotation name='org.jetbrains.annotations.NonNls'/>
<annotation name='org.jetbrains.annotations.NotNull'/>
@@ -1655,6 +1725,26 @@
<item name="java.lang.String void getChars(int, int, char[], int) 2">
<annotation name="org.jetbrains.annotations.NotNull"/>
</item>
<item name='java.lang.StringBuffer StringBuffer(java.lang.CharSequence)'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
</annotation>
</item>
<item name='java.lang.StringBuffer StringBuffer(java.lang.String)'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
</annotation>
</item>
<item name='java.lang.StringBuilder StringBuilder(java.lang.CharSequence)'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
</annotation>
</item>
<item name='java.lang.StringBuilder StringBuilder(java.lang.String)'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
</annotation>
</item>
<item name='java.lang.SuppressWarnings java.lang.String[] value()'>
<annotation name='org.jetbrains.annotations.NonNls'/>
</item>
@@ -2854,6 +2854,11 @@
<item name='java.util.Comparator java.util.Comparator&lt;T&gt; thenComparingLong(java.util.function.ToLongFunction&lt;? super T&gt;) 0'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item name='java.util.Date Date()'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
</annotation>
</item>
<item name='java.util.Date Date(int, int, int)'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
@@ -3278,6 +3283,26 @@
<val name="targetIsContainer" val="true"/>
</annotation>
</item>
<item name='java.util.Hashtable Hashtable()'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
</annotation>
</item>
<item name='java.util.Hashtable Hashtable(int)'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
</annotation>
</item>
<item name='java.util.Hashtable Hashtable(int, float)'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
</annotation>
</item>
<item name='java.util.Hashtable Hashtable(java.util.Map&lt;? extends K,? extends V&gt;)'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
</annotation>
</item>
<item name='java.util.Iterator E next()'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="mutates" val="&quot;this&quot;"/>
@@ -3288,6 +3313,46 @@
<val name="pure" val="true"/>
</annotation>
</item>
<item name='java.util.LinkedHashMap LinkedHashMap(int)'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
</annotation>
</item>
<item name='java.util.LinkedHashMap LinkedHashMap(int, float)'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
</annotation>
</item>
<item name='java.util.LinkedHashMap LinkedHashMap(int, float, boolean)'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
</annotation>
</item>
<item name='java.util.LinkedHashMap LinkedHashMap(java.util.Map&lt;? extends K,? extends V&gt;)'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
</annotation>
</item>
<item name='java.util.LinkedHashSet LinkedHashSet()'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
</annotation>
</item>
<item name='java.util.LinkedHashSet LinkedHashSet(int)'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
</annotation>
</item>
<item name='java.util.LinkedHashSet LinkedHashSet(int, float)'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
</annotation>
</item>
<item name='java.util.LinkedHashSet LinkedHashSet(java.util.Collection&lt;? extends E&gt;)'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
</annotation>
</item>
<item name='java.util.LinkedHashSet LinkedHashSet(java.util.Collection&lt;? extends E&gt;) 0'>
<annotation name='org.jetbrains.annotations.NotNull'/>
<annotation name='org.intellij.lang.annotations.Flow'>
@@ -3295,6 +3360,11 @@
<val name="targetIsContainer" val="true"/>
</annotation>
</item>
<item name='java.util.LinkedList LinkedList(java.util.Collection&lt;? extends E&gt;)'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
</annotation>
</item>
<item name='java.util.LinkedList LinkedList(java.util.Collection&lt;? extends E&gt;) 0'>
<annotation name='org.jetbrains.annotations.NotNull'/>
<annotation name='org.intellij.lang.annotations.Flow'>
@@ -4994,6 +5064,11 @@
<val name="to" val="java.lang.Long.MAX_VALUE"/>
</annotation>
</item>
<item name='java.util.Stack Stack()'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
</annotation>
</item>
<item name="java.util.TimeZone java.util.TimeZone getTimeZone(java.lang.String) 0">
<annotation name="org.jetbrains.annotations.NonNls"/>
</item>
@@ -5047,6 +5122,26 @@
<item name="java.util.UUID">
<annotation name="org.jetbrains.annotations.NonNls"/>
</item>
<item name='java.util.Vector Vector()'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
</annotation>
</item>
<item name='java.util.Vector Vector(int)'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
</annotation>
</item>
<item name='java.util.Vector Vector(int, int)'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
</annotation>
</item>
<item name='java.util.Vector Vector(java.util.Collection&lt;? extends E&gt;)'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
</annotation>
</item>
<item name='java.util.Vector Vector(java.util.Collection&lt;? extends E&gt;) 0'>
<annotation name='org.jetbrains.annotations.NotNull'/>
<annotation name='org.intellij.lang.annotations.Flow'>
@@ -5054,5 +5149,25 @@
<val name="targetIsContainer" val="true"/>
</annotation>
</item>
<item name='java.util.WeakHashMap WeakHashMap()'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
</annotation>
</item>
<item name='java.util.WeakHashMap WeakHashMap(int)'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
</annotation>
</item>
<item name='java.util.WeakHashMap WeakHashMap(int, float)'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
</annotation>
</item>
<item name='java.util.WeakHashMap WeakHashMap(java.util.Map&lt;? extends K,? extends V&gt;)'>
<annotation name='org.jetbrains.annotations.Contract'>
<val name="pure" val="true"/>
</annotation>
</item>
</root>