IG: report enum singletons in "Singleton" inspection

This commit is contained in:
Bas Leijdekkers
2017-11-28 19:26:51 +01:00
parent 896b1f9976
commit 4c88396bf2
4 changed files with 37 additions and 10 deletions
@@ -916,6 +916,7 @@ constant.declared.in.interface.problem.descriptor=Constant <code>#ref</code> dec
static.inheritance.problem.descriptor=Interface <code>#ref</code> is implemented only for its 'static' constants #loc
utility.class.problem.descriptor=Class <code>#ref</code> has only 'static' members, indicating procedural construction #loc
singleton.problem.descriptor=Class <code>#ref</code> is a singleton #loc
enum.singleton.problem.descriptor=Enum <code>#ref</code> is a singleton #loc
final.private.method.problem.descriptor='private' method declared <code>#ref</code> #loc
noop.method.in.abstract.class.problem.descriptor=No-op Method <code>#ref()</code> should be made abstract #loc
final.static.method.problem.descriptor='static' method declared <code>#ref</code> #loc
@@ -1,5 +1,5 @@
/*
* Copyright 2003-2010 Dave Griffith, Bas Leijdekkers
* Copyright 2003-2017 Dave Griffith, Bas Leijdekkers
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -33,7 +33,9 @@ public class SingletonInspection extends BaseInspection {
@Override
@NotNull
protected String buildErrorString(Object... infos) {
return InspectionGadgetsBundle.message("singleton.problem.descriptor");
return (Boolean)infos[0] ?
InspectionGadgetsBundle.message("enum.singleton.problem.descriptor") :
InspectionGadgetsBundle.message("singleton.problem.descriptor");
}
@Override
@@ -50,11 +52,10 @@ public class SingletonInspection extends BaseInspection {
@Override
public void visitClass(@NotNull PsiClass aClass) {
// no call to super, so that it doesn't drill down to inner classes
if (!SingletonUtil.isSingleton(aClass)) {
return;
}
registerClassError(aClass);
registerClassError(aClass, aClass.isEnum());
}
}
}
@@ -33,18 +33,26 @@ public class SingletonUtil {
private SingletonUtil() {}
public static boolean isSingleton(@Nullable PsiClass aClass) {
if (aClass == null || aClass.isInterface() || aClass.isEnum() || aClass.isAnnotationType()) {
if (aClass == null ||
aClass.isInterface() ||
aClass.isAnnotationType() ||
aClass instanceof PsiTypeParameter ||
aClass instanceof PsiAnonymousClass) {
return false;
}
if (aClass instanceof PsiTypeParameter || aClass instanceof PsiAnonymousClass) {
return false;
if (aClass.isEnum()) {
if (!ControlFlowUtils.hasChildrenOfTypeCount(aClass, 1, PsiEnumConstant.class)) {
return false;
}
// has at least on accessible instance method
return Arrays.stream(aClass.getMethods())
.anyMatch(m -> !m.isConstructor() && !m.hasModifierProperty(PsiModifier.PRIVATE) && !m.hasModifierProperty(PsiModifier.STATIC));
}
final PsiMethod[] constructors = getIfOnlyInvisibleConstructors(aClass);
if (constructors.length == 0) {
if (getIfOnlyInvisibleConstructors(aClass).length == 0) {
return false;
}
final PsiField selfInstance = getIfOneStaticSelfInstance(aClass);
return selfInstance != null && newOnlyAssignsToStaticSelfInstance(constructors[0], selfInstance);
return selfInstance != null && newOnlyAssignsToStaticSelfInstance(getIfOnlyInvisibleConstructors(aClass)[0], selfInstance);
}
private static PsiField getIfOneStaticSelfInstance(PsiClass aClass) {
@@ -49,4 +49,21 @@ class <warning descr="Class 'Singleton2' is a singleton">Singleton2</warning> {
private static class SingletonHolder {
private static final Singleton2 INSTANCE = new Singleton2();
}
}
enum No1 {
;
void f() {}
}
enum No2 {
ONE, TWO;
void f() {}
}
enum No3 {
INSTANCE
}
enum <warning descr="Enum 'Yes' is a singleton">Yes</warning> {
INSTANCE;
void f() {}
}