diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/testOnly/TestOnlyInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/testOnly/TestOnlyInspection.java index 47d2b2b2a550..a41470e02b56 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/testOnly/TestOnlyInspection.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/testOnly/TestOnlyInspection.java @@ -14,6 +14,7 @@ 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 com.intellij.util.ObjectUtils; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -54,8 +55,8 @@ public class TestOnlyInspection extends AbstractBaseJavaLocalInspectionTool { @Override public void visitNewExpression(PsiNewExpression expression) { PsiJavaCodeReferenceElement reference = expression.getClassOrAnonymousClassReference(); - if (reference != null) { - validate(reference, expression.resolveMethod(), h); + if (reference != null && validate(reference, expression.resolveMethod(), h)) { + validate(reference, ObjectUtils.tryCast(reference.resolve(), PsiMember.class), h); } } @@ -88,27 +89,33 @@ public class TestOnlyInspection extends AbstractBaseJavaLocalInspectionTool { }; } - private static void validate(@NotNull PsiElement place, @Nullable PsiMember member, ProblemsHolder h) { - if (member == null || !isAnnotatedAsTestOnly(member)) return; - if (isInsideTestOnlyMethod(place)) return; - if (isInsideTestOnlyField(place)) return; - if (isInsideTestClass(place)) return; - if (isUnderTestSources(place)) return; + private static boolean validate(@NotNull PsiElement place, @Nullable PsiMember member, ProblemsHolder h) { + if (member == null) { + return true; + } - PsiAnnotation anno = findVisibleForTestingAnnotation(member); - if (anno != null) { - String modifier = getAccessModifierWithoutTesting(anno); + PsiAnnotation vft = findVisibleForTestingAnnotation(member); + if (vft == null && !isAnnotatedAsTestOnly(member)) { + return true; + } + if (isInsideTestOnlyMethod(place) || isInsideTestOnlyField(place) || isInsideTestClass(place) || isUnderTestSources(place)) { + return true; + } + + if (vft != null) { + String modifier = getAccessModifierWithoutTesting(vft); if (modifier == null) { modifier = getNextLowerAccessLevel(member); } LightModifierList modList = new LightModifierList(member.getManager(), JavaLanguage.INSTANCE, modifier); if (JavaResolveUtil.isAccessible(member, member.getContainingClass(), modList, place, null, null)) { - return; + return true; } } reportProblem(place, member, h); + return false; } private static final List ourModifiersDescending = @@ -141,13 +148,7 @@ public class TestOnlyInspection extends AbstractBaseJavaLocalInspectionTool { @Nullable private static PsiAnnotation findVisibleForTestingAnnotation(@NotNull PsiMember member) { PsiAnnotation anno = AnnotationUtil.findAnnotation(member, "com.google.common.annotations.VisibleForTesting"); - if (anno == null) { - anno = AnnotationUtil.findAnnotation(member, "com.android.annotations.VisibleForTesting"); - } - if (anno != null) return anno; - - PsiClass containingClass = member.getContainingClass(); - return containingClass != null ? findVisibleForTestingAnnotation(containingClass) : null; + return anno != null ? anno : AnnotationUtil.findAnnotation(member, "com.android.annotations.VisibleForTesting"); } private static boolean isInsideTestOnlyMethod(PsiElement e) { @@ -161,7 +162,6 @@ public class TestOnlyInspection extends AbstractBaseJavaLocalInspectionTool { private static boolean isAnnotatedAsTestOnly(@Nullable PsiMember m) { if (m == null) return false; return AnnotationUtil.isAnnotated(m, AnnotationUtil.TEST_ONLY, CHECK_EXTERNAL) - || findVisibleForTestingAnnotation(m) != null || isAnnotatedAsTestOnly(m.getContainingClass()); } diff --git a/java/java-tests/testData/inspection/testOnly/visibleForTesting/expected.xml b/java/java-tests/testData/inspection/testOnly/visibleForTesting/expected.xml index 1e8c8cfbcd2b..9571fac9f6fc 100644 --- a/java/java-tests/testData/inspection/testOnly/visibleForTesting/expected.xml +++ b/java/java-tests/testData/inspection/testOnly/visibleForTesting/expected.xml @@ -18,7 +18,7 @@ B.java 8 - Test-only method is called in production code + Test-only class is referenced in production code AGrandChild.java diff --git a/java/java-tests/testData/inspection/testOnly/visibleForTesting/src/pkg1/InstantiatingPackageLocal.java b/java/java-tests/testData/inspection/testOnly/visibleForTesting/src/pkg1/InstantiatingPackageLocal.java new file mode 100644 index 000000000000..2ddfd55d256d --- /dev/null +++ b/java/java-tests/testData/inspection/testOnly/visibleForTesting/src/pkg1/InstantiatingPackageLocal.java @@ -0,0 +1,11 @@ +@com.google.common.annotations.VisibleForTesting +public class RelaxedForTesting { + RelaxedForTesting() { + } +} + +class Client { + { + new RelaxedForTesting(); + } +} \ No newline at end of file