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 17d77286d140..9ce9470d3b1b 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
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2009 JetBrains s.r.o.
+ * Copyright 2000-2015 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.
@@ -17,7 +17,10 @@ package com.intellij.codeInspection.testOnly;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInsight.TestFrameworks;
-import com.intellij.codeInspection.*;
+import com.intellij.codeInspection.BaseJavaBatchLocalInspectionTool;
+import com.intellij.codeInspection.InspectionsBundle;
+import com.intellij.codeInspection.ProblemHighlightType;
+import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.vfs.VirtualFile;
@@ -52,35 +55,51 @@ public class TestOnlyInspection extends BaseJavaBatchLocalInspectionTool {
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder h, boolean isOnTheFly) {
return new JavaElementVisitor() {
@Override public void visitCallExpression(PsiCallExpression e) {
- validate(e, h);
+ validate(e, e.resolveMethod(), h);
+ }
+
+ @Override
+ public void visitReferenceExpression(PsiReferenceExpression reference) {
+ PsiElement resolve = reference.resolve();
+ if (resolve instanceof PsiField) {
+ validate(reference, ((PsiField)resolve), h);
+ }
+ }
+
+ @Override
+ public void visitReferenceElement(PsiJavaCodeReferenceElement reference) {
+ if (reference.getParent() instanceof PsiNewExpression
+ || PsiTreeUtil.getParentOfType(reference, PsiImportStatementBase.class) != null) {
+ return;
+ }
+ PsiElement resolve = reference.resolve();
+ if (resolve instanceof PsiClass) validate(reference, ((PsiClass)resolve), h);
}
};
}
- private static void validate(PsiCallExpression e, ProblemsHolder h) {
- PsiMethod method = e.resolveMethod();
+ private static void validate(@NotNull PsiElement reference, @Nullable PsiMember member, ProblemsHolder h) {
+ if (member == null || !isAnnotatedAsTestOnly(member)) return;
+ if (isInsideTestOnlyMethod(reference)) return;
+ if (isInsideTestClass(reference)) return;
+ if (isUnderTestSources(reference)) return;
- if (method == null || !isAnnotatedAsTestOnly(method)) return;
- if (isInsideTestOnlyMethod(e)) return;
- if (isInsideTestClass(e)) return;
- if (isUnderTestSources(e)) return;
-
- PsiAnnotation anno = findVisibleForTestingAnnotation(method);
+ PsiAnnotation anno = findVisibleForTestingAnnotation(member);
if (anno != null) {
String modifier = getAccessModifierWithoutTesting(anno);
if (modifier == null) {
- modifier = method.hasModifierProperty(PsiModifier.PUBLIC) ? PsiModifier.PROTECTED :
- method.hasModifierProperty(PsiModifier.PROTECTED) ? PsiModifier.PACKAGE_LOCAL :
+ modifier = member.hasModifierProperty(PsiModifier.PUBLIC) ? PsiModifier.PROTECTED :
+ member.hasModifierProperty(PsiModifier.PROTECTED) ? PsiModifier.PACKAGE_LOCAL :
PsiModifier.PRIVATE;
}
- LightModifierList modList = new LightModifierList(method.getManager(), JavaLanguage.INSTANCE, modifier);
- if (JavaResolveUtil.isAccessible(method, method.getContainingClass(), modList, e, null, null)) {
+ LightModifierList modList = new LightModifierList(member.getManager(), JavaLanguage.INSTANCE, modifier);
+ if (JavaResolveUtil.isAccessible(member, member.getContainingClass(), modList, reference, null, null)) {
return;
}
}
- reportProblem(e, h);
+ reportProblem(reference, member, h);
}
@Nullable
@@ -97,21 +116,24 @@ public class TestOnlyInspection extends BaseJavaBatchLocalInspectionTool {
}
@Nullable
- private static PsiAnnotation findVisibleForTestingAnnotation(@NotNull PsiMethod method) {
- PsiAnnotation anno = AnnotationUtil.findAnnotation(method, "com.google.common.annotations.VisibleForTesting");
- return anno != null ? anno : AnnotationUtil.findAnnotation(method, "com.android.annotations.VisibleForTesting");
+ private static PsiAnnotation findVisibleForTestingAnnotation(@NotNull PsiMember member) {
+ PsiAnnotation anno = AnnotationUtil.findAnnotation(member, "com.google.common.annotations.VisibleForTesting");
+ return anno != null ? anno : AnnotationUtil.findAnnotation(member, "com.android.annotations.VisibleForTesting");
}
- private static boolean isInsideTestOnlyMethod(PsiCallExpression e) {
+ private static boolean isInsideTestOnlyMethod(PsiElement e) {
PsiMethod m = getTopLevelParentOfType(e, PsiMethod.class);
return isAnnotatedAsTestOnly(m);
}
- private static boolean isAnnotatedAsTestOnly(@Nullable PsiMethod m) {
- return m != null && (AnnotationUtil.isAnnotated(m, AnnotationUtil.TEST_ONLY, false, false) || findVisibleForTestingAnnotation(m) != null);
+ private static boolean isAnnotatedAsTestOnly(@Nullable PsiMember m) {
+ if (m == null) return false;
+ return AnnotationUtil.isAnnotated(m, AnnotationUtil.TEST_ONLY, false, false)
+ || findVisibleForTestingAnnotation(m) != null
+ || isAnnotatedAsTestOnly(m.getContainingClass());
}
- private static boolean isInsideTestClass(PsiCallExpression e) {
+ private static boolean isInsideTestClass(PsiElement e) {
PsiClass c = getTopLevelParentOfType(e, PsiClass.class);
return c != null && TestFrameworks.getInstance().isTestClass(c);
}
@@ -128,14 +150,16 @@ public class TestOnlyInspection extends BaseJavaBatchLocalInspectionTool {
while (true);
}
- private static boolean isUnderTestSources(PsiCallExpression e) {
+ private static boolean isUnderTestSources(PsiElement e) {
ProjectRootManager rm = ProjectRootManager.getInstance(e.getProject());
VirtualFile f = e.getContainingFile().getVirtualFile();
return f != null && rm.getFileIndex().isInTestSourceContent(f);
}
- private static void reportProblem(PsiCallExpression e, ProblemsHolder h) {
- String message = InspectionsBundle.message("inspection.test.only.problems.test.only.method.call");
+ private static void reportProblem(PsiElement e, PsiMember target, ProblemsHolder h) {
+ String message = target instanceof PsiClass
+ ? InspectionsBundle.message("inspection.test.only.problems.test.only.class.reference")
+ : InspectionsBundle.message("inspection.test.only.problems.test.only.method.call");
h.registerProblem(e, message, ProblemHighlightType.GENERIC_ERROR_OR_WARNING);
}
}
diff --git a/java/java-tests/testData/inspection/testOnly/class/expected.xml b/java/java-tests/testData/inspection/testOnly/class/expected.xml
new file mode 100644
index 000000000000..2302c74cf00f
--- /dev/null
+++ b/java/java-tests/testData/inspection/testOnly/class/expected.xml
@@ -0,0 +1,73 @@
+
+
+
+ B.java
+ 10
+ <default>
+ Test-only class is referenced in production code
+
+
+ B.java
+ 10
+ <default>
+ Test-only class is referenced in production code
+
+
+ B.java
+ 11
+ <default>
+ Test-only method is called in production code
+
+
+
+ B.java
+ 13
+ <default>
+ Test-only class is referenced in production code
+
+
+ B.java
+ 13
+ <default>
+ Test-only method is called in production code
+
+
+
+ B.java
+ 14
+ <default>
+ Test-only method is called in production code
+
+
+ B.java
+ 15
+ <default>
+ Test-only method is called in production code
+
+
+ B.java
+ 16
+ <default>
+ Test-only method is called in production code
+
+
+
+ B.java
+ 18
+ <default>
+ Test-only method is called in production code
+
+
+ B.java
+ 19
+ <default>
+ Test-only method is called in production code
+
+
+ B.java
+ 20
+ <default>
+ Test-only method is called in production code
+
+
+
diff --git a/java/java-tests/testData/inspection/testOnly/class/src/B.java b/java/java-tests/testData/inspection/testOnly/class/src/B.java
new file mode 100644
index 000000000000..515121d003cd
--- /dev/null
+++ b/java/java-tests/testData/inspection/testOnly/class/src/B.java
@@ -0,0 +1,22 @@
+import packageA.A;
+
+import java.util.ArrayList;
+
+import static packageA.A.staticImportedField;
+import static packageA.A.staticImportedMethod;
+
+public class B {
+ public void foo() {
+ Iterable iterable = new ArrayList();
+ System.out.println(iterable.iterator().next().field);
+
+ A a = new A();
+ a.method();
+ A.staticMethod();
+ staticImportedMethod();
+
+ System.out.println(a.field);
+ System.out.println(A.staticField);
+ System.out.println(staticImportedField);
+ }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/inspection/testOnly/class/src/packageA/A.java b/java/java-tests/testData/inspection/testOnly/class/src/packageA/A.java
new file mode 100644
index 000000000000..78282591851d
--- /dev/null
+++ b/java/java-tests/testData/inspection/testOnly/class/src/packageA/A.java
@@ -0,0 +1,14 @@
+package packageA;
+
+@org.jetbrains.annotations.TestOnly
+public class A {
+ public String field;
+ public static String staticField;
+ public static String staticImportedField;
+
+ public A() {}
+
+ public void method() {}
+ public static void staticMethod() {}
+ public static void staticImportedMethod() {}
+}
\ No newline at end of file
diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/TestOnlyInspectionTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/TestOnlyInspectionTest.java
index e368d0e73e14..07d17637f218 100644
--- a/java/java-tests/testSrc/com/intellij/codeInspection/TestOnlyInspectionTest.java
+++ b/java/java-tests/testSrc/com/intellij/codeInspection/TestOnlyInspectionTest.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright 2000-2015 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.intellij.codeInspection;
import com.intellij.analysis.AnalysisScope;
@@ -41,6 +56,10 @@ public class TestOnlyInspectionTest extends InspectionTestCase {
public void testUnresolved() throws Exception {
doTest(); // shouldn't throw
}
+
+ public void testClass() throws Exception {
+ doTest();
+ }
private void doTest() throws Exception {
TestOnlyInspection i = new TestOnlyInspection();
diff --git a/platform/platform-resources-en/src/messages/InspectionsBundle.properties b/platform/platform-resources-en/src/messages/InspectionsBundle.properties
index 40e563729a0e..9242f80b2e93 100644
--- a/platform/platform-resources-en/src/messages/InspectionsBundle.properties
+++ b/platform/platform-resources-en/src/messages/InspectionsBundle.properties
@@ -154,8 +154,9 @@ inspection.nullable.problems.NotNull.parameter.overrides.not.annotated=Parameter
inspection.nullable.problems.parameter.overrides.NotNull=Not annotated parameter overrides @{0} parameter
inspection.nullable.problems.primitive.type.annotation=Primitive type members cannot be annotated
-inspection.test.only.problems.display.name=Test-only method call in production code
+inspection.test.only.problems.display.name=Test-only class or method call in production code
inspection.test.only.problems.test.only.method.call=Test-only method is called in production code
+inspection.test.only.problems.test.only.class.reference=Test-only class is referenced in production code
inspection.visibility.display.name=Declaration access can be weaker
inspection.visibility.option=Suggest package local visibility level for class members
diff --git a/resources-en/src/inspectionDescriptions/TestOnlyProblems.html b/resources-en/src/inspectionDescriptions/TestOnlyProblems.html
index 4410aef61075..2703c37964c0 100644
--- a/resources-en/src/inspectionDescriptions/TestOnlyProblems.html
+++ b/resources-en/src/inspectionDescriptions/TestOnlyProblems.html
@@ -2,8 +2,8 @@
This inspection reports usages of @TestOnly - or VisibleForTesting
-annotated methods in production code.
-
The problems are not reported if such method is called from
+annotated methods and classes in production code.
+
The problems are not reported if such method or class is referenced from
- code under 'Test Sources' folder
- test-class (JUnit/TestNG)