From 6edd0b8035fca4f8b60b2c1d016717bda796891d Mon Sep 17 00:00:00 2001 From: eller86 Date: Wed, 21 Aug 2013 20:50:12 +0800 Subject: [PATCH 1/9] implement detector to check implicit elements. --- .../findbugs/jpa/AbstractColumnDetector.java | 68 +++++++++++++++++++ .../findbugs/jpa/ImplicitLengthDetector.java | 38 +++++++++++ .../jpa/ImplicitNullnessDetector.java | 28 ++++++++ .../findbugs/jpa/LongColumnNameDetector.java | 15 +--- .../oss/findbugs/jpa/VisitedFieldFinder.java | 20 +++++- .../oss/findbugs/jpa/ColumnWithLength.java | 12 ++++ .../oss/findbugs/jpa/ColumnWithNullable.java | 12 ++++ .../findbugs/jpa/ColumnWithoutElement.java | 12 ++++ .../oss/findbugs/jpa/ImplicitLengthTest.java | 33 +++++++++ .../findbugs/jpa/ImplicitNullnessTest.java | 33 +++++++++ 10 files changed, 256 insertions(+), 15 deletions(-) create mode 100644 src/main/java/jp/co/worksap/oss/findbugs/jpa/AbstractColumnDetector.java create mode 100644 src/main/java/jp/co/worksap/oss/findbugs/jpa/ImplicitLengthDetector.java create mode 100644 src/main/java/jp/co/worksap/oss/findbugs/jpa/ImplicitNullnessDetector.java create mode 100644 src/test/java/jp/co/worksap/oss/findbugs/jpa/ColumnWithLength.java create mode 100644 src/test/java/jp/co/worksap/oss/findbugs/jpa/ColumnWithNullable.java create mode 100644 src/test/java/jp/co/worksap/oss/findbugs/jpa/ColumnWithoutElement.java create mode 100644 src/test/java/jp/co/worksap/oss/findbugs/jpa/ImplicitLengthTest.java create mode 100644 src/test/java/jp/co/worksap/oss/findbugs/jpa/ImplicitNullnessTest.java diff --git a/src/main/java/jp/co/worksap/oss/findbugs/jpa/AbstractColumnDetector.java b/src/main/java/jp/co/worksap/oss/findbugs/jpa/AbstractColumnDetector.java new file mode 100644 index 0000000..2e799d1 --- /dev/null +++ b/src/main/java/jp/co/worksap/oss/findbugs/jpa/AbstractColumnDetector.java @@ -0,0 +1,68 @@ +package jp.co.worksap.oss.findbugs.jpa; + +import java.util.Map; + +import javax.annotation.CheckReturnValue; +import javax.annotation.Nonnull; + +import org.apache.bcel.classfile.ElementValue; +import org.apache.bcel.classfile.Field; +import org.apache.bcel.generic.Type; + +import com.google.common.base.Objects; + +import edu.umd.cs.findbugs.BugReporter; +import edu.umd.cs.findbugs.bcel.AnnotationDetector; +import edu.umd.cs.findbugs.internalAnnotations.DottedClassName; + +abstract class AbstractColumnDetector extends AnnotationDetector { + private final BugReporter bugReporter; + + AbstractColumnDetector(BugReporter bugReporter) { + this.bugReporter = bugReporter; + } + + @Nonnull + @CheckReturnValue + protected final BugReporter getBugReporter() { + return bugReporter; + } + + @Override + public final void visitAnnotation(@DottedClassName String annotationClass, + Map map, boolean runtimeVisible) { + if (!Objects.equal(annotationClass, "javax.persistence.Column")) { + return; + } + + Type columnType; + if (visitingField()) { + columnType = getField().getType(); + } else if (visitingMethod()) { + Field visitingField = findFieldInVisitingMethod(); + columnType = visitingField.getType(); + } else { + throw new IllegalStateException("@Column should annotate field or method."); + } + verifyColumn(columnType, map); + } + + @Nonnull + private Field findFieldInVisitingMethod() { + String fieldName = VisitedFieldFinder.findFieldWhichisVisitedInVisitingMethod(this); + Field visitingField = null; + for (Field field : getThisClass().getFields()) { + if (Objects.equal(field.getName(), fieldName)) { + visitingField = field; + break; + } + } + if (visitingField == null) { + throw new IllegalStateException("Cannot find field which named as " + fieldName + "."); + } + return visitingField; + } + + protected abstract void verifyColumn(Type columnType, Map elements); + +} diff --git a/src/main/java/jp/co/worksap/oss/findbugs/jpa/ImplicitLengthDetector.java b/src/main/java/jp/co/worksap/oss/findbugs/jpa/ImplicitLengthDetector.java new file mode 100644 index 0000000..f04fc36 --- /dev/null +++ b/src/main/java/jp/co/worksap/oss/findbugs/jpa/ImplicitLengthDetector.java @@ -0,0 +1,38 @@ +package jp.co.worksap.oss.findbugs.jpa; + +import java.util.Map; + +import org.apache.bcel.classfile.ElementValue; +import org.apache.bcel.generic.Type; + +import edu.umd.cs.findbugs.BugInstance; +import edu.umd.cs.findbugs.BugReporter; + +public class ImplicitLengthDetector extends AbstractColumnDetector { + + public ImplicitLengthDetector(BugReporter bugReporter) { + super(bugReporter); + } + + @Override + protected void verifyColumn(Type columnType, + Map elements) { + if (verifyColumnType(columnType) && !elements.containsKey("length")) { + BugInstance bug = new BugInstance(this, "IMPLICIT_LENGTH", HIGH_PRIORITY); + if (visitingMethod()) { + bug.addMethod(this); + } + getBugReporter().reportBug(bug); + } + } + + /** + * @param columnType + * @return true if column type requires length property. + */ + private boolean verifyColumnType(Type columnType) { + // TODO true if target is annotated with @Lob + return Type.STRING.equals(columnType); + } + +} diff --git a/src/main/java/jp/co/worksap/oss/findbugs/jpa/ImplicitNullnessDetector.java b/src/main/java/jp/co/worksap/oss/findbugs/jpa/ImplicitNullnessDetector.java new file mode 100644 index 0000000..c702ff7 --- /dev/null +++ b/src/main/java/jp/co/worksap/oss/findbugs/jpa/ImplicitNullnessDetector.java @@ -0,0 +1,28 @@ +package jp.co.worksap.oss.findbugs.jpa; + +import java.util.Map; + +import org.apache.bcel.classfile.ElementValue; +import org.apache.bcel.generic.Type; + +import edu.umd.cs.findbugs.BugInstance; +import edu.umd.cs.findbugs.BugReporter; + +public class ImplicitNullnessDetector extends AbstractColumnDetector { + + public ImplicitNullnessDetector(BugReporter bugReporter) { + super(bugReporter); + } + + @Override + protected void verifyColumn(Type columnType, + Map elements) { + if (! elements.containsKey("nullable")) { + BugInstance bug = new BugInstance(this, "IMPLICIT_NULLNESS", HIGH_PRIORITY); + if (visitingMethod()) { + bug.addMethod(this); + } + getBugReporter().reportBug(bug); + } + } +} diff --git a/src/main/java/jp/co/worksap/oss/findbugs/jpa/LongColumnNameDetector.java b/src/main/java/jp/co/worksap/oss/findbugs/jpa/LongColumnNameDetector.java index 98c631d..b2672f1 100755 --- a/src/main/java/jp/co/worksap/oss/findbugs/jpa/LongColumnNameDetector.java +++ b/src/main/java/jp/co/worksap/oss/findbugs/jpa/LongColumnNameDetector.java @@ -5,7 +5,6 @@ import org.apache.bcel.classfile.ElementValue; import org.apache.bcel.classfile.Method; import org.apache.commons.lang.IllegalClassException; -import org.objectweb.asm.ClassReader; import com.google.common.base.Objects; @@ -49,7 +48,7 @@ public void visitAnnotation(@DottedClassName String annotationClass, columnName = getFieldName(); } else if (visitingMethod()) { Method targetMethod = getMethod(); - columnName = findFieldWhichisVisitedIn(targetMethod); + columnName = VisitedFieldFinder.findFieldWhichisVisitedInVisitingMethod(this); if (columnName == null) { throw new IllegalClassException(String.format( "Method which is annotated with @Column should access to field, but %s#%s does not access.", @@ -62,18 +61,6 @@ public void visitAnnotation(@DottedClassName String annotationClass, detectLongName(columnName); } - private String findFieldWhichisVisitedIn(Method targetMethod) { - byte[] classByteCode = getClassContext().getJavaClass().getBytes(); - ClassReader reader = new ClassReader(classByteCode); - - // note: bcel's #getSignature() method returns String like "(J)V", this is named as "descriptor" in the context of ASM. - // This is the reason why we call `targetMethod.getSignature()` to get value for `targetMethodDescriptor` argument. - VisitedFieldFinder visitedFieldFinder = new VisitedFieldFinder(targetMethod.getName(), targetMethod.getSignature()); - - reader.accept(visitedFieldFinder, 0); - return visitedFieldFinder.getVisitedFieldName(); - } - private void detectLongName(String tableName) { if (tableName.length() > MAX_TABLE_LENGTH) { bugReporter.reportBug(new BugInstance(this, "LONG_COLUMN_NAME", diff --git a/src/main/java/jp/co/worksap/oss/findbugs/jpa/VisitedFieldFinder.java b/src/main/java/jp/co/worksap/oss/findbugs/jpa/VisitedFieldFinder.java index 607af8d..fbff640 100644 --- a/src/main/java/jp/co/worksap/oss/findbugs/jpa/VisitedFieldFinder.java +++ b/src/main/java/jp/co/worksap/oss/findbugs/jpa/VisitedFieldFinder.java @@ -6,9 +6,13 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.apache.bcel.classfile.FieldOrMethod; +import org.objectweb.asm.ClassReader; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.commons.EmptyVisitor; +import edu.umd.cs.findbugs.bcel.AnnotationDetector; + /** *

Simple ClassVisitor implementation to find visited field in the specified method.

*

To create instance, you need to provide name and descriptor to specify the target method.

@@ -28,7 +32,7 @@ public VisitedFieldFinder(@Nonnull String targetMethodName, @Nonnull String targ @CheckReturnValue @Nullable - String getVisitedFieldName() { + private String getVisitedFieldName() { return visitedFieldName; } @@ -48,4 +52,18 @@ public void visitFieldInsn(int code, String owner, String name, String descripti this.visitedFieldName = name; } + @Nullable + @CheckReturnValue + static String findFieldWhichisVisitedInVisitingMethod(AnnotationDetector detector) { + byte[] classByteCode = detector.getClassContext().getJavaClass().getBytes(); + ClassReader reader = new ClassReader(classByteCode); + + FieldOrMethod targetMethod = detector.getMethod(); + // note: bcel's #getSignature() method returns String like "(J)V", this is named as "descriptor" in the context of ASM. + // This is the reason why we call `targetMethod.getSignature()` to get value for `targetMethodDescriptor` argument. + VisitedFieldFinder visitedFieldFinder = new VisitedFieldFinder(targetMethod .getName(), targetMethod.getSignature()); + + reader.accept(visitedFieldFinder, 0); + return visitedFieldFinder.getVisitedFieldName(); + } } diff --git a/src/test/java/jp/co/worksap/oss/findbugs/jpa/ColumnWithLength.java b/src/test/java/jp/co/worksap/oss/findbugs/jpa/ColumnWithLength.java new file mode 100644 index 0000000..938a83f --- /dev/null +++ b/src/test/java/jp/co/worksap/oss/findbugs/jpa/ColumnWithLength.java @@ -0,0 +1,12 @@ +package jp.co.worksap.oss.findbugs.jpa; + +import javax.persistence.Column; + +public class ColumnWithLength { + @Column(length = 100) + private String name; + + public String getName() { + return name; + } +} diff --git a/src/test/java/jp/co/worksap/oss/findbugs/jpa/ColumnWithNullable.java b/src/test/java/jp/co/worksap/oss/findbugs/jpa/ColumnWithNullable.java new file mode 100644 index 0000000..9c7d88f --- /dev/null +++ b/src/test/java/jp/co/worksap/oss/findbugs/jpa/ColumnWithNullable.java @@ -0,0 +1,12 @@ +package jp.co.worksap.oss.findbugs.jpa; + +import javax.persistence.Column; + +public class ColumnWithNullable { + @Column(nullable = false) + private String name; + + public String getName() { + return name; + } +} diff --git a/src/test/java/jp/co/worksap/oss/findbugs/jpa/ColumnWithoutElement.java b/src/test/java/jp/co/worksap/oss/findbugs/jpa/ColumnWithoutElement.java new file mode 100644 index 0000000..075d361 --- /dev/null +++ b/src/test/java/jp/co/worksap/oss/findbugs/jpa/ColumnWithoutElement.java @@ -0,0 +1,12 @@ +package jp.co.worksap.oss.findbugs.jpa; + +import javax.persistence.Column; + +public class ColumnWithoutElement { + @Column + private String name; + + public String getName() { + return name; + } +} diff --git a/src/test/java/jp/co/worksap/oss/findbugs/jpa/ImplicitLengthTest.java b/src/test/java/jp/co/worksap/oss/findbugs/jpa/ImplicitLengthTest.java new file mode 100644 index 0000000..228b61c --- /dev/null +++ b/src/test/java/jp/co/worksap/oss/findbugs/jpa/ImplicitLengthTest.java @@ -0,0 +1,33 @@ +package jp.co.worksap.oss.findbugs.jpa; + +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 org.junit.Before; +import org.junit.Test; + +import edu.umd.cs.findbugs.BugReporter; + +public class ImplicitLengthTest { + private BugReporter bugReporter; + private ImplicitLengthDetector detector; + + @Before + public void before() { + this.bugReporter = bugReporterForTesting(); + this.detector = new ImplicitLengthDetector(bugReporter); + } + + @Test + public void testImplicitLength() throws Exception { + assertNoBugsReported(ColumnWithLength.class, detector, + bugReporter); + } + + @Test + public void testExplicitLength() throws Exception { + assertBugReported(ColumnWithoutElement.class, detector, + bugReporter); + } +} diff --git a/src/test/java/jp/co/worksap/oss/findbugs/jpa/ImplicitNullnessTest.java b/src/test/java/jp/co/worksap/oss/findbugs/jpa/ImplicitNullnessTest.java new file mode 100644 index 0000000..0888847 --- /dev/null +++ b/src/test/java/jp/co/worksap/oss/findbugs/jpa/ImplicitNullnessTest.java @@ -0,0 +1,33 @@ +package jp.co.worksap.oss.findbugs.jpa; + +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 org.junit.Before; +import org.junit.Test; + +import edu.umd.cs.findbugs.BugReporter; + +public class ImplicitNullnessTest { + private BugReporter bugReporter; + private ImplicitNullnessDetector detector; + + @Before + public void before() { + this.bugReporter = bugReporterForTesting(); + this.detector = new ImplicitNullnessDetector(bugReporter); + } + + @Test + public void testImplicitNullness() throws Exception { + assertNoBugsReported(ColumnWithNullable.class, detector, + bugReporter); + } + + @Test + public void testExplicitNullness() throws Exception { + assertBugReported(ColumnWithoutElement.class, detector, + bugReporter); + } +} From a09bab417e52db5e163f8e84e54a398603062481 Mon Sep 17 00:00:00 2001 From: eller86 Date: Wed, 21 Aug 2013 21:13:07 +0800 Subject: [PATCH 2/9] update metadata and README. --- README.md | 2 ++ src/main/meta/bugrank.txt | 4 +++- src/main/meta/findbugs.xml | 10 ++++++++++ src/main/meta/messages.xml | 39 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 54 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c767d3b..0614cec 100755 --- a/README.md +++ b/README.md @@ -34,6 +34,8 @@ To use this product, please configure your findbugs-maven-plugin like below. - added LongColumnNameDetector - added UnknownNullnessDetector - added UndocumentedIgnoreDetector +- added ImplicitLengthDetector +- added ImplicitNullnessDetector ## 0.0.1 diff --git a/src/main/meta/bugrank.txt b/src/main/meta/bugrank.txt index c47fcfb..102c40e 100755 --- a/src/main/meta/bugrank.txt +++ b/src/main/meta/bugrank.txt @@ -6,4 +6,6 @@ 0 BugPattern LONG_COLUMN_NAME 0 BugPattern UNKNOWN_NULLNESS_OF_PARAMETER 0 BugPattern UNKNOWN_NULLNESS_OF_RETURNED_VALUE -0 BugPattern UNDOCUMENTED_IGNORE \ No newline at end of file +0 BugPattern UNDOCUMENTED_IGNORE +0 BugPattern IMPLICIT_NULLNESS +0 BugPattern IMPLICIT_LENGTH diff --git a/src/main/meta/findbugs.xml b/src/main/meta/findbugs.xml index 8a352e6..4deb29f 100755 --- a/src/main/meta/findbugs.xml +++ b/src/main/meta/findbugs.xml @@ -37,6 +37,16 @@ +