[java-inspections] Fix getName handling for local/nested/anonymous; support getCanonicalName

Fixes IDEA-248063 Incorrect ConstantConditions inspection warnings when calling getClass().getName() in an anonymous inner class

GitOrigin-RevId: 1773e6c086aa9938c6f3b8cad395f383e2a767e4
This commit is contained in:
Tagir Valeev
2020-08-18 12:33:00 +07:00
committed by intellij-monorepo-bot
parent a878b7bebf
commit 4c1ddc1016
2 changed files with 45 additions and 2 deletions

View File

@@ -14,6 +14,8 @@ import com.intellij.codeInspection.util.OptionalUtil;
import com.intellij.psi.*;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.util.CachedValuesManager;
import com.intellij.psi.util.ClassUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.ReflectionUtil;
import com.siyeh.ig.callMatcher.CallMapper;
@@ -117,7 +119,7 @@ final class CustomMethodHandlers {
(args, memState, factory, method) -> enumName(args.myQualifier, memState, method.getReturnType()))
.register(staticCall(JAVA_UTIL_COLLECTIONS, "emptyList", "emptySet", "emptyMap").parameterCount(0),
(args, memState, factory, method) -> getEmptyCollectionConstant(method))
.register(exactInstanceCall(JAVA_LANG_CLASS, "getName", "getSimpleName").parameterCount(0),
.register(exactInstanceCall(JAVA_LANG_CLASS, "getName", "getSimpleName", "getCanonicalName").parameterCount(0),
(args, memState, factory, method) -> className(memState, args.myQualifier, method.getName(), method.getReturnType()))
.register(anyOf(
staticCall(JAVA_UTIL_COLLECTIONS, "singleton", "singletonList", "singletonMap"),
@@ -383,7 +385,22 @@ final class CustomMethodHandlers {
if (type != null) {
PsiClass psiClass = type.resolve();
if (psiClass != null) {
return constant(name.equals("getSimpleName") ? psiClass.getName() : psiClass.getQualifiedName(), stringType);
String result;
switch (name) {
case "getSimpleName":
result = psiClass instanceof PsiAnonymousClass ? "" : psiClass.getName();
break;
case "getName":
if (PsiUtil.isLocalOrAnonymousClass(psiClass)) {
return TOP;
}
result = ClassUtil.getJVMClassName(psiClass);
break;
default:
result = psiClass.getQualifiedName();
break;
}
return constant(result, stringType);
}
}
return TOP;

View File

@@ -101,4 +101,30 @@ class GetClass {
}
}
}
void testAnonymous() {
Runnable r = new Runnable() {
public void run() {
if (<warning descr="Condition 'getClass().getSimpleName().isEmpty()' is always 'true'">getClass().getSimpleName().isEmpty()</warning>) {}
if (getClass().getName().isEmpty()) {}
if (getClass().getCanonicalName().<warning descr="Method invocation 'isEmpty' will produce 'NullPointerException'">isEmpty</warning>()) {}
}
};
}
static final class X {}
void testNested(X x) {
if (<warning descr="Condition 'x.getClass().getSimpleName().equals(\"X\")' is always 'true'">x.getClass().getSimpleName().equals("X")</warning>) {}
if (<warning descr="Condition 'x.getClass().getName().equals(\"GetClass$X\")' is always 'true'">x.getClass().getName().equals("GetClass$X")</warning>) {}
if (<warning descr="Condition 'x.getClass().getCanonicalName().equals(\"GetClass.X\")' is always 'true'">x.getClass().getCanonicalName().equals("GetClass.X")</warning>) {}
}
void testLocal() {
class X {}
X x = new X();
if (<warning descr="Condition 'x.getClass().getSimpleName().equals(\"X\")' is always 'true'">x.getClass().getSimpleName().equals("X")</warning>) {}
if (x.getClass().getName().equals("GetClass$X")) {}
if (x.getClass().getCanonicalName().<warning descr="Method invocation 'equals' will produce 'NullPointerException'">equals</warning>("GetClass.X")) {}
}
}