|
| 1 | +/** |
| 2 | + * Finds code which uses elements that are only intended to be used by tests. |
| 3 | + * |
| 4 | + * Since these elements are not considered part of the public API, relying on them |
| 5 | + * should be avoided because they could be removed in future versions or their behavior |
| 6 | + * could change without any announcement. |
| 7 | + * |
| 8 | + * @kind problem |
| 9 | + */ |
| 10 | + |
| 11 | +import java |
| 12 | + |
| 13 | +class TestOnlyAnnotation extends Annotation { |
| 14 | + TestOnlyAnnotation() { |
| 15 | + getType().hasName([ |
| 16 | + // JetBrains annotations |
| 17 | + "TestOnly", |
| 18 | + ]) |
| 19 | + } |
| 20 | +} |
| 21 | + |
| 22 | +from Annotatable elementForTesting, Expr usage |
| 23 | +where |
| 24 | + ( |
| 25 | + elementForTesting.getAnAnnotation() instanceof TestOnlyAnnotation |
| 26 | + or |
| 27 | + elementForTesting.getAnAnnotation().getType().hasName([ |
| 28 | + // Android, Guava and JetBrains annotations |
| 29 | + "VisibleForTesting", |
| 30 | + ]) |
| 31 | + // To reduce false positives only consider elements which are public for testing and |
| 32 | + // are used from other package |
| 33 | + and elementForTesting.getCompilationUnit().getPackage() != usage.getCompilationUnit().getPackage() |
| 34 | + ) |
| 35 | + and ( |
| 36 | + elementForTesting.(Field).getAnAccess() = usage |
| 37 | + or elementForTesting = usage.(Call).getCallee().getSourceDeclaration() |
| 38 | + or elementForTesting = usage.(TypeAccess).getType().(RefType).getSourceDeclaration() |
| 39 | + ) |
| 40 | + // Ignore if access is from test code |
| 41 | + and not ( |
| 42 | + usage.getEnclosingCallable().getDeclaringType() instanceof TestClass |
| 43 | + or usage.getFile().getAbsolutePath().matches("%/test/%") |
| 44 | + ) |
| 45 | + // Ignore if caller is itself also only intended for testing |
| 46 | + and not usage.getEnclosingCallable().getAnAnnotation() instanceof TestOnlyAnnotation |
| 47 | +select usage, "Uses element '$@' which is only visible for testing", elementForTesting, elementForTesting.getName() |
0 commit comments