forked from WorksApplications/findbugs-plugin
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request WorksApplications#23 from KengoTODA/visible-for-te…
…sting Introducing 2 new detectors which checks annotation
- Loading branch information
Showing
16 changed files
with
358 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
57 changes: 57 additions & 0 deletions
57
...main/java/jp/co/worksap/oss/findbugs/findbugs/UndocumentedSuppressFBWarningsDetector.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
package jp.co.worksap.oss.findbugs.findbugs; | ||
|
||
import static com.google.common.base.Preconditions.checkNotNull; | ||
|
||
import java.util.Collections; | ||
import java.util.Map; | ||
import java.util.Set; | ||
|
||
import javax.annotation.Nonnull; | ||
|
||
import org.apache.bcel.classfile.ElementValue; | ||
|
||
import com.google.common.collect.Sets; | ||
|
||
import edu.umd.cs.findbugs.BugInstance; | ||
import edu.umd.cs.findbugs.BugReporter; | ||
import edu.umd.cs.findbugs.BytecodeScanningDetector; | ||
import edu.umd.cs.findbugs.internalAnnotations.DottedClassName; | ||
|
||
/** | ||
* <p>A detector to ensure that FindBugs' SuppressWarnings annotation has justification.</p> | ||
* @see edu.umd.cs.findbugs.annotations.SuppressWarnings | ||
* @see edu.umd.cs.findbugs.annotations.SuppressFBWarnings | ||
* @author Kengo TODA ([email protected]) | ||
*/ | ||
public class UndocumentedSuppressFBWarningsDetector extends BytecodeScanningDetector { | ||
private static final Set<String> TARGET_ANNOTATIONS = Collections.unmodifiableSet(Sets.newHashSet( | ||
"edu.umd.cs.findbugs.annotations.SuppressWarnings", | ||
"edu.umd.cs.findbugs.annotations.SuppressFBWarnings" | ||
)); | ||
|
||
@Nonnull | ||
private final BugReporter bugReporter; | ||
|
||
public UndocumentedSuppressFBWarningsDetector(BugReporter bugReporter) { | ||
this.bugReporter = checkNotNull(bugReporter); | ||
} | ||
|
||
@Override | ||
public void visitAnnotation(@DottedClassName String annotationClass, | ||
Map<String, ElementValue> map, boolean runtimeVisible) { | ||
if (! TARGET_ANNOTATIONS.contains(annotationClass)) { | ||
return; | ||
} | ||
|
||
final ElementValue reason = map.get("justification"); | ||
if (reason == null || reason.stringifyValue().trim().isEmpty()) { | ||
BugInstance bugInstance = new BugInstance("FINDBUGS_UNDOCUMENTED_SUPPRESS_WARNINGS", | ||
HIGH_PRIORITY).addClass(this); | ||
if (visitingMethod()) { | ||
bugInstance.addMethod(this).addSourceLine(this); | ||
} | ||
bugReporter.reportBug(bugInstance); | ||
} | ||
} | ||
|
||
} |
110 changes: 110 additions & 0 deletions
110
src/main/java/jp/co/worksap/oss/findbugs/guava/UnexpectedAccessDetector.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
package jp.co.worksap.oss.findbugs.guava; | ||
|
||
import static com.google.common.base.Preconditions.checkNotNull; | ||
|
||
import javax.annotation.Nonnull; | ||
|
||
import org.apache.bcel.Repository; | ||
import org.apache.bcel.classfile.AnnotationEntry; | ||
import org.apache.bcel.classfile.JavaClass; | ||
import org.apache.bcel.classfile.Method; | ||
|
||
import edu.umd.cs.findbugs.BugInstance; | ||
import edu.umd.cs.findbugs.BugReporter; | ||
import edu.umd.cs.findbugs.BytecodeScanningDetector; | ||
import edu.umd.cs.findbugs.bcel.BCELUtil; | ||
import edu.umd.cs.findbugs.classfile.ClassDescriptor; | ||
import edu.umd.cs.findbugs.classfile.MethodDescriptor; | ||
|
||
/** | ||
* <p>A detector to ensure that implementation (class in src/main/java) doesn't call | ||
* package-private method in other class which is annotated by {@code @VisibleForTesting}.</p> | ||
* | ||
* @author Kengo TODA ([email protected]) | ||
* @see com.google.common.annotations.VisibleForTesting | ||
*/ | ||
public class UnexpectedAccessDetector extends BytecodeScanningDetector { | ||
@Nonnull | ||
private final BugReporter bugReporter; | ||
|
||
public UnexpectedAccessDetector(BugReporter bugReporter) { | ||
this.bugReporter = checkNotNull(bugReporter); | ||
} | ||
|
||
@Override | ||
public void sawOpcode(int opcode) { | ||
if (! isInvoking(opcode)) { | ||
return; | ||
} | ||
|
||
ClassDescriptor currentClass = getClassDescriptor(); | ||
ClassDescriptor invokedClass = getClassDescriptorOperand(); | ||
if (currentClass.equals(invokedClass)) { | ||
// no need to check, because method is called by owner | ||
} else if (! currentClass.getPackageName().equals(invokedClass.getPackageName())) { | ||
// no need to check, because method is called by class in other package | ||
} else { | ||
MethodDescriptor invokedMethod = getMethodDescriptorOperand(); | ||
|
||
try { | ||
verifyVisibility(invokedClass, invokedMethod); | ||
} catch (ClassNotFoundException e) { | ||
String message = String.format("Detector could not find %s, you should add this class into CLASSPATH", invokedClass.getDottedClassName()); | ||
bugReporter.logError(message, e); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* <p>Report if specified method is package-private and annotated by {@code @VisibleForTesting}.</p> | ||
*/ | ||
private void verifyVisibility(ClassDescriptor invokedClass, MethodDescriptor invokedMethod) throws ClassNotFoundException { | ||
JavaClass bcelClass = Repository.getRepository().loadClass(invokedClass.getDottedClassName()); | ||
Method bcelMethod = findMethod(bcelClass, invokedMethod); | ||
|
||
if (checkVisibility(bcelMethod) && checkAnnotated(bcelMethod)) { | ||
BugInstance bug = new BugInstance(this, "GUAVA_UNEXPECTED_ACCESS_TO_VISIBLE_FOR_TESTING", HIGH_PRIORITY) | ||
.addCalledMethod(this).addClassAndMethod(this).addSourceLine(this); | ||
bugReporter.reportBug(bug); | ||
} | ||
} | ||
|
||
private Method findMethod(JavaClass bcelClass, MethodDescriptor invokedMethod) { | ||
for (Method bcelMethod : bcelClass.getMethods()) { | ||
MethodDescriptor methodDescriptor = BCELUtil.getMethodDescriptor(bcelClass, bcelMethod); | ||
if (methodDescriptor.equals(invokedMethod)) { | ||
return bcelMethod; | ||
} | ||
} | ||
|
||
throw new IllegalArgumentException("Method not found: " + invokedMethod); | ||
} | ||
|
||
/** | ||
* @return true if visibility of specified method is package-private. | ||
*/ | ||
private boolean checkVisibility(Method bcelMethod) { | ||
return ! (bcelMethod.isPrivate() || bcelMethod.isProtected() || bcelMethod.isPublic()); | ||
} | ||
|
||
/** | ||
* @return true if specified method is annotated by {@code VisibleForTesting}. | ||
*/ | ||
private boolean checkAnnotated(Method bcelMethod) { | ||
for (AnnotationEntry annotation : bcelMethod.getAnnotationEntries()) { | ||
String type = annotation.getAnnotationType(); | ||
if ("Lcom/google/common/annotations/VisibleForTesting;".equals(type)) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
private boolean isInvoking(int opcode) { | ||
return opcode == INVOKESPECIAL || | ||
opcode == INVOKEINTERFACE || | ||
opcode == INVOKESTATIC || | ||
opcode == INVOKEVIRTUAL; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
9 changes: 9 additions & 0 deletions
9
src/test/java/jp/co/worksap/oss/findbugs/findbugs/DocumentedSuppressFBWarnings.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package jp.co.worksap.oss.findbugs.findbugs; | ||
|
||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; | ||
|
||
public class DocumentedSuppressFBWarnings { | ||
@SuppressFBWarnings(justification = "only for unit test.") | ||
public void method() { | ||
} | ||
} |
8 changes: 8 additions & 0 deletions
8
src/test/java/jp/co/worksap/oss/findbugs/findbugs/DocumentedSuppressWarnings.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package jp.co.worksap.oss.findbugs.findbugs; | ||
|
||
import edu.umd.cs.findbugs.annotations.SuppressWarnings; | ||
|
||
@SuppressWarnings(justification = "only for unit test.") | ||
public class DocumentedSuppressWarnings { | ||
|
||
} |
9 changes: 9 additions & 0 deletions
9
src/test/java/jp/co/worksap/oss/findbugs/findbugs/UndocumentedSuppressFBWarnings.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package jp.co.worksap.oss.findbugs.findbugs; | ||
|
||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; | ||
|
||
public class UndocumentedSuppressFBWarnings { | ||
@SuppressFBWarnings | ||
public void method() { | ||
} | ||
} |
33 changes: 33 additions & 0 deletions
33
.../java/jp/co/worksap/oss/findbugs/findbugs/UndocumentedSuppressFBWarningsDetectorTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package jp.co.worksap.oss.findbugs.findbugs; | ||
|
||
import static com.youdevise.fbplugins.tdd4fb.DetectorAssert.*; | ||
|
||
import org.junit.Before; | ||
import org.junit.Test; | ||
|
||
import edu.umd.cs.findbugs.BugReporter; | ||
|
||
public class UndocumentedSuppressFBWarningsDetectorTest { | ||
|
||
private UndocumentedSuppressFBWarningsDetector detector; | ||
private BugReporter bugReporter; | ||
|
||
@Before | ||
public void setup() { | ||
bugReporter = bugReporterForTesting(); | ||
detector = new UndocumentedSuppressFBWarningsDetector(bugReporter); | ||
} | ||
|
||
@Test | ||
public void testDocumentedClasses() throws Exception { | ||
assertNoBugsReported(DocumentedSuppressWarnings.class, detector, bugReporter); | ||
assertNoBugsReported(DocumentedSuppressFBWarnings.class, detector, bugReporter); | ||
} | ||
|
||
@Test | ||
public void testUndocumentedClasses() throws Exception { | ||
assertBugReported(UndocumentedSuppressWarnings.class, detector, bugReporter, ofType("FINDBUGS_UNDOCUMENTED_SUPPRESS_WARNINGS")); | ||
assertBugReported(UndocumentedSuppressFBWarnings.class, detector, bugReporter, ofType("FINDBUGS_UNDOCUMENTED_SUPPRESS_WARNINGS")); | ||
} | ||
|
||
} |
8 changes: 8 additions & 0 deletions
8
src/test/java/jp/co/worksap/oss/findbugs/findbugs/UndocumentedSuppressWarnings.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package jp.co.worksap.oss.findbugs.findbugs; | ||
|
||
import edu.umd.cs.findbugs.annotations.SuppressWarnings; | ||
|
||
@SuppressWarnings | ||
public class UndocumentedSuppressWarnings { | ||
|
||
} |
12 changes: 12 additions & 0 deletions
12
src/test/java/jp/co/worksap/oss/findbugs/guava/ClassWhichCallsNormalMethod.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package jp.co.worksap.oss.findbugs.guava; | ||
|
||
public class ClassWhichCallsNormalMethod { | ||
public void method() { | ||
new MethodWithoutVisibleForTesting().method(); | ||
} | ||
|
||
public void anotherMethod() { | ||
System.out.println("this method invoking has no problem, because package isn't same."); | ||
method(); | ||
} | ||
} |
7 changes: 7 additions & 0 deletions
7
src/test/java/jp/co/worksap/oss/findbugs/guava/ClassWhichCallsVisibleMethodForTesting.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package jp.co.worksap.oss.findbugs.guava; | ||
|
||
public class ClassWhichCallsVisibleMethodForTesting { | ||
public void method() { | ||
new MethodWithVisibleForTesting().method(); | ||
} | ||
} |
8 changes: 8 additions & 0 deletions
8
src/test/java/jp/co/worksap/oss/findbugs/guava/MethodWithVisibleForTesting.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package jp.co.worksap.oss.findbugs.guava; | ||
|
||
import com.google.common.annotations.VisibleForTesting; | ||
|
||
public class MethodWithVisibleForTesting { | ||
@VisibleForTesting | ||
void method() {} | ||
} |
5 changes: 5 additions & 0 deletions
5
src/test/java/jp/co/worksap/oss/findbugs/guava/MethodWithoutVisibleForTesting.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package jp.co.worksap.oss.findbugs.guava; | ||
|
||
public class MethodWithoutVisibleForTesting { | ||
void method() {} | ||
} |
34 changes: 34 additions & 0 deletions
34
src/test/java/jp/co/worksap/oss/findbugs/guava/UnexpectedAccessDetectorTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package jp.co.worksap.oss.findbugs.guava; | ||
|
||
import static com.youdevise.fbplugins.tdd4fb.DetectorAssert.assertBugReported; | ||
import static com.youdevise.fbplugins.tdd4fb.DetectorAssert.assertNoBugsReported; | ||
import static com.youdevise.fbplugins.tdd4fb.DetectorAssert.bugReporterForTesting; | ||
import static com.youdevise.fbplugins.tdd4fb.DetectorAssert.ofType; | ||
|
||
import org.junit.Before; | ||
import org.junit.Test; | ||
|
||
import edu.umd.cs.findbugs.BugReporter; | ||
|
||
public class UnexpectedAccessDetectorTest { | ||
|
||
private UnexpectedAccessDetector detector; | ||
private BugReporter bugReporter; | ||
|
||
@Before | ||
public void setup() { | ||
bugReporter = bugReporterForTesting(); | ||
detector = new UnexpectedAccessDetector(bugReporter); | ||
} | ||
|
||
@Test | ||
public void testNormalMethod() throws Exception { | ||
assertNoBugsReported(ClassWhichCallsNormalMethod.class, detector, bugReporter); | ||
} | ||
|
||
@Test | ||
public void testCallingAnnotatedMethod() throws Exception { | ||
assertBugReported(ClassWhichCallsVisibleMethodForTesting.class, detector, bugReporter, ofType("GUAVA_UNEXPECTED_ACCESS_TO_VISIBLE_FOR_TESTING")); | ||
} | ||
|
||
} |