mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-107730@VisibleForTesting is marked as a warning when called within class
This commit is contained in:
+33
-6
@@ -18,9 +18,12 @@ package com.intellij.codeInspection.testOnly;
|
||||
import com.intellij.codeInsight.AnnotationUtil;
|
||||
import com.intellij.codeInsight.TestFrameworks;
|
||||
import com.intellij.codeInspection.*;
|
||||
import com.intellij.lang.java.JavaLanguage;
|
||||
import com.intellij.openapi.roots.ProjectRootManager;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.light.LightModifierList;
|
||||
import com.intellij.psi.impl.source.resolve.JavaResolveUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -55,16 +58,42 @@ public class TestOnlyInspection extends BaseJavaBatchLocalInspectionTool {
|
||||
}
|
||||
|
||||
private static void validate(PsiCallExpression e, ProblemsHolder h) {
|
||||
if (!isTestOnlyMethodCalled(e)) return;
|
||||
PsiMethod method = e.resolveMethod();
|
||||
|
||||
if (method == null || !isAnnotatedAsTestOnly(method)) return;
|
||||
if (isInsideTestOnlyMethod(e)) return;
|
||||
if (isInsideTestClass(e)) return;
|
||||
if (isUnderTestSources(e)) return;
|
||||
|
||||
PsiAnnotation anno = findVisibleForTestingAnnotation(method);
|
||||
if (anno != null) {
|
||||
LightModifierList modList = new LightModifierList(method.getManager(), JavaLanguage.INSTANCE, getAccessModifierWithoutTesting(anno));
|
||||
if (JavaResolveUtil.isAccessible(method, method.getContainingClass(), modList, e, null, null)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
reportProblem(e, h);
|
||||
}
|
||||
|
||||
private static boolean isTestOnlyMethodCalled(PsiCallExpression e) {
|
||||
return isAnnotatedAsTestOnly(e.resolveMethod());
|
||||
private static String getAccessModifierWithoutTesting(PsiAnnotation anno) {
|
||||
String modifier = PsiModifier.PRIVATE;
|
||||
PsiAnnotationMemberValue ref = anno.findDeclaredAttributeValue("visibility");
|
||||
if (ref instanceof PsiReferenceExpression) {
|
||||
PsiElement target = ((PsiReferenceExpression)ref).resolve();
|
||||
if (target instanceof PsiEnumConstant) {
|
||||
String name = ((PsiEnumConstant)target).getName();
|
||||
modifier = "PRIVATE".equals(name) ? PsiModifier.PRIVATE : "PROTECTED".equals(name) ? PsiModifier.PROTECTED : PsiModifier.PACKAGE_LOCAL;
|
||||
}
|
||||
}
|
||||
return modifier;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiAnnotation findVisibleForTestingAnnotation(@NotNull PsiMethod method) {
|
||||
PsiModifierList modifierList = method.getModifierList();
|
||||
PsiAnnotation anno = modifierList.findAnnotation("com.google.common.annotations.VisibleForTesting");
|
||||
return anno != null ? anno : modifierList.findAnnotation("com.android.annotations.VisibleForTesting");
|
||||
}
|
||||
|
||||
private static boolean isInsideTestOnlyMethod(PsiCallExpression e) {
|
||||
@@ -73,9 +102,7 @@ public class TestOnlyInspection extends BaseJavaBatchLocalInspectionTool {
|
||||
}
|
||||
|
||||
private static boolean isAnnotatedAsTestOnly(@Nullable PsiMethod m) {
|
||||
return m != null &&
|
||||
(AnnotationUtil.isAnnotated(m, AnnotationUtil.TEST_ONLY, false, false) ||
|
||||
AnnotationUtil.isAnnotated(m, "com.google.common.annotations.VisibleForTesting", false, false));
|
||||
return m != null && (AnnotationUtil.isAnnotated(m, AnnotationUtil.TEST_ONLY, false, false) || findVisibleForTestingAnnotation(m) != null);
|
||||
}
|
||||
|
||||
private static boolean isInsideTestClass(PsiCallExpression e) {
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<problems>
|
||||
<problem>
|
||||
<file>B.java</file>
|
||||
<line>3</line>
|
||||
<description>Test-only method is called in production code</description>
|
||||
</problem>
|
||||
</problems>
|
||||
@@ -0,0 +1,16 @@
|
||||
public class A {
|
||||
void publicMethod() {
|
||||
invisibleMethod(1);
|
||||
visibleMethod(1);
|
||||
}
|
||||
|
||||
@com.google.common.annotations.VisibleForTesting()
|
||||
void invisibleMethod(int a) {
|
||||
|
||||
}
|
||||
|
||||
@com.google.common.annotations.VisibleForTesting(visibility=com.google.common.annotations.VisibleForTesting.Visibility.PROTECTED)
|
||||
void visibleMethod(int a) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
public class B {
|
||||
void publicMethod() {
|
||||
new A().invisibleMethod(2);
|
||||
new A().visibleMethod(2);
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package com.google.common.annotations;
|
||||
|
||||
public @interface VisibleForTesting {
|
||||
enum Visibility {
|
||||
/** The element should be considered protected. */
|
||||
PROTECTED,
|
||||
/** The element should be considered package-private. */
|
||||
PACKAGE,
|
||||
/** The element should be considered private. */
|
||||
PRIVATE
|
||||
}
|
||||
|
||||
Visibility visibility() default Visibility.PRIVATE;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
public class Test {
|
||||
void publicMethod() {
|
||||
new A().invisibleMethod(3);
|
||||
new A().visibleMethod(3);
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,8 @@ public class TestOnlyInspectionTest extends InspectionTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testVisibleForTesting() throws Exception { doTest(); }
|
||||
|
||||
public void testUnresolved() throws Exception {
|
||||
doTest(); // shouldn't throw
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user