method return always constants (IDEA-173896)

ensure external derivatives are not ignored, do not report on derivatives - report on api method, don't visit same methods multiple times
This commit is contained in:
Anna Kozlova
2017-06-05 14:34:17 +03:00
parent 92c3daa014
commit 5b962a018f
3 changed files with 38 additions and 34 deletions
@@ -1448,7 +1448,7 @@ integer.multiplication.implicit.cast.to.long.option=<html>Ignore compile time co
wait.or.await.without.timeout.display.name='wait()' or 'await()' without timeout
wait.or.await.without.timeout.problem.descriptor=<code>#ref</code> without timeout #loc
method.return.always.constant.display.name=Method returns per-class constant
method.return.always.constant.problem.descriptor=Method <code>#ref()</code> returns a per-class constant
method.return.always.constant.problem.descriptor=Method <code>#ref()</code> and all it's derivables always return constants
class.with.too.many.dependencies.display.name=Class with too many dependencies
class.with.too.many.dependencies.problem.descriptor=Class ''{0}'' has too many dependencies ({1} > {2})
class.with.too.many.transitive.dependencies.display.name=Class with too many transitive dependencies
@@ -17,9 +17,7 @@ package com.siyeh.ig.classlayout;
import com.intellij.analysis.AnalysisScope;
import com.intellij.codeInspection.*;
import com.intellij.codeInspection.reference.RefEntity;
import com.intellij.codeInspection.reference.RefMethod;
import com.intellij.openapi.util.Key;
import com.intellij.codeInspection.reference.*;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiUtil;
import com.siyeh.InspectionGadgetsBundle;
@@ -34,14 +32,10 @@ import java.util.Set;
public class MethodReturnAlwaysConstantInspection extends BaseGlobalInspection {
private static final Key<Boolean> ALWAYS_CONSTANT =
Key.create("ALWAYS_CONSTANT");
@NotNull
@Override
public String getDisplayName() {
return InspectionGadgetsBundle.message(
"method.return.always.constant.display.name");
return InspectionGadgetsBundle.message("method.return.always.constant.display.name");
}
@Override
@@ -51,35 +45,24 @@ public class MethodReturnAlwaysConstantInspection extends BaseGlobalInspection {
if (!(refEntity instanceof RefMethod)) {
return null;
}
final RefMethod refMethod = (RefMethod)refEntity;
final Boolean alreadyProcessed = refMethod.getUserData(ALWAYS_CONSTANT);
if (alreadyProcessed != null && alreadyProcessed.booleanValue()) {
//don't warn on overriders
if (((RefMethod)refEntity).hasSuperMethods()) {
return null;
}
final RefMethod refMethod = (RefMethod)refEntity;
if (!(refMethod.getElement() instanceof PsiMethod)) {
return null;
}
final PsiMethod method = (PsiMethod)refMethod.getElement();
if (method.getBody() == null) {
return null; //we'll catch it on another method
}
if (!alwaysReturnsConstant(method)) {
return null;
}
final Set<RefMethod> siblingMethods =
MethodInheritanceUtils.calculateSiblingMethods(refMethod);
for (RefMethod siblingMethod : siblingMethods) {
final PsiMethod siblingPsiMethod =
(PsiMethod)siblingMethod.getElement();
if (method.getBody() != null &&
!alwaysReturnsConstant(siblingPsiMethod)) {
final Set<RefMethod> allScopeInheritors = MethodInheritanceUtils.calculateSiblingMethods(refMethod);
for (RefMethod siblingMethod : allScopeInheritors) {
final PsiMethod siblingPsiMethod = (PsiMethod)siblingMethod.getElement();
if (siblingPsiMethod.getBody() != null && !alwaysReturnsConstant(siblingPsiMethod)) {
return null;
}
}
final List<ProblemDescriptor> out = new ArrayList<>();
for (RefMethod siblingRefMethod : siblingMethods) {
final PsiMethod siblingMethod =
(PsiMethod)siblingRefMethod.getElement();
for (RefMethod siblingRefMethod : allScopeInheritors) {
final PsiMethod siblingMethod = (PsiMethod)siblingRefMethod.getElement();
final PsiIdentifier identifier = siblingMethod.getNameIdentifier();
if (identifier == null) {
continue;
@@ -88,8 +71,6 @@ public class MethodReturnAlwaysConstantInspection extends BaseGlobalInspection {
InspectionGadgetsBundle.message(
"method.return.always.constant.problem.descriptor"), false, null,
ProblemHighlightType.GENERIC_ERROR_OR_WARNING));
siblingRefMethod.putUserData(ALWAYS_CONSTANT,
Boolean.valueOf(true));
}
return out.toArray(new ProblemDescriptor[out.size()]);
}
@@ -100,9 +81,32 @@ public class MethodReturnAlwaysConstantInspection extends BaseGlobalInspection {
if (!(statement instanceof PsiReturnStatement)) {
return false;
}
final PsiReturnStatement returnStatement =
(PsiReturnStatement)statement;
final PsiReturnStatement returnStatement = (PsiReturnStatement)statement;
final PsiExpression value = returnStatement.getReturnValue();
return value != null && PsiUtil.isConstantExpression(value);
}
@Override
protected boolean queryExternalUsagesRequests(@NotNull final RefManager manager, @NotNull final GlobalJavaInspectionContext globalContext,
@NotNull final ProblemDescriptionsProcessor processor) {
manager.iterate(new RefJavaVisitor() {
@Override public void visitElement(@NotNull RefEntity refEntity) {
if (refEntity instanceof RefElement && processor.getDescriptions(refEntity) != null) {
refEntity.accept(new RefJavaVisitor() {
@Override public void visitMethod(@NotNull final RefMethod refMethod) {
globalContext.enqueueDerivedMethodsProcessor(refMethod, new GlobalJavaInspectionContext.DerivedMethodsProcessor() {
@Override
public boolean process(PsiMethod derivedMethod) {
processor.ignoreElement(refMethod);
return false;
}
});
}
});
}
}
});
return false;
}
}
@@ -1,6 +1,6 @@
<html>
<body>
Reports methods which only ever return a constant.
Reports methods which only ever return a constant, probably different for different inheritors.
Because this inspection requires global code analysis it is only available for <em>Analyze|Inspect Code</em> or
<em>Analyze|Run Inspection by Name</em> and it will not report in the editor.
<!-- tooltip end -->