fix and clarify "Cyclic class dependency" inspection warning

This commit is contained in:
Bas Leijdekkers
2013-12-23 20:42:17 +01:00
parent 6bd22b4074
commit 68b7ce46db
6 changed files with 141 additions and 16 deletions
@@ -1510,6 +1510,8 @@ class.with.too.many.transitive.dependencies.max.option=Maximum number of transit
class.with.too.many.transitive.dependents.max.option=Maximum number of transitive dependents
cyclic.class.dependency.display.name=Cyclic class dependency
cyclic.class.dependency.problem.descriptor=Class ''{0}'' is cyclically dependent on {1} other classes
cyclic.class.dependency.1.problem.descriptor=Class ''{0}'' is cyclically dependent on class ''{1}''
cyclic.class.dependency.2.problem.descriptor=Class ''{0}'' is cyclically dependent on classes ''{1}'' and ''{2}''
cyclic.package.dependency.display.name=Cyclic package dependency
cyclic.package.dependency.problem.descriptor=Package ''{0}'' is cyclically dependent on {1} other packages
class.unconnected.to.package.display.name=Class independent of its package
@@ -16,17 +16,19 @@
package com.siyeh.ig.dependency;
import com.intellij.analysis.AnalysisScope;
import com.intellij.codeInspection.CommonProblemDescriptor;
import com.intellij.codeInspection.GlobalInspectionContext;
import com.intellij.codeInspection.InspectionManager;
import com.intellij.codeInspection.*;
import com.intellij.codeInspection.reference.RefClass;
import com.intellij.codeInspection.reference.RefEntity;
import com.intellij.codeInspection.util.RefEntityAlphabeticalComparator;
import com.intellij.psi.PsiAnonymousClass;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.siyeh.InspectionGadgetsBundle;
import com.siyeh.ig.BaseGlobalInspection;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
@@ -35,8 +37,7 @@ public class CyclicClassDependencyInspection extends BaseGlobalInspection {
@NotNull
@Override
public String getDisplayName() {
return InspectionGadgetsBundle.message(
"cyclic.class.dependency.display.name");
return InspectionGadgetsBundle.message("cyclic.class.dependency.display.name");
}
@Override
@@ -54,22 +55,42 @@ public class CyclicClassDependencyInspection extends BaseGlobalInspection {
if (aClass == null || aClass.getContainingClass() != null) {
return null;
}
final Set<RefClass> dependencies =
DependencyUtils.calculateTransitiveDependenciesForClass(refClass);
final Set<RefClass> dependents =
DependencyUtils.calculateTransitiveDependentsForClass(refClass);
final Set<RefClass> mutualDependents =
new HashSet<RefClass>(dependencies);
final Set<RefClass> dependencies = DependencyUtils.calculateTransitiveDependenciesForClass(refClass);
final Set<RefClass> dependents = DependencyUtils.calculateTransitiveDependentsForClass(refClass);
final Set<RefClass> mutualDependents = new HashSet<RefClass>(dependencies);
mutualDependents.retainAll(dependents);
final int numMutualDependents = mutualDependents.size();
if (numMutualDependents <= 1) {
if (numMutualDependents == 0) {
return null;
}
final String errorString = InspectionGadgetsBundle.message(
"cyclic.class.dependency.problem.descriptor",
refEntity.getName(), Integer.valueOf(numMutualDependents - 1));
final String errorString;
if (numMutualDependents == 1) {
final RefClass[] classes = mutualDependents.toArray(new RefClass[1]);
errorString = InspectionGadgetsBundle.message("cyclic.class.dependency.1.problem.descriptor",
refEntity.getName(), classes[0].getExternalName());
}
else if (numMutualDependents == 2) {
final RefClass[] classes = mutualDependents.toArray(new RefClass[2]);
Arrays.sort(classes, RefEntityAlphabeticalComparator.getInstance());
errorString = InspectionGadgetsBundle.message("cyclic.class.dependency.2.problem.descriptor",
refEntity.getName(), classes[0].getExternalName(), classes[1].getExternalName());
}
else {
errorString = InspectionGadgetsBundle.message("cyclic.class.dependency.problem.descriptor",
refEntity.getName(), Integer.valueOf(numMutualDependents));
}
final PsiElement anchor;
if (aClass instanceof PsiAnonymousClass) {
final PsiAnonymousClass anonymousClass = (PsiAnonymousClass)aClass;
anchor = anonymousClass.getBaseClassReference();
}
else {
anchor = aClass.getNameIdentifier();
if (anchor == null) return null;
}
return new CommonProblemDescriptor[]{
inspectionManager.createProblemDescriptor(errorString)
inspectionManager.createProblemDescriptor(anchor, errorString, (LocalQuickFix)null,
ProblemHighlightType.GENERIC_ERROR_OR_WARNING, false)
};
}
}
@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<problems>
<problem>
<file>Cyclic.java</file>
<line>9</line>
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Cyclic class dependency</problem_class>
<description>Class 'anonymous (java.lang.Object)' is cyclically dependent on 3 other classes</description>
</problem>
<problem>
<file>Cyclic.java</file>
<line>17</line>
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Cyclic class dependency</problem_class>
<description>Class 'Base' is cyclically dependent on classes 'com.siyeh.igtest.abstraction.cyclic_class_dependency.src.Cyclic' and 'com.siyeh.igtest.abstraction.cyclic_class_dependency.src.Top'</description>
</problem>
<problem>
<file>Cyclic.java</file>
<line>29</line>
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Cyclic class dependency</problem_class>
<description>Class 'Coffee' is cyclically dependent on class 'com.siyeh.igtest.abstraction.cyclic_class_dependency.src.FiveOClock'</description>
</problem>
<problem>
<file>Cyclic.java</file>
<line>25</line>
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Cyclic class dependency</problem_class>
<description>Class 'FiveOClock' is cyclically dependent on class 'com.siyeh.igtest.abstraction.cyclic_class_dependency.src.Coffee'</description>
</problem>
<problem>
<file>Cyclic.java</file>
<line>22</line>
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Cyclic class dependency</problem_class>
<description>Class 'Top' is cyclically dependent on classes 'com.siyeh.igtest.abstraction.cyclic_class_dependency.src.Base' and 'com.siyeh.igtest.abstraction.cyclic_class_dependency.src.Cyclic'</description>
</problem>
<problem>
<file>Cyclic.java</file>
<line>6</line>
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Cyclic class dependency</problem_class>
<description>Class 'Cyclic' is cyclically dependent on classes 'com.siyeh.igtest.abstraction.cyclic_class_dependency.src.Base' and 'com.siyeh.igtest.abstraction.cyclic_class_dependency.src.Top'</description>
</problem>
</problems>
@@ -0,0 +1,29 @@
package com.siyeh.igtest.abstraction.cyclic_class_dependency.src;
/**
* @author Bas Leijdekkers
*/
public class Cyclic extends Base {
Cyclic() {
new Object() {{
foo();
}};
}
void foo() {}
}
class Base {
void a() {
Top.m();
}
}
class Top extends Cyclic {
public static void m() {}
}
interface FiveOClock {
void m(Coffee c);
}
interface Coffee extends FiveOClock {}
@@ -0,0 +1,29 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.siyeh.ig.abstraction;
import com.siyeh.ig.IGInspectionTestCase;
import com.siyeh.ig.dependency.CyclicClassDependencyInspection;
/**
* @author Bas Leijdekkers
*/
public class CyclicClassDependencyInspectionTest extends IGInspectionTestCase {
public void test() {
doTest("com/siyeh/igtest/abstraction/cyclic_class_dependency", new CyclicClassDependencyInspection());
}
}