├── .arcconfig ├── .arclint ├── .gitflowconfig ├── .gitignore ├── .travis.yml ├── README.md ├── bump-version ├── findbugs-exclude.xml ├── pom.xml └── src ├── main ├── java │ ├── com │ │ └── monits │ │ │ └── findbugs │ │ │ ├── effectivejava │ │ │ ├── EqualsOverrideDetector.java │ │ │ └── ToStringDetector.java │ │ │ └── jdk │ │ │ ├── InconsistentHashCodeEqualsDetector.java │ │ │ ├── NonStaticPatternCompileDetector.java │ │ │ └── UselessStringValueOfCallDetector.java │ └── jp │ │ └── co │ │ └── worksap │ │ └── oss │ │ └── findbugs │ │ ├── ForbiddenSystemClass.java │ │ ├── findbugs │ │ └── UndocumentedSuppressFBWarningsDetector.java │ │ ├── guava │ │ └── UnexpectedAccessDetector.java │ │ ├── jpa │ │ ├── AbstractColumnDetector.java │ │ ├── ColumnDefinitionDetector.java │ │ ├── ImplicitLengthDetector.java │ │ ├── ImplicitNullnessDetector.java │ │ ├── LongColumnNameDetector.java │ │ ├── LongIndexNameDetector.java │ │ ├── LongTableNameDetector.java │ │ ├── NullablePrimitiveDetector.java │ │ └── VisitedFieldFinder.java │ │ ├── jsr305 │ │ ├── BrokenImmutableClassDetector.java │ │ └── nullness │ │ │ ├── GenericsData.java │ │ │ └── UnknownNullnessDetector.java │ │ └── junit │ │ └── UndocumentedIgnoreDetector.java └── resources │ └── metadata │ ├── META-INF │ └── MANIFEST.MF │ ├── bugrank.txt │ ├── findbugs.xml │ └── messages.xml └── test ├── java ├── com │ └── monits │ │ └── findbugs │ │ ├── effectivejava │ │ ├── EqualsOverrideDetectorTest.java │ │ └── ToStringDetectorTest.java │ │ └── jdk │ │ ├── InconsistentHashCodeEqualsDetectorTest.java │ │ ├── PatternCompileTest.java │ │ └── UselessStringValueOfTest.java ├── jp │ └── co │ │ └── worksap │ │ └── oss │ │ └── findbugs │ │ ├── ForbiddenSystemDetectorTest.java │ │ ├── findbugs │ │ └── UndocumentedSuppressFBWarningsDetectorTest.java │ │ ├── guava │ │ └── UnexpectedAccessDetectorTest.java │ │ ├── jpa │ │ ├── ColumnDefinitionTest.java │ │ ├── ColumnNameLengthTest.java │ │ ├── ImplicitLengthTest.java │ │ ├── ImplicitNullnessTest.java │ │ ├── IndexNameLengthTest.java │ │ ├── NullablePrimitiveDetectorTest.java │ │ └── TableNameLengthTest.java │ │ ├── jsr305 │ │ ├── BrokenImmutableClassDetectorTest.java │ │ └── nullness │ │ │ └── UnknownNullnessDetectorTest.java │ │ └── junit │ │ └── UndocumentedIgnoreDetectorTest.java └── samples │ ├── effectivejava │ ├── item10 │ │ ├── ArrayMissingToStringClass.java │ │ ├── BadEnumCompositeClass.java │ │ ├── BadToStringCompositeInterestingClass.java │ │ ├── EnumWithState.java │ │ ├── FakeCompleteToStringClass.java │ │ ├── GoodMissingArrayToStringClass.java │ │ ├── GoodSuppressedToStringComposite.java │ │ ├── GoodToStringAllIgnoredFieldClass.java │ │ ├── GoodToStringClass.java │ │ ├── GoodToStringCompositeClass.java │ │ ├── GoodToStringIgnoredFieldClass.java │ │ ├── GoodToStringWithStaticClass.java │ │ ├── IncompleteToStringClass.java │ │ ├── InnerPublicStaticClass.java │ │ ├── MissingToStringClass.java │ │ ├── NoFieldClass.java │ │ ├── NoToStringLibraryFieldClass.java │ │ ├── SuppressedToStringClass.java │ │ └── ToStringLibraryFieldClass.java │ └── item8 │ │ ├── BadEqualsOverride.java │ │ ├── BaseConcreteClass.java │ │ ├── BaseNonEqualsConcreteClass.java │ │ └── GoodEqualsOverride.java │ ├── findbugs │ ├── DocumentedSuppressFBWarnings.java │ ├── DocumentedSuppressWarnings.java │ ├── EqualsContainsHashCode.java │ ├── EqualsHashCodeDifferentFields.java │ ├── GoodEqualsHashCodeImplementation.java │ ├── HashCodeContainsEquals.java │ ├── NoEqualsHashCode.java │ ├── OnlyEqualsImplementation.java │ ├── SubclassOfBadEqualsHashCode.java │ ├── UndocumentedSuppressFBWarnings.java │ ├── UndocumentedSuppressWarnings.java │ └── jdk │ │ ├── UselessStringValueOfCall.java │ │ └── patterncompile │ │ ├── NonStaticPatternCompile.java │ │ └── StaticPatternCompile.java │ ├── guava │ ├── ClassWhichCallsNormalMethod.java │ ├── ClassWhichCallsVisibleMethodForTesting.java │ ├── JUnit3Test.java │ ├── JUnit4Test.java │ ├── MethodWithVisibleForTesting.java │ └── MethodWithoutVisibleForTesting.java │ ├── jpa │ ├── ColumnWithLength.java │ ├── ColumnWithLongLengthAndLob.java │ ├── ColumnWithNegativeLength.java │ ├── ColumnWithNullable.java │ ├── ColumnWithTooLongLength.java │ ├── ColumnWithoutElement.java │ ├── GetterWithLongLengthAndLob.java │ ├── GetterWithTooLongLength.java │ ├── GetterWithoutElement.java │ ├── LongColumnName.java │ ├── LongColumnNameByAnnotatedMethod.java │ ├── LongColumnNameWithoutAnnotationParameter.java │ ├── LongIndexNameForHibernate.java │ ├── LongIndexNameForOpenJPA.java │ ├── LongTableName.java │ ├── LongTableNameWithoutAnnotationParameter.java │ ├── NonNullablePrimitiveColumn.java │ ├── NullableBooleanColumn.java │ ├── NullableBooleanGetter.java │ ├── NullableByteColumn.java │ ├── NullableDoubleColumn.java │ ├── NullableFloatColumn.java │ ├── NullableIntColumn.java │ ├── NullableLongColumn.java │ ├── NullableShortColumn.java │ ├── ShortColumnName.java │ ├── ShortColumnNameWithoutAnnotationParameter.java │ ├── ShortIndexNameForHibernate.java │ ├── ShortIndexNameForOpenJPA.java │ ├── ShortTableName.java │ ├── ShortTableNameNoAnnotationPara.java │ └── UseColumnDefinition.java │ ├── jsr305 │ ├── BadImmutableClass.java │ ├── ExtendsMutableClass.java │ ├── GoodMutableClass.java │ ├── ImmutableEnum.java │ └── nullness │ │ ├── AnnotatedArgument.java │ │ ├── AnnotatedArgumentsEnum.java │ │ ├── AnnotatedClass.java │ │ ├── AnnotatedInnerClassArguments.java │ │ ├── AnnotatedMethod.java │ │ ├── AnnotatedReturnValue.java │ │ ├── AnonymousClassConstructor.java │ │ ├── ChildOfUnannotatedBoundGenerics.java │ │ ├── ComplexGenerics.java │ │ ├── NoAnnotation.java │ │ ├── PrimitiveArgument.java │ │ ├── StandardEnum.java │ │ ├── UnannotatedBoundGenerics.java │ │ ├── UnannotatedEnumLookAlike.java │ │ ├── UnannotatedExtendingLibClass.java │ │ ├── UnannotatedExtendingLibClassPropagatingGenerics.java │ │ ├── UnannotatedIndirectGenericBinding.java │ │ ├── UnannotatedInnerClassArguments.java │ │ ├── UnannotatedReturnValue.java │ │ ├── UnannotatedVarargs.java │ │ └── annotatedpackage │ │ ├── AnnotatedPackage.java │ │ └── package-info.java │ ├── junit │ ├── IgnoreClassWithEmptyExplanation.java │ ├── IgnoreClassWithExplanation.java │ ├── IgnoreClassWithoutExplanation.java │ ├── IgnoreMethodWithEmptyExplanation.java │ ├── IgnoreMethodWithExplanation.java │ └── IgnoreMethodWithoutExplanation.java │ └── system │ ├── UseSystemErr.java │ └── UseSystemOut.java └── resources ├── fst-1.63.jar └── rxjava-1.0.16.jar /.arcconfig: -------------------------------------------------------------------------------- 1 | { 2 | "project_id" : "Monits - Findbugs Plugin", 3 | "conduit_uri" : "http://ph.monits.com/", 4 | "unit.engine": "MavenUnitTestEngine", 5 | "lint.maven.options": [ 6 | ], 7 | "unit.maven.options": [ 8 | "-T1C" 9 | ], 10 | "load": [ 11 | "/opt/arc-lint" 12 | ] 13 | } 14 | 15 | -------------------------------------------------------------------------------- /.arclint: -------------------------------------------------------------------------------- 1 | { 2 | "linters": { 3 | "json": { 4 | "type": "json", 5 | "include": [ 6 | "(^\\.arcconfig$)", 7 | "(^\\.arclint$)" 8 | ] 9 | }, 10 | "chmod": { 11 | "type": "chmod" 12 | }, 13 | "merge-conflict": { 14 | "type": "merge-conflict" 15 | }, 16 | "spelling": { 17 | "type": "spelling" 18 | }, 19 | "maven": { 20 | "type": "maven", 21 | "lints": [ "checkstyle", "pmd", "cpd", "findbugs" ] 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.gitflowconfig: -------------------------------------------------------------------------------- 1 | DEV=master 2 | 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | .project 3 | .classpath 4 | .settings 5 | .DS_Store 6 | *~ 7 | pom.xml.releaseBackup 8 | release.properties 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - oraclejdk8 4 | sudo: false 5 | cache: 6 | directories: 7 | - $HOME/.m2 8 | install: true #Override default build step for maven (bad Travis!) 9 | script: 10 | - mvn clean install -Dgpg.skip=true 11 | after_success: 12 | - mvn clean test jacoco:report coveralls:report -Dgpg.skip=true 13 | notifications: 14 | email: 15 | recipients: 16 | - jmsotuyo@monits.com 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Findbugs Plugin 2 | Findbugs plugin set from Monits. Removing bugs before they happen by enforcing best practices. 3 | 4 | [![Build Status](https://secure.travis-ci.org/Monits/findbugs-plugin.svg)](http://travis-ci.org/Monits/findbugs-plugin) 5 | [![Coverage Status](https://coveralls.io/repos/Monits/findbugs-plugin/badge.svg)](https://coveralls.io/r/Monits/findbugs-plugin) 6 | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.monits/findbugs-plugin/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.monits/findbugs-plugin) 7 | [![Download](https://api.bintray.com/packages/monits/monits-android/findbugs-plugin/images/download.svg) ](https://bintray.com/monits/monits-android/findbugs-plugin/_latestVersion) 8 | 9 | # How to use with Maven 10 | 11 | To use this plugin, please configure your findbugs-maven-plugin like below. 12 | 13 | ```xml 14 | 15 | org.codehaus.mojo 16 | findbugs-maven-plugin 17 | 3.0.1 18 | 19 | 20 | 21 | com.monits 22 | findbugs-plugin 23 | 0.2.0 24 | 25 | 26 | 27 | 28 | ``` 29 | 30 | We are both on **Central Repository** (formerly *Maven Central*) and **jcenter**. 31 | 32 | # History 33 | 34 | ## 0.2.0 35 | New detectors: 36 | - added `InconsistentHashCodeEqualsDetector`: this new detector will check for 37 | classes that uses distinct fields in the calculation of `hashCode` and `equals`. 38 | A class with this bug breaks the "equal objects must have equal hashcodes" invariant 39 | and/or may also generate hash collisions on objects that are unequal. 40 | - Effective Java's item 10. `toString` should be overridden when a 41 | class has inner state. The check will make sure if members have themselves any 42 | state / are primitives to discard meaningless reports 43 | (think of a Service with a reference to a DAO). 44 | - Effective Java's item 8. Never override `equals` if defined by a super-class 45 | other than `Object`. Doing so breaks the general `equals` contract by breaking 46 | *symmetry*. 47 | - added `UselessStringValueOfCallDetector` to detect useless `String.valueOf` calls 48 | when the argument given is already a string. 49 | - added `NonStaticPatternCompileDetector` to report methods that have locals `Pattern.compile` 50 | with a harcoded, static or final local regex as a static final to avoid recompiling the regex. 51 | 52 | ## 0.1.1 53 | - forked from [WorksApplication's original plugin](WorksApplications/findbugs-plugin). 54 | Awesome plugin, but Findbugs 2 only. 55 | - upgraded to Findbugs 3 56 | - rewrote most error messages to be more specific 57 | - fixed method detection for `UnknownNullnessDetector` 58 | - rewrote all unit tests, and added several new ones. Great code coverage 59 | - made `UnexpectedAccessDetector` ignore calls from classes that use JUnit's 60 | annotations or extends `junit.framework.TestCase`, which being tests are 61 | legit accesses. 62 | 63 | ## 0.0.3 64 | 65 | - added `UnexpectedAccessDetector` 66 | - added `UndocumentedSuppressFBWarningsDetector` 67 | - upgraded JDK from 1.6 to 1.7 68 | 69 | ## 0.0.2 70 | 71 | - added `BrokenImmutableClassDetector` 72 | - added `LongIndexNameDetector` 73 | - added `LongTableNameDetector` 74 | - added `LongColumnNameDetector` 75 | - added `UnknownNullnessDetector` 76 | - added `UndocumentedIgnoreDetector` 77 | - added `ImplicitLengthDetector` 78 | - added `ImplicitNullnessDetector` 79 | - added `ColumnDefinitionDetector` 80 | - added `NullablePrimitiveDetector` 81 | 82 | ## 0.0.1 83 | 84 | - First release 85 | 86 | # Copyright and License 87 | 88 | Copyright 2015 Monits S.A. 89 | Copyright 2013 Works Applications. Co.,Ltd. 90 | 91 | Licensed under the Apache License, Version 2.0 (the "License"); 92 | you may not use this file except in compliance with the License. 93 | You may obtain a copy of the License at 94 | 95 | http://www.apache.org/licenses/LICENSE-2.0 96 | 97 | Unless required by applicable law or agreed to in writing, software 98 | distributed under the License is distributed on an "AS IS" BASIS, 99 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 100 | See the License for the specific language governing permissions and 101 | limitations under the License. 102 | -------------------------------------------------------------------------------- /bump-version: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ -z "$1" ]; then 4 | exit 0 5 | fi 6 | 7 | # -dev versions are -SNAPSHOT, RCs and releases are left untouched 8 | version=$1 9 | if [[ $1 =~ -dev$ ]]; then 10 | version=${1%-dev}-SNAPSHOT 11 | fi 12 | 13 | mvn versions:set -DnewVersion=$version -DgenerateBackupPoms=false 14 | -------------------------------------------------------------------------------- /findbugs-exclude.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/main/java/com/monits/findbugs/effectivejava/EqualsOverrideDetector.java: -------------------------------------------------------------------------------- 1 | package com.monits.findbugs.effectivejava; 2 | 3 | import java.util.Set; 4 | 5 | import javax.annotation.Nonnull; 6 | 7 | import org.apache.bcel.classfile.Method; 8 | 9 | import edu.umd.cs.findbugs.BugInstance; 10 | import edu.umd.cs.findbugs.BugReporter; 11 | import edu.umd.cs.findbugs.BytecodeScanningDetector; 12 | import edu.umd.cs.findbugs.ba.Hierarchy2; 13 | import edu.umd.cs.findbugs.ba.XMethod; 14 | import edu.umd.cs.findbugs.classfile.ClassDescriptor; 15 | import edu.umd.cs.findbugs.classfile.DescriptorFactory; 16 | 17 | public class EqualsOverrideDetector extends BytecodeScanningDetector { 18 | 19 | private final static ClassDescriptor OBJECT_CLASS_DESCRIPTOR 20 | = DescriptorFactory.createClassDescriptor(Object.class); 21 | 22 | private final BugReporter bugReporter; 23 | 24 | public EqualsOverrideDetector(@Nonnull final BugReporter bugReporter) { 25 | this.bugReporter = bugReporter; 26 | } 27 | 28 | @Override 29 | public void visitMethod(@Nonnull final Method method) { 30 | // Is this equals? 31 | if ("equals".equals(method.getName()) && "(Ljava/lang/Object;)Z".equals(method.getSignature())) { 32 | final Set superMethods = Hierarchy2.findSuperMethods(getXMethod()); 33 | 34 | for (final XMethod xm : superMethods) { 35 | final ClassDescriptor classDescriptor = xm.getClassDescriptor(); 36 | if (!OBJECT_CLASS_DESCRIPTOR.equals(classDescriptor)) { 37 | final BugInstance bug = new BugInstance(this, "DONT_OVERRIDE_EQUALS", NORMAL_PRIORITY) 38 | .addClass(this) 39 | .addClass(classDescriptor); 40 | bugReporter.reportBug(bug); 41 | return; 42 | } 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/monits/findbugs/effectivejava/ToStringDetector.java: -------------------------------------------------------------------------------- 1 | package com.monits.findbugs.effectivejava; 2 | 3 | import java.util.BitSet; 4 | import java.util.Deque; 5 | import java.util.HashMap; 6 | import java.util.Iterator; 7 | import java.util.LinkedList; 8 | import java.util.List; 9 | import java.util.Map; 10 | import java.util.Map.Entry; 11 | 12 | import javax.annotation.Nonnull; 13 | 14 | import org.apache.bcel.classfile.Method; 15 | import org.apache.bcel.generic.ConstantPoolGen; 16 | import org.apache.bcel.generic.FieldInstruction; 17 | import org.apache.bcel.generic.GETFIELD; 18 | import org.apache.bcel.generic.Instruction; 19 | import org.apache.bcel.generic.InstructionHandle; 20 | 21 | import edu.umd.cs.findbugs.BugInstance; 22 | import edu.umd.cs.findbugs.BugReporter; 23 | import edu.umd.cs.findbugs.BytecodeScanningDetector; 24 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 25 | import edu.umd.cs.findbugs.ba.AnalysisContext; 26 | import edu.umd.cs.findbugs.ba.BasicBlock; 27 | import edu.umd.cs.findbugs.ba.BasicBlock.InstructionIterator; 28 | import edu.umd.cs.findbugs.ba.CFG; 29 | import edu.umd.cs.findbugs.ba.CFGBuilderException; 30 | import edu.umd.cs.findbugs.ba.ClassContext; 31 | import edu.umd.cs.findbugs.ba.Edge; 32 | import edu.umd.cs.findbugs.ba.XClass; 33 | import edu.umd.cs.findbugs.ba.XField; 34 | import edu.umd.cs.findbugs.ba.XMethod; 35 | import edu.umd.cs.findbugs.classfile.CheckedAnalysisException; 36 | import edu.umd.cs.findbugs.classfile.ClassDescriptor; 37 | import edu.umd.cs.findbugs.classfile.DescriptorFactory; 38 | import edu.umd.cs.findbugs.classfile.MissingClassException; 39 | import edu.umd.cs.findbugs.classfile.analysis.AnnotatedObject; 40 | import edu.umd.cs.findbugs.classfile.analysis.AnnotationValue; 41 | 42 | /** 43 | * ToStringDetector tries to detect issues with ToString() method. 44 | * 45 | * This detector will warn you if: 46 | * 47 | * - Your class has internal state but doesn't override ToString() method. 48 | * 49 | * - Your class has an internal variable that is not used in ToString() method. 50 | * 51 | * Enums and static fields are ignored. 52 | * 53 | * Interfaces and external libraries without ToString() are ignored too. 54 | */ 55 | public class ToStringDetector extends BytecodeScanningDetector { 56 | 57 | public static final String MISSING_FIELD_IN_TO_STRING = "MISSING_FIELD_IN_TO_STRING"; 58 | public static final String MISSING_TO_STRING_OVERRIDE = "MISSING_TO_STRING_OVERRIDE"; 59 | 60 | private static final String TO_STRING = "toString"; 61 | private static final String JAVA_LANG_ENUM = "java.lang.Enum"; 62 | private static final ClassDescriptor SUPPRESS_FB_WARNING_CD = DescriptorFactory 63 | .createClassDescriptor(SuppressFBWarnings.class); 64 | 65 | private final BugReporter bugReporter; 66 | 67 | @SuppressFBWarnings(value = "PMB_POSSIBLE_MEMORY_BLOAT", justification = "We need this as database.") 68 | private static final Map IS_INTERESTING_CLASS_CACHE = new HashMap<>(); 69 | private Map interestFields; 70 | 71 | private boolean hasToStringOverride; 72 | 73 | /** 74 | * Creates a new ToStringDetector. 75 | * 76 | * @param bugReporter 77 | * the Findbugs bug reporter. 78 | * 79 | */ 80 | public ToStringDetector(@Nonnull final BugReporter bugReporter) { 81 | this.bugReporter = bugReporter; 82 | 83 | // Known interesting classes 84 | IS_INTERESTING_CLASS_CACHE.put("java/lang/String", Boolean.TRUE); 85 | } 86 | 87 | @Override 88 | public void visitClassContext(@Nonnull final ClassContext classContext) { 89 | try { 90 | final XClass xClass = classContext.getXClass(); 91 | 92 | if (xClass == null) { 93 | return; 94 | } 95 | 96 | // Never report on enums, they are good as they are 97 | final ClassDescriptor superCD = xClass.getSuperclassDescriptor(); 98 | if (superCD != null && JAVA_LANG_ENUM.equals(superCD.getDottedClassName())) { 99 | return; 100 | } 101 | 102 | interestFields = getInterestingFields(xClass); 103 | 104 | if (!interestFields.isEmpty()) { 105 | // Continue with analysis.... 106 | super.visitClassContext(classContext); 107 | 108 | // ... and check if any fields were not included in toString 109 | if (!interestFields.isEmpty()) { 110 | if (hasToStringOverride) { 111 | for (final Entry entry : interestFields.entrySet()) { 112 | final BugInstance bug = new BugInstance(this, MISSING_FIELD_IN_TO_STRING, NORMAL_PRIORITY) 113 | .addClass(this).addField(entry.getValue()); 114 | bugReporter.reportBug(bug); 115 | } 116 | } else { 117 | final BugInstance bug = new BugInstance(this, MISSING_TO_STRING_OVERRIDE, NORMAL_PRIORITY) 118 | .addClass(this); 119 | bugReporter.reportBug(bug); 120 | } 121 | } 122 | } 123 | } catch (final CheckedAnalysisException e) { 124 | if (e instanceof MissingClassException) { 125 | AnalysisContext.reportMissingClass((MissingClassException) e); 126 | } 127 | } finally { 128 | interestFields = null; 129 | hasToStringOverride = false; 130 | } 131 | } 132 | 133 | @Nonnull 134 | private Map getInterestingFields(@Nonnull final XClass xClass) throws CheckedAnalysisException { 135 | final List fields = xClass.getXFields(); 136 | final Map toStringFields = new HashMap(); 137 | 138 | for (final XField f : fields) { 139 | if (!f.isStatic() && !f.isSynthetic()) { 140 | if (isIgnored(f, MISSING_FIELD_IN_TO_STRING)) { 141 | continue; 142 | } 143 | 144 | if (f.isReferenceType()) { 145 | final String signature = f.getSignature(); 146 | final ClassDescriptor fieldClassDescriptor = DescriptorFactory 147 | .createClassDescriptorFromFieldSignature(signature); 148 | if (fieldClassDescriptor == null) { 149 | // This is an array of primitives, interesting by 150 | // default 151 | toStringFields.put(f.getName(), f); 152 | } else { 153 | // Field classes are analyzed recursively 154 | if (isClassFieldAnInterestingField(fieldClassDescriptor)) { 155 | toStringFields.put(f.getName(), f); 156 | } 157 | } 158 | } else { 159 | // primitives are interesting by default! 160 | toStringFields.put(f.getName(), f); 161 | } 162 | } 163 | } 164 | 165 | return toStringFields; 166 | } 167 | 168 | private static boolean isIgnored(@Nonnull final AnnotatedObject ao, @Nonnull final String error) { 169 | final AnnotationValue suppressAnnotation = ao.getAnnotation(ToStringDetector.SUPPRESS_FB_WARNING_CD); 170 | 171 | if (suppressAnnotation != null) { 172 | final Object[] values = (Object[]) suppressAnnotation.getValue("value"); 173 | if (values != null) { 174 | for (final Object v : values) { 175 | if (error.equals(v)) { 176 | return true; 177 | } 178 | } 179 | } 180 | } 181 | 182 | return false; 183 | } 184 | 185 | private boolean isClassFieldAnInterestingField(@Nonnull final ClassDescriptor fieldClassDescriptor) 186 | throws CheckedAnalysisException { 187 | final XClass fieldXClass = fieldClassDescriptor.getXClass(); 188 | 189 | if (AnalysisContext.currentAnalysisContext().isApplicationClass(fieldClassDescriptor)) { 190 | // It's an Application fields, check if it needs a toString itself. 191 | return !isIgnored(fieldXClass, MISSING_TO_STRING_OVERRIDE) && isStatefullClass(fieldXClass); 192 | } else { 193 | // For non-application fields, just check if they provide a 194 | // toString() override. 195 | final XMethod toString = fieldXClass.findMethod(TO_STRING, "()Ljava/lang/String;", false); 196 | return toString != null && !"java.lang.Object".equals(toString.getClassName()); 197 | } 198 | } 199 | 200 | private boolean isStatefullClass(@Nonnull final XClass xClass) throws CheckedAnalysisException { 201 | final String className = xClass.getClassDescriptor().getClassName(); 202 | if (IS_INTERESTING_CLASS_CACHE.containsKey(className)) { 203 | return IS_INTERESTING_CLASS_CACHE.get(className); 204 | } 205 | 206 | // Is it an enum? 207 | final ClassDescriptor superCD = xClass.getSuperclassDescriptor(); 208 | if (superCD != null && JAVA_LANG_ENUM.equals(superCD.getDottedClassName())) { 209 | IS_INTERESTING_CLASS_CACHE.put(className, Boolean.TRUE); 210 | return true; 211 | } 212 | 213 | // Default to false in case of cross references between classes... 214 | IS_INTERESTING_CLASS_CACHE.put(className, Boolean.FALSE); 215 | 216 | final Map interestingFields = getInterestingFields(xClass); 217 | final Boolean ret = interestingFields.isEmpty() ? Boolean.FALSE : Boolean.TRUE; 218 | IS_INTERESTING_CLASS_CACHE.put(className, ret); 219 | 220 | return ret; 221 | } 222 | 223 | @Override 224 | public String toString() { 225 | return "ToStringDetector [bugReporter=" + bugReporter + ", interestFields=" + interestFields 226 | + ", hasToStringOverride=" + hasToStringOverride + "]"; 227 | } 228 | 229 | @Override 230 | public void visitMethod(@Nonnull final Method method) { 231 | // Is this toString? 232 | if (TO_STRING.equals(method.getName()) && method.getArgumentTypes().length == 0) { 233 | hasToStringOverride = true; 234 | 235 | try { 236 | final CFG cfg = getClassContext().getCFG(method); 237 | final ConstantPoolGen cpg = cfg.getMethodGen().getConstantPool(); 238 | final BasicBlock bb = cfg.getEntry(); 239 | checkBlock(bb, cpg, cfg); 240 | } catch (final CFGBuilderException cbe) { 241 | interestFields.clear(); 242 | } 243 | } 244 | } 245 | 246 | private void checkBlock(@Nonnull final BasicBlock bb, @Nonnull final ConstantPoolGen cpg, @Nonnull final CFG cfg) { 247 | final BitSet visitedBlock = new BitSet(); 248 | final Deque toBeProcessed = new LinkedList(); 249 | toBeProcessed.add(bb); 250 | 251 | while (!toBeProcessed.isEmpty()) { 252 | final BasicBlock currentBlock = toBeProcessed.removeFirst(); 253 | final InstructionIterator ii = currentBlock.instructionIterator(); 254 | 255 | while (!interestFields.isEmpty() && ii.hasNext()) { 256 | final InstructionHandle ih = ii.next(); 257 | final Instruction ins = ih.getInstruction(); 258 | 259 | if (ins instanceof FieldInstruction) { 260 | final FieldInstruction fi = (FieldInstruction) ins; 261 | final String fieldName = fi.getFieldName(cpg); 262 | 263 | if (ins instanceof GETFIELD) { 264 | // TODO : Make sure we are actually using it to place it in the string representation 265 | interestFields.remove(fieldName); 266 | } 267 | } 268 | // TODO : Check for INVOKESPECIAL / INVOKEVIRTUAL calling toString from other objects 269 | } 270 | 271 | if (interestFields.isEmpty()) { 272 | return; 273 | } 274 | 275 | // Get adjacent blocks 276 | final Iterator oei = cfg.outgoingEdgeIterator(currentBlock); 277 | while (oei.hasNext()) { 278 | final Edge e = oei.next(); 279 | final BasicBlock cb = e.getTarget(); 280 | final int label = cb.getLabel(); 281 | 282 | // Avoid repeated blocks 283 | if (!visitedBlock.get(label)) { 284 | toBeProcessed.addLast(cb); 285 | visitedBlock.set(label); 286 | } 287 | } 288 | } 289 | } 290 | } 291 | -------------------------------------------------------------------------------- /src/main/java/com/monits/findbugs/jdk/InconsistentHashCodeEqualsDetector.java: -------------------------------------------------------------------------------- 1 | package com.monits.findbugs.jdk; 2 | 3 | import java.util.BitSet; 4 | import java.util.Deque; 5 | import java.util.HashMap; 6 | import java.util.HashSet; 7 | import java.util.Iterator; 8 | import java.util.LinkedList; 9 | import java.util.Map; 10 | import java.util.Set; 11 | 12 | import javax.annotation.Nonnull; 13 | 14 | import org.apache.bcel.classfile.Method; 15 | import org.apache.bcel.generic.ConstantPoolGen; 16 | import org.apache.bcel.generic.FieldInstruction; 17 | import org.apache.bcel.generic.GETFIELD; 18 | import org.apache.bcel.generic.Instruction; 19 | import org.apache.bcel.generic.InstructionHandle; 20 | 21 | import edu.umd.cs.findbugs.BugInstance; 22 | import edu.umd.cs.findbugs.BugReporter; 23 | import edu.umd.cs.findbugs.BytecodeScanningDetector; 24 | import edu.umd.cs.findbugs.ba.BasicBlock; 25 | import edu.umd.cs.findbugs.ba.BasicBlock.InstructionIterator; 26 | import edu.umd.cs.findbugs.ba.CFG; 27 | import edu.umd.cs.findbugs.ba.CFGBuilderException; 28 | import edu.umd.cs.findbugs.ba.ClassContext; 29 | import edu.umd.cs.findbugs.ba.Edge; 30 | import edu.umd.cs.findbugs.ba.XField; 31 | import edu.umd.cs.findbugs.ba.XMethod; 32 | 33 | public class InconsistentHashCodeEqualsDetector extends BytecodeScanningDetector { 34 | 35 | private static final String HASH_CODE_METHOD_NAME = "hashCode"; 36 | private static final String EQUALS_METHOD_NAME = "equals"; 37 | private static final String EQUALS_SIGNATURE = "(Ljava/lang/Object;)Z"; 38 | 39 | private XMethodAndFields equalsFields; 40 | private XMethodAndFields hashCodeFields; 41 | private Map fieldNameToXField; 42 | 43 | private final BugReporter reporter; 44 | 45 | public InconsistentHashCodeEqualsDetector(@Nonnull final BugReporter reporter) { 46 | this.reporter = reporter; 47 | } 48 | 49 | @Override 50 | public void visitClassContext(final ClassContext classContext) { 51 | if (qualifiesForDetection(classContext)) { 52 | fieldNameToXField = populateNamesAndXFields(classContext); 53 | super.visitClassContext(classContext); 54 | 55 | if (!hashCodeFields.getFieldNames().equals(equalsFields.getFieldNames())) { 56 | reportBugs("HASHCHODE_HAS_MORE_FIELDS_THAN_EQUALS", HIGH_PRIORITY, hashCodeFields.getXMethod(), 57 | getFieldsDifference(hashCodeFields.getFieldNames(), equalsFields.getFieldNames())); 58 | 59 | reportBugs("EQUALS_HAS_MORE_FIELDS_THAN_HASHCODE", HIGH_PRIORITY, equalsFields.getXMethod(), 60 | getFieldsDifference(equalsFields.getFieldNames(), hashCodeFields.getFieldNames())); 61 | } 62 | 63 | equalsFields = null; 64 | hashCodeFields = null; 65 | fieldNameToXField = null; 66 | } 67 | } 68 | 69 | @Nonnull 70 | private Map populateNamesAndXFields(@Nonnull final ClassContext classContext) { 71 | final Map nameToXField = new HashMap(); 72 | for (final XField xField : classContext.getXClass().getXFields()) { 73 | if (!xField.isStatic() && !xField.isSynthetic()) { 74 | nameToXField.put(xField.getName(), xField); 75 | } 76 | } 77 | return nameToXField; 78 | } 79 | 80 | @Nonnull 81 | private Set getFieldsDifference(@Nonnull final Set comparable, 82 | @Nonnull final Set comparator) { 83 | 84 | final Set copyComparable = new HashSet(comparable); 85 | for (final XField xField : comparator) { 86 | copyComparable.remove(xField); 87 | } 88 | 89 | return copyComparable; 90 | } 91 | 92 | private boolean qualifiesForDetection(@Nonnull final ClassContext ctx) { 93 | int methodCounter = 0; 94 | for (final Method m : ctx.getMethodsInCallOrder()) { 95 | if (isEqualsMethod(m) || isHashCodeMethod(m)) { 96 | methodCounter++; 97 | } 98 | } 99 | 100 | // must be 2, hashCode and equals methods 101 | return methodCounter == 2; 102 | } 103 | 104 | private void reportBugs(@Nonnull final String bugType, @Nonnull final int priority, @Nonnull final XMethod method, 105 | @Nonnull final Set fields) { 106 | 107 | for (final XField xField : fields) { 108 | final BugInstance bug = new BugInstance(this, bugType, priority) 109 | .addClass(getClassContext().getClassDescriptor()) 110 | .addMethod(method) 111 | .addField(xField); 112 | 113 | reporter.reportBug(bug); 114 | } 115 | } 116 | 117 | @Override 118 | public void visitMethod(final Method method) { 119 | if (isEqualsMethod(method)) { 120 | equalsFields = new XMethodAndFields(getXMethod(), getMethodXFields(method)); 121 | } else if (isHashCodeMethod(method)) { 122 | hashCodeFields = new XMethodAndFields(getXMethod(), getMethodXFields(method)); 123 | } 124 | } 125 | 126 | private boolean isEqualsMethod(@Nonnull final Method method) { 127 | return EQUALS_METHOD_NAME.equals(method.getName()) && EQUALS_SIGNATURE.equals(method.getSignature()); 128 | } 129 | 130 | private boolean isHashCodeMethod(@Nonnull final Method method) { 131 | return HASH_CODE_METHOD_NAME.equals(method.getName()) && method.getArgumentTypes().length == 0; 132 | } 133 | 134 | @Nonnull 135 | private Set getMethodXFields(@Nonnull final Method method) { 136 | final Set xFields = new HashSet(); 137 | 138 | final CFG cfg; 139 | final ConstantPoolGen cpg; 140 | final BasicBlock bb; 141 | 142 | try { 143 | cfg = getClassContext().getCFG(method); 144 | cpg = cfg.getMethodGen().getConstantPool(); 145 | bb = cfg.getEntry(); 146 | } catch (final CFGBuilderException cbe) { 147 | return xFields; 148 | } 149 | 150 | final BitSet visitedBlock = new BitSet(); 151 | final Deque toBeProcessed = new LinkedList(); 152 | toBeProcessed.add(bb); 153 | 154 | while (!toBeProcessed.isEmpty()) { 155 | final BasicBlock currentBlock = toBeProcessed.removeFirst(); 156 | final InstructionIterator ii = currentBlock.instructionIterator(); 157 | 158 | while (ii.hasNext()) { 159 | final InstructionHandle ih = ii.next(); 160 | final Instruction ins = ih.getInstruction(); 161 | 162 | if (ins instanceof FieldInstruction) { 163 | final FieldInstruction fi = (FieldInstruction) ins; 164 | final String fieldName = fi.getFieldName(cpg); 165 | 166 | if (ins instanceof GETFIELD) { 167 | // TODO : Make sure we are actually using it to compute hashCode / equals 168 | final XField xField = fieldNameToXField.get(fieldName); 169 | if (null != xField) { 170 | // add field metadata 171 | xFields.add(xField); 172 | } 173 | } 174 | } 175 | // TODO : Check for INVOKESPECIAL / INVOKEVIRTUAL calling toString from other objects 176 | } 177 | 178 | // Get adjacent blocks 179 | final Iterator oei = cfg.outgoingEdgeIterator(currentBlock); 180 | while (oei.hasNext()) { 181 | final Edge e = oei.next(); 182 | final BasicBlock cb = e.getTarget(); 183 | final int label = cb.getLabel(); 184 | 185 | // Avoid repeated blocks 186 | if (!visitedBlock.get(label)) { 187 | toBeProcessed.addLast(cb); 188 | visitedBlock.set(label); 189 | } 190 | } 191 | } 192 | 193 | return xFields; 194 | } 195 | 196 | private static final class XMethodAndFields { 197 | private final XMethod method; 198 | private final Set fieldNames; 199 | 200 | protected XMethodAndFields(@Nonnull final XMethod method, @Nonnull final Set fieldNames) { 201 | this.method = method; 202 | this.fieldNames = fieldNames; 203 | } 204 | 205 | @Nonnull public XMethod getXMethod() { 206 | return method; 207 | } 208 | 209 | @Nonnull public Set getFieldNames() { 210 | return fieldNames; 211 | } 212 | } 213 | 214 | } 215 | -------------------------------------------------------------------------------- /src/main/java/com/monits/findbugs/jdk/NonStaticPatternCompileDetector.java: -------------------------------------------------------------------------------- 1 | package com.monits.findbugs.jdk; 2 | 3 | import javax.annotation.Nonnull; 4 | 5 | import edu.umd.cs.findbugs.BugInstance; 6 | import edu.umd.cs.findbugs.BugReporter; 7 | import edu.umd.cs.findbugs.ba.XMethod; 8 | import edu.umd.cs.findbugs.bcel.OpcodeStackDetector; 9 | 10 | public class NonStaticPatternCompileDetector extends OpcodeStackDetector { 11 | 12 | public static final String NON_STATIC_PATTERN_COMPILE_CALL = "NON_STATIC_PATTERN_COMPILE_CALL"; 13 | private static final String PATTERN_CLASSNAME = "java/util/regex/Pattern"; 14 | private static final String COMPILE = "compile"; 15 | 16 | private final BugReporter bugReporter; 17 | 18 | /** 19 | * Report any non-static Pattern.compile 20 | * @param bugReporter The BugReporter 21 | */ 22 | public NonStaticPatternCompileDetector(@Nonnull final BugReporter bugReporter) { 23 | this.bugReporter = bugReporter; 24 | } 25 | 26 | @Override 27 | public void sawOpcode(@Nonnull final int seen) { 28 | if (seen == INVOKESTATIC 29 | && PATTERN_CLASSNAME.equals(getClassConstantOperand()) 30 | && COMPILE.equals(getFieldDescriptorOperand().getName()) 31 | && getNextOpcode() != PUTSTATIC 32 | // There is not a load instruction in the previous opcode 33 | // when the regex is final or static or hardwired. 34 | && !isRegisterLoad(getPrevOpcode(1)) 35 | && !isParamFormedByConcatenation() 36 | // we don't have to report if the regex came from a method 37 | && getPrevOpcode(1) != INVOKEVIRTUAL) { 38 | this.bugReporter.reportBug(new BugInstance(this, NON_STATIC_PATTERN_COMPILE_CALL, NORMAL_PRIORITY) 39 | .addClassAndMethod(this) 40 | .addSourceLine(this)); 41 | } 42 | } 43 | 44 | private boolean isParamFormedByConcatenation() { 45 | // get the method invoked in the param 46 | final XMethod xMethod = stack.getStackItem(0).getReturnValueOf(); 47 | if (xMethod == null) { 48 | //ignore those that are not a method 49 | return false; 50 | } 51 | // if the concatenation is complex the compiler translates that into a StringBuilder 52 | return "java/lang/StringBuilder".equals( 53 | // get the descriptor of the method and get the slashed class name being loaded 54 | xMethod.getMethodDescriptor().getSlashedClassName()); 55 | } 56 | 57 | private boolean isRegisterLoad(@Nonnull final int prevOpcode) { 58 | return prevOpcode == ALOAD || prevOpcode == ALOAD_0 || prevOpcode == ALOAD_1 59 | || prevOpcode == ALOAD_2 || prevOpcode == ALOAD_3; 60 | } 61 | } -------------------------------------------------------------------------------- /src/main/java/com/monits/findbugs/jdk/UselessStringValueOfCallDetector.java: -------------------------------------------------------------------------------- 1 | package com.monits.findbugs.jdk; 2 | 3 | import java.util.Collections; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | import javax.annotation.Nonnull; 8 | 9 | import edu.umd.cs.findbugs.BugInstance; 10 | import edu.umd.cs.findbugs.BugReporter; 11 | import edu.umd.cs.findbugs.OpcodeStack.Item; 12 | import edu.umd.cs.findbugs.bcel.OpcodeStackDetector; 13 | 14 | public class UselessStringValueOfCallDetector extends OpcodeStackDetector { 15 | 16 | public static final String USELESS_STRING_VALUEOF_CALL = "USELESS_STRING_VALUEOF_CALL"; 17 | private static final String VALUE_OF = "valueOf"; 18 | private static final Map CLASSNAME_SIGNATURE_MAP; 19 | 20 | static { 21 | final Map map = new HashMap(); 22 | map.put("java/lang/String", "Ljava/lang/String;"); 23 | CLASSNAME_SIGNATURE_MAP = Collections.unmodifiableMap(map); 24 | } 25 | 26 | private final BugReporter bugReporter; 27 | 28 | /** 29 | * Report any useless valueOf uses. 30 | * @param bugReporter The BugReporter 31 | */ 32 | public UselessStringValueOfCallDetector(@Nonnull final BugReporter bugReporter) { 33 | this.bugReporter = bugReporter; 34 | } 35 | 36 | @Override 37 | public void sawOpcode(final int seen) { 38 | if (seen == INVOKESTATIC 39 | && VALUE_OF.equals(getFieldDescriptorOperand().getName()) 40 | && isSameTypeArgumentAndClassOperand(getClassConstantOperand(), stack.getStackItem(0).getSignature()) 41 | && !isAConcatenationOfStrings()) { 42 | this.bugReporter.reportBug(new BugInstance(this, USELESS_STRING_VALUEOF_CALL, NORMAL_PRIORITY) 43 | .addClassAndMethod(this) 44 | .addSourceLine(this)); 45 | } 46 | } 47 | 48 | private boolean isAConcatenationOfStrings() { 49 | final Item stackItem = stack.getStackItem(0); 50 | // check if the param is an undefined String 51 | return "Ljava/lang/String;".equals(stackItem.getSignature()) && stackItem.getConstant() == null 52 | // if the current string is not defined and is in a concatenation of strings, 53 | // then the compiler use an StringBuilder to put together those objects 54 | && "Ljava/lang/StringBuilder;".equals(stack.getStackItem(1).getSignature()); 55 | } 56 | 57 | private boolean isSameTypeArgumentAndClassOperand(@Nonnull final String classConstantOperand, 58 | @Nonnull final String argument) { 59 | return CLASSNAME_SIGNATURE_MAP.containsKey(classConstantOperand) 60 | && argument.equals(CLASSNAME_SIGNATURE_MAP.get(classConstantOperand)); 61 | } 62 | } -------------------------------------------------------------------------------- /src/main/java/jp/co/worksap/oss/findbugs/ForbiddenSystemClass.java: -------------------------------------------------------------------------------- 1 | package jp.co.worksap.oss.findbugs; 2 | 3 | import javax.annotation.Nonnull; 4 | 5 | import org.apache.bcel.classfile.Code; 6 | 7 | import edu.umd.cs.findbugs.BugInstance; 8 | import edu.umd.cs.findbugs.BugReporter; 9 | import edu.umd.cs.findbugs.bcel.OpcodeStackDetector; 10 | 11 | /** 12 | * ForbiddenSystemClass detects uses of System.out and System.err, 13 | * logger classes should be used instead. 14 | */ 15 | public class ForbiddenSystemClass extends OpcodeStackDetector { 16 | private BugReporter bugReporter; 17 | 18 | /** 19 | * Creates a new ForbiddenSystemClass detector. 20 | * @param bugReporter the bug reporter to use. 21 | */ 22 | public ForbiddenSystemClass(@Nonnull final BugReporter bugReporter) { 23 | this.bugReporter = bugReporter; 24 | } 25 | 26 | @Override 27 | public void visit(final Code obj) { 28 | super.visit(obj); 29 | } 30 | 31 | @Override 32 | public void sawOpcode(final int seen) { 33 | if (seen == GETSTATIC) { 34 | if ("java/lang/System".equals(getClassConstantOperand()) 35 | && "out".equals(getNameConstantOperand()) 36 | || "err".equals(getNameConstantOperand())) { 37 | final BugInstance bug = new BugInstance(this, "FORBIDDEN_SYSTEM", NORMAL_PRIORITY) 38 | .addClassAndMethod(this).addSourceLine(this, getPC()); 39 | bugReporter.reportBug(bug); 40 | } 41 | } 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/jp/co/worksap/oss/findbugs/findbugs/UndocumentedSuppressFBWarningsDetector.java: -------------------------------------------------------------------------------- 1 | package jp.co.worksap.oss.findbugs.findbugs; 2 | 3 | import static com.google.common.base.Preconditions.checkNotNull; 4 | 5 | import java.util.Collections; 6 | import java.util.Map; 7 | import java.util.Set; 8 | 9 | import javax.annotation.Nonnull; 10 | 11 | import org.apache.bcel.classfile.ElementValue; 12 | 13 | import com.google.common.collect.Sets; 14 | 15 | import edu.umd.cs.findbugs.BugInstance; 16 | import edu.umd.cs.findbugs.BugReporter; 17 | import edu.umd.cs.findbugs.BytecodeScanningDetector; 18 | import edu.umd.cs.findbugs.internalAnnotations.DottedClassName; 19 | 20 | /** 21 | *

A detector to ensure that FindBugs' SuppressWarnings annotation has justification.

22 | * @see edu.umd.cs.findbugs.annotations.SuppressWarnings 23 | * @see edu.umd.cs.findbugs.annotations.SuppressFBWarnings 24 | * @author Kengo TODA (toda_k@worksap.co.jp) 25 | */ 26 | public class UndocumentedSuppressFBWarningsDetector extends BytecodeScanningDetector { 27 | private static final Set TARGET_ANNOTATIONS = Collections.unmodifiableSet(Sets.newHashSet( 28 | "edu.umd.cs.findbugs.annotations.SuppressWarnings", 29 | "edu.umd.cs.findbugs.annotations.SuppressFBWarnings" 30 | )); 31 | 32 | @Nonnull 33 | private final BugReporter bugReporter; 34 | 35 | /** 36 | * Creates a new UndocumentedSupressFBWarningDetector 37 | * @param bugReporter the Bug Reporter to use. 38 | */ 39 | public UndocumentedSuppressFBWarningsDetector(@Nonnull final BugReporter bugReporter) { 40 | this.bugReporter = bugReporter; 41 | } 42 | 43 | @Override 44 | public void visitAnnotation(@DottedClassName String annotationClass, 45 | Map map, boolean runtimeVisible) { 46 | if (!TARGET_ANNOTATIONS.contains(annotationClass)) { 47 | return; 48 | } 49 | 50 | final ElementValue reason = map.get("justification"); 51 | if (reason == null || reason.stringifyValue().trim().isEmpty()) { 52 | BugInstance bugInstance = new BugInstance("FINDBUGS_UNDOCUMENTED_SUPPRESS_WARNINGS", 53 | HIGH_PRIORITY).addClass(this); 54 | if (visitingMethod()) { 55 | bugInstance.addMethod(this).addSourceLine(this); 56 | } 57 | bugReporter.reportBug(bugInstance); 58 | } 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/jp/co/worksap/oss/findbugs/guava/UnexpectedAccessDetector.java: -------------------------------------------------------------------------------- 1 | package jp.co.worksap.oss.findbugs.guava; 2 | 3 | import static com.google.common.base.Preconditions.checkNotNull; 4 | 5 | import java.util.List; 6 | 7 | import javax.annotation.Nonnull; 8 | 9 | import edu.umd.cs.findbugs.BugInstance; 10 | import edu.umd.cs.findbugs.BugReporter; 11 | import edu.umd.cs.findbugs.BytecodeScanningDetector; 12 | import edu.umd.cs.findbugs.ba.ClassContext; 13 | import edu.umd.cs.findbugs.ba.Hierarchy; 14 | import edu.umd.cs.findbugs.ba.XClass; 15 | import edu.umd.cs.findbugs.ba.XMethod; 16 | import edu.umd.cs.findbugs.classfile.ClassDescriptor; 17 | import edu.umd.cs.findbugs.classfile.analysis.AnnotationValue; 18 | 19 | /** 20 | *

21 | * A detector to ensure that implementation (class in src/main/java) doesn't 22 | * call package-private method in other class which is annotated by 23 | * {@code @VisibleForTesting}. 24 | *

25 | * 26 | * @author Kengo TODA (toda_k@worksap.co.jp) 27 | * @see com.google.common.annotations.VisibleForTesting 28 | */ 29 | public class UnexpectedAccessDetector extends BytecodeScanningDetector { 30 | @Nonnull 31 | private final BugReporter bugReporter; 32 | 33 | /** 34 | * @param bugReporter The BugReporter to be used. 35 | */ 36 | public UnexpectedAccessDetector(@Nonnull final BugReporter bugReporter) { 37 | this.bugReporter = checkNotNull(bugReporter); 38 | } 39 | 40 | @Override 41 | public void visitClassContext(@Nonnull final ClassContext classContext) { 42 | final XClass currentClass = classContext.getXClass(); 43 | 44 | try { 45 | if (Hierarchy.isSubtype(currentClass.getClassDescriptor() 46 | .getDottedClassName(), "junit.framework.TestCase")) { 47 | // no need to check, because method is called by JUnit's 3 48 | // TestCase method 49 | return; 50 | } 51 | 52 | // Check if this is a JUnit 4 test class 53 | final List xMethods = currentClass.getXMethods(); 54 | for (final XMethod xm : xMethods) { 55 | for (final ClassDescriptor acd : xm.getAnnotationDescriptors()) { 56 | if (acd.getDottedClassName().startsWith("org.junit.")) { 57 | return; 58 | } 59 | } 60 | } 61 | 62 | // We want to check this class' opcodes 63 | super.visitClassContext(classContext); 64 | } catch (final ClassNotFoundException e) { 65 | bugReporter.reportMissingClass(e); 66 | } 67 | } 68 | 69 | @Override 70 | public void sawOpcode(final int opcode) { 71 | if (!isInvoking(opcode)) { 72 | return; 73 | } 74 | 75 | final ClassDescriptor currentClass = getClassDescriptor(); 76 | final ClassDescriptor invokedClass = getClassDescriptorOperand(); 77 | 78 | if (currentClass.equals(invokedClass)) { 79 | // no need to check, because method is called by owner 80 | return; 81 | } 82 | 83 | if (!currentClass.getPackageName().equals( 84 | invokedClass.getPackageName())) { 85 | // no need to check, because method is called by class in other 86 | // package 87 | return; 88 | } 89 | 90 | final XMethod invokedMethod = getXMethodOperand(); 91 | if (invokedMethod != null) { 92 | verifyVisibility(invokedMethod); 93 | } 94 | } 95 | 96 | /** 97 | *

98 | * Report if specified method is package-private and annotated by 99 | * {@code @VisibleForTesting}. 100 | *

101 | */ 102 | private void verifyVisibility(@Nonnull final XMethod invokedMethod) { 103 | if (checkVisibility(invokedMethod) && checkAnnotated(invokedMethod)) { 104 | final BugInstance bug = new BugInstance(this, 105 | "GUAVA_UNEXPECTED_ACCESS_TO_VISIBLE_FOR_TESTING", 106 | HIGH_PRIORITY); 107 | 108 | bug.addCalledMethod(this).addClassAndMethod(this) 109 | .addSourceLine(this); 110 | bugReporter.reportBug(bug); 111 | } 112 | } 113 | 114 | /** 115 | * @return true if visibility of specified method is package-private. 116 | */ 117 | private boolean checkVisibility(@Nonnull final XMethod bcelMethod) { 118 | return !(bcelMethod.isPrivate() || bcelMethod.isProtected() || bcelMethod 119 | .isPublic()); 120 | } 121 | 122 | /** 123 | * @return true if specified method is annotated by 124 | * {@code VisibleForTesting}. 125 | */ 126 | private boolean checkAnnotated(@Nonnull final XMethod bcelMethod) { 127 | for (final AnnotationValue annotation : bcelMethod.getAnnotations()) { 128 | final String type = annotation.getAnnotationClass().getSignature(); 129 | if ("Lcom/google/common/annotations/VisibleForTesting;" 130 | .equals(type)) { 131 | return true; 132 | } 133 | } 134 | return false; 135 | } 136 | 137 | private boolean isInvoking(final int opcode) { 138 | return opcode == INVOKESPECIAL || opcode == INVOKEINTERFACE 139 | || opcode == INVOKESTATIC || opcode == INVOKEVIRTUAL; 140 | } 141 | 142 | } 143 | -------------------------------------------------------------------------------- /src/main/java/jp/co/worksap/oss/findbugs/jpa/AbstractColumnDetector.java: -------------------------------------------------------------------------------- 1 | package jp.co.worksap.oss.findbugs.jpa; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | 6 | import javax.annotation.CheckReturnValue; 7 | import javax.annotation.Nonnull; 8 | 9 | import org.apache.bcel.classfile.AnnotationEntry; 10 | import org.apache.bcel.classfile.ElementValue; 11 | import org.apache.bcel.classfile.Field; 12 | import org.apache.bcel.classfile.FieldOrMethod; 13 | import org.apache.bcel.generic.Type; 14 | 15 | import com.google.common.base.Objects; 16 | import com.google.common.collect.Lists; 17 | 18 | import edu.umd.cs.findbugs.BugReporter; 19 | import edu.umd.cs.findbugs.bcel.AnnotationDetector; 20 | import edu.umd.cs.findbugs.internalAnnotations.DottedClassName; 21 | 22 | abstract class AbstractColumnDetector extends AnnotationDetector { 23 | private final BugReporter bugReporter; 24 | 25 | /** 26 | * Creates a new AbstractColumnDetector. 27 | * @param bugReporter the Bug Reporter to use. 28 | */ 29 | AbstractColumnDetector(@Nonnull final BugReporter bugReporter) { 30 | this.bugReporter = bugReporter; 31 | } 32 | 33 | @Nonnull 34 | @CheckReturnValue 35 | protected final BugReporter getBugReporter() { 36 | return bugReporter; 37 | } 38 | 39 | @Override 40 | public final void visitAnnotation(@DottedClassName String annotationClass, 41 | Map map, boolean runtimeVisible) { 42 | if (!Objects.equal(annotationClass, "javax.persistence.Column")) { 43 | return; 44 | } 45 | 46 | Type columnType = findVisitingColumnType(); 47 | verifyColumn(columnType, map); 48 | } 49 | 50 | protected boolean isVisitingLob() { 51 | List targetListToSearch = Lists.newArrayList(); 52 | if (visitingField()) { 53 | targetListToSearch.add(getField()); 54 | } else if (visitingMethod()) { 55 | targetListToSearch.add(getMethod()); 56 | targetListToSearch.add(findFieldInVisitingMethod()); 57 | } else { 58 | throw new IllegalStateException("@Column should annotate field or method."); 59 | } 60 | 61 | for (FieldOrMethod targetToSearch : targetListToSearch) { 62 | for (AnnotationEntry annotation : targetToSearch.getAnnotationEntries()) { 63 | if (!Objects.equal(annotation.getAnnotationType(), "Ljavax/persistence/Lob;")) { 64 | continue; 65 | } 66 | return true; 67 | } 68 | } 69 | 70 | return false; 71 | } 72 | 73 | private @Nonnull Type findVisitingColumnType() { 74 | final Type columnType; 75 | if (visitingField()) { 76 | columnType = getField().getType(); 77 | } else if (visitingMethod()) { 78 | Field visitingField = findFieldInVisitingMethod(); 79 | columnType = visitingField.getType(); 80 | } else { 81 | throw new IllegalStateException("@Column should annotate field or method."); 82 | } 83 | return columnType; 84 | } 85 | 86 | @Nonnull 87 | private Field findFieldInVisitingMethod() { 88 | String fieldName = VisitedFieldFinder.findFieldWhichisVisitedInVisitingMethod(this); 89 | Field visitingField = null; 90 | for (Field field : getThisClass().getFields()) { 91 | if (Objects.equal(field.getName(), fieldName)) { 92 | visitingField = field; 93 | break; 94 | } 95 | } 96 | if (visitingField == null) { 97 | throw new IllegalStateException("Cannot find field which named as " + fieldName + "."); 98 | } 99 | return visitingField; 100 | } 101 | 102 | protected abstract void verifyColumn(@Nonnull Type columnType, @Nonnull Map elements); 103 | 104 | } 105 | -------------------------------------------------------------------------------- /src/main/java/jp/co/worksap/oss/findbugs/jpa/ColumnDefinitionDetector.java: -------------------------------------------------------------------------------- 1 | package jp.co.worksap.oss.findbugs.jpa; 2 | 3 | import java.util.Map; 4 | 5 | import javax.annotation.Nonnull; 6 | 7 | import org.apache.bcel.classfile.ElementValue; 8 | 9 | import com.google.common.base.Objects; 10 | 11 | import edu.umd.cs.findbugs.BugInstance; 12 | import edu.umd.cs.findbugs.BugReporter; 13 | import edu.umd.cs.findbugs.bcel.AnnotationDetector; 14 | import edu.umd.cs.findbugs.internalAnnotations.DottedClassName; 15 | 16 | /** 17 | *

A detector which finds columnDefinition property of Column annotation 18 | * which may break portability.

19 | * 20 | * @author Kengo TODA 21 | */ 22 | public class ColumnDefinitionDetector extends AnnotationDetector { 23 | 24 | private final BugReporter reporter; 25 | 26 | public ColumnDefinitionDetector(@Nonnull BugReporter reporter) { 27 | this.reporter = reporter; 28 | } 29 | 30 | @Override 31 | public void visitAnnotation(@DottedClassName String annotationClass, 32 | Map map, boolean runtimeVisible) { 33 | if (!Objects.equal(annotationClass, "javax.persistence.Column")) { 34 | return; 35 | } 36 | 37 | ElementValue columnDefinition = map.get("columnDefinition"); 38 | if (columnDefinition != null) { 39 | detectExistence(columnDefinition); 40 | } 41 | } 42 | 43 | private void detectExistence(@Nonnull ElementValue columnDefinition) { 44 | String value = columnDefinition.stringifyValue(); 45 | if (!value.isEmpty()) { 46 | reporter.reportBug(new BugInstance(this, "USE_COLUMN_DEFINITION", NORMAL_PRIORITY).addClass(this).addField(this)); 47 | } 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/jp/co/worksap/oss/findbugs/jpa/ImplicitLengthDetector.java: -------------------------------------------------------------------------------- 1 | package jp.co.worksap.oss.findbugs.jpa; 2 | 3 | import java.util.Map; 4 | 5 | import javax.annotation.Nonnull; 6 | 7 | import org.apache.bcel.classfile.ElementValue; 8 | import org.apache.bcel.generic.Type; 9 | 10 | import edu.umd.cs.findbugs.BugInstance; 11 | import edu.umd.cs.findbugs.BugReporter; 12 | 13 | public class ImplicitLengthDetector extends AbstractColumnDetector { 14 | /** 15 | * @see http://docs.oracle.com/cd/B28359_01/server.111/b28320/limits001.htm 16 | */ 17 | private static final int MAX_LENGTH_OF_ORACLE_VARCHAR = 4000; 18 | /** 19 | * @see http://www-01.ibm.com/support/knowledgecenter/SSEPEK_10.0.0/com.ibm.db2z10.doc.intro/src/tpc/db2z_stringdatatypes.htm 20 | */ 21 | private static final int MAX_LENGTH_OF_DB2_VARCHAR = 32704; 22 | 23 | private static final int MAX_LENGTH_OF_VARCHAR = Math.min(MAX_LENGTH_OF_ORACLE_VARCHAR, MAX_LENGTH_OF_DB2_VARCHAR); 24 | 25 | public ImplicitLengthDetector(final BugReporter bugReporter) { 26 | super(bugReporter); 27 | } 28 | 29 | @Override 30 | protected void verifyColumn(Type columnType, 31 | Map elements) { 32 | if (! isTarget(columnType)) { 33 | return; 34 | } 35 | 36 | if (! elements.containsKey("length")) { 37 | BugInstance bug = new BugInstance(this, "IMPLICIT_LENGTH", HIGH_PRIORITY).addClass(this); 38 | if (visitingMethod()) { 39 | bug.addMethod(this); 40 | } else if (visitingField()) { 41 | bug.addField(this); 42 | } 43 | getBugReporter().reportBug(bug); 44 | } else { 45 | ElementValue value = elements.get("length"); 46 | int lengthValue = Integer.parseInt(value.stringifyValue()); 47 | 48 | if (lengthValue <= 0) { 49 | reportIllegalLength(lengthValue); 50 | } else if (MAX_LENGTH_OF_VARCHAR < lengthValue && !isVisitingLob()) { 51 | reportIllegalLength(lengthValue); 52 | } 53 | } 54 | } 55 | 56 | private void reportIllegalLength(int lengthValue) { 57 | BugInstance bug = new BugInstance(this, "ILLEGAL_LENGTH", HIGH_PRIORITY).addClass(this); 58 | if (visitingMethod()) { 59 | bug.addMethod(this); 60 | } else if (visitingField()) { 61 | bug.addField(this); 62 | } 63 | getBugReporter().reportBug(bug); 64 | } 65 | 66 | /** 67 | * @return true if column type requires length property. 68 | */ 69 | private boolean isTarget(@Nonnull Type columnType) { 70 | return Type.STRING.equals(columnType) || Type.STRINGBUFFER.equals(columnType); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/jp/co/worksap/oss/findbugs/jpa/ImplicitNullnessDetector.java: -------------------------------------------------------------------------------- 1 | package jp.co.worksap.oss.findbugs.jpa; 2 | 3 | import java.util.Map; 4 | 5 | import org.apache.bcel.classfile.ElementValue; 6 | import org.apache.bcel.generic.Type; 7 | 8 | import edu.umd.cs.findbugs.BugInstance; 9 | import edu.umd.cs.findbugs.BugReporter; 10 | 11 | public class ImplicitNullnessDetector extends AbstractColumnDetector { 12 | 13 | public ImplicitNullnessDetector(BugReporter bugReporter) { 14 | super(bugReporter); 15 | } 16 | 17 | @Override 18 | protected void verifyColumn(Type columnType, 19 | Map elements) { 20 | if (! elements.containsKey("nullable")) { 21 | BugInstance bug = new BugInstance(this, "IMPLICIT_NULLNESS", HIGH_PRIORITY).addClass(this); 22 | if (visitingMethod()) { 23 | bug.addMethod(this); 24 | } else if (visitingField()) { 25 | bug.addField(this); 26 | } 27 | getBugReporter().reportBug(bug); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/jp/co/worksap/oss/findbugs/jpa/LongColumnNameDetector.java: -------------------------------------------------------------------------------- 1 | package jp.co.worksap.oss.findbugs.jpa; 2 | 3 | import java.util.Map; 4 | 5 | import javax.annotation.Nonnull; 6 | 7 | import org.apache.bcel.classfile.ElementValue; 8 | import org.apache.bcel.classfile.Method; 9 | import org.apache.commons.lang.IllegalClassException; 10 | 11 | import com.google.common.base.Objects; 12 | 13 | import edu.umd.cs.findbugs.BugInstance; 14 | import edu.umd.cs.findbugs.BugReporter; 15 | import edu.umd.cs.findbugs.bcel.AnnotationDetector; 16 | import edu.umd.cs.findbugs.internalAnnotations.DottedClassName; 17 | 18 | /** 19 | *

Detect column which has too long name. Note that {@code @Column} annotation should annotate FIELD. 20 | * Currently we do not support annotated METHOD. 21 | * 22 | * @author Kengo TODA 23 | */ 24 | public class LongColumnNameDetector extends AnnotationDetector { 25 | /** 26 | *

Oracle database limits the length of column name, and max length is {@code 30} bytes. 27 | * 28 | * @see http://docs.oracle.com/cd/B19306_01/server.102/b14200/sql_elements008.htm 29 | * @see http://stackoverflow.com/questions/1378133/why-are-oracle-table-column-index-names-limited-to-30-characters 30 | */ 31 | private static final int MAX_TABLE_LENGTH = 30; 32 | 33 | private final BugReporter bugReporter; 34 | 35 | public LongColumnNameDetector(@Nonnull BugReporter bugReporter) { 36 | this.bugReporter = bugReporter; 37 | } 38 | 39 | @Override 40 | public void visitAnnotation(@DottedClassName String annotationClass, 41 | Map map, boolean runtimeVisible) { 42 | if (!Objects.equal(annotationClass, "javax.persistence.Column")) { 43 | return; 44 | } 45 | ElementValue specifiedName = map.get("name"); 46 | final String columnName; 47 | if (specifiedName != null) { 48 | columnName = specifiedName.stringifyValue(); 49 | } else if (visitingField()){ 50 | columnName = getFieldName(); 51 | } else if (visitingMethod()) { 52 | Method targetMethod = getMethod(); 53 | columnName = VisitedFieldFinder.findFieldWhichisVisitedInVisitingMethod(this); 54 | if (columnName == null) { 55 | throw new IllegalClassException(String.format( 56 | "Method which is annotated with @Column should access to field, but %s#%s does not access.", 57 | getClassContext().getClassDescriptor().getClassName().replace('/', '.'), 58 | targetMethod.getName())); 59 | } 60 | } else { 61 | throw new IllegalClassException("@Column should annotate method or field."); 62 | } 63 | detectLongName(columnName); 64 | } 65 | 66 | private void detectLongName(@Nonnull String tableName) { 67 | if (tableName.length() > MAX_TABLE_LENGTH) { 68 | bugReporter.reportBug(new BugInstance(this, "LONG_COLUMN_NAME", 69 | HIGH_PRIORITY).addClass(this)); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/jp/co/worksap/oss/findbugs/jpa/LongIndexNameDetector.java: -------------------------------------------------------------------------------- 1 | package jp.co.worksap.oss.findbugs.jpa; 2 | 3 | import java.util.Map; 4 | 5 | import javax.annotation.Nonnull; 6 | 7 | import org.apache.bcel.classfile.ElementValue; 8 | 9 | import com.google.common.base.Objects; 10 | 11 | import edu.umd.cs.findbugs.BugInstance; 12 | import edu.umd.cs.findbugs.BugReporter; 13 | import edu.umd.cs.findbugs.bcel.AnnotationDetector; 14 | import edu.umd.cs.findbugs.internalAnnotations.DottedClassName; 15 | 16 | public class LongIndexNameDetector extends AnnotationDetector { 17 | /** 18 | *

Oracle database limits the length of index name, and max length is {@code 30} bytes. 19 | * 20 | * @see http://docs.oracle.com/cd/B19306_01/server.102/b14200/sql_elements008.htm 21 | * @see http://stackoverflow.com/questions/1378133/why-are-oracle-table-column-index-names-limited-to-30-characters 22 | */ 23 | private static final int MAX_INDEX_LENGTH = 30; 24 | private static final String PARAMETER_NAME_OF_HIBERNATE = "name"; 25 | private static final String PARAMETER_NAME_OF_OPENJPA = "name"; 26 | private final BugReporter bugReporter; 27 | 28 | public LongIndexNameDetector(@Nonnull BugReporter bugReporter) { 29 | this.bugReporter = bugReporter; 30 | } 31 | 32 | @Override 33 | public void visitAnnotation(@DottedClassName String annotationClass, 34 | Map map, boolean runtimeVisible) { 35 | if (visitingHibernateAnnotation(annotationClass)) { 36 | detectLongName(map, PARAMETER_NAME_OF_HIBERNATE); 37 | } else if (visitingOpenJPAAnnotation(annotationClass)) { 38 | detectLongName(map, PARAMETER_NAME_OF_OPENJPA); 39 | } 40 | } 41 | 42 | private boolean visitingOpenJPAAnnotation( 43 | @Nonnull @DottedClassName String annotationClass) { 44 | return Objects.equal(annotationClass, "org.apache.openjpa.persistence.jdbc.Index"); 45 | } 46 | 47 | private boolean visitingHibernateAnnotation( 48 | @Nonnull @DottedClassName String annotationClass) { 49 | return Objects.equal(annotationClass, "org.hibernate.annotations.Index"); 50 | } 51 | 52 | private void detectLongName(@Nonnull final Map map, 53 | @Nonnull final String parameterName) { 54 | final ElementValue indexName = map.get(parameterName); 55 | if (indexName != null 56 | && indexName.stringifyValue().length() > MAX_INDEX_LENGTH) { 57 | bugReporter.reportBug(new BugInstance(this, "LONG_INDEX_NAME", 58 | HIGH_PRIORITY).addClass(this).addField(this)); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/jp/co/worksap/oss/findbugs/jpa/LongTableNameDetector.java: -------------------------------------------------------------------------------- 1 | package jp.co.worksap.oss.findbugs.jpa; 2 | 3 | import java.util.Map; 4 | 5 | import javax.annotation.Nonnull; 6 | 7 | import org.apache.bcel.classfile.ElementValue; 8 | 9 | import com.google.common.annotations.VisibleForTesting; 10 | import com.google.common.base.Objects; 11 | 12 | import edu.umd.cs.findbugs.BugInstance; 13 | import edu.umd.cs.findbugs.BugReporter; 14 | import edu.umd.cs.findbugs.bcel.AnnotationDetector; 15 | import edu.umd.cs.findbugs.internalAnnotations.DottedClassName; 16 | import edu.umd.cs.findbugs.internalAnnotations.SlashedClassName; 17 | 18 | public class LongTableNameDetector extends AnnotationDetector { 19 | /** 20 | *

Oracle database limits the length of table name, and max length is {@code 30} bytes. 21 | * 22 | * @see http://docs.oracle.com/cd/B19306_01/server.102/b14200/sql_elements008.htm 23 | * @see http://stackoverflow.com/questions/1378133/why-are-oracle-table-column-index-names-limited-to-30-characters 24 | */ 25 | private static final int MAX_TABLE_LENGTH = 30; 26 | 27 | private final BugReporter bugReporter; 28 | 29 | public LongTableNameDetector(@Nonnull BugReporter bugReporter) { 30 | this.bugReporter = bugReporter; 31 | } 32 | 33 | @Override 34 | public void visitAnnotation(@DottedClassName String annotationClass, 35 | Map map, boolean runtimeVisible) { 36 | if (!Objects.equal(annotationClass, "javax.persistence.Entity")) { 37 | return; 38 | } 39 | ElementValue specifiedName = map.get("name"); 40 | if (specifiedName != null) { 41 | detectLongName(specifiedName.stringifyValue()); 42 | } else { 43 | String entityClassName = trimPackage(getClassName()); 44 | detectLongName(entityClassName); 45 | } 46 | } 47 | 48 | @VisibleForTesting 49 | @Nonnull String trimPackage(@Nonnull @SlashedClassName String className) { 50 | int index = className.lastIndexOf('/'); 51 | if (index < 0) { 52 | return className; 53 | } else { 54 | return className.substring(index + 1); 55 | } 56 | } 57 | 58 | private void detectLongName(@Nonnull String tableName) { 59 | if (tableName.length() > MAX_TABLE_LENGTH) { 60 | bugReporter.reportBug(new BugInstance(this, "LONG_TABLE_NAME", 61 | HIGH_PRIORITY).addClass(this)); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/jp/co/worksap/oss/findbugs/jpa/NullablePrimitiveDetector.java: -------------------------------------------------------------------------------- 1 | package jp.co.worksap.oss.findbugs.jpa; 2 | 3 | import java.util.Map; 4 | 5 | import javax.annotation.Nonnull; 6 | 7 | import org.apache.bcel.classfile.ElementValue; 8 | import org.apache.bcel.generic.ObjectType; 9 | import org.apache.bcel.generic.Type; 10 | 11 | import edu.umd.cs.findbugs.BugInstance; 12 | import edu.umd.cs.findbugs.BugReporter; 13 | 14 | public class NullablePrimitiveDetector extends AbstractColumnDetector { 15 | 16 | public NullablePrimitiveDetector(@Nonnull final BugReporter bugReporter) { 17 | super(bugReporter); 18 | } 19 | 20 | @Override 21 | protected void verifyColumn(Type columnType, 22 | Map elements) { 23 | if (! isPrimitive(columnType)) { 24 | return; 25 | } 26 | 27 | boolean isNullableColumn = detectNullability(elements); 28 | if (isNullableColumn) { 29 | reportNullablePrimitive(columnType); 30 | } 31 | } 32 | 33 | private void reportNullablePrimitive(@Nonnull final Type columnType) { 34 | BugInstance bug = new BugInstance(this, "NULLABLE_PRIMITIVE", NORMAL_PRIORITY) 35 | .addClass(this); 36 | if (visitingMethod()) { 37 | bug.addMethod(this); 38 | } else if (visitingField()) { 39 | bug.addField(this); 40 | } 41 | 42 | getBugReporter().reportBug(bug); 43 | } 44 | 45 | private boolean detectNullability(@Nonnull final Map elements) { 46 | if (! elements.containsKey("nullable")) { 47 | // in JPA 1.0 specification, default value of 'nullable' parameter is true 48 | // note that this case will be reported by ImplicitNullnessDetector. 49 | return true; 50 | } 51 | 52 | String nullability = elements.get("nullable").stringifyValue(); 53 | return "true".equals(nullability); 54 | } 55 | 56 | /** 57 | * Checks whether a given type is a primitive value or not. 58 | * 59 | * @param columnType the type of the column. 60 | * @return true if column type is primitive value (not reference type). 61 | */ 62 | private boolean isPrimitive(@Nonnull final Type columnType) { 63 | return ! (columnType instanceof ObjectType); // looks bad, but simple way to check primitive or not. 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/jp/co/worksap/oss/findbugs/jpa/VisitedFieldFinder.java: -------------------------------------------------------------------------------- 1 | package jp.co.worksap.oss.findbugs.jpa; 2 | 3 | import static com.google.common.base.Preconditions.checkNotNull; 4 | 5 | import javax.annotation.CheckReturnValue; 6 | import javax.annotation.Nonnull; 7 | import javax.annotation.Nullable; 8 | 9 | import org.apache.bcel.classfile.FieldOrMethod; 10 | import org.objectweb.asm.ClassReader; 11 | import org.objectweb.asm.ClassVisitor; 12 | import org.objectweb.asm.MethodVisitor; 13 | import org.objectweb.asm.Opcodes; 14 | 15 | import edu.umd.cs.findbugs.bcel.AnnotationDetector; 16 | 17 | /** 18 | *

19 | * Simple ClassVisitor implementation to find visited field in the specified 20 | * method. 21 | *

22 | *

23 | * To create instance, you need to provide name and descriptor to specify the 24 | * target method. 25 | *

26 | * 27 | * @author Kengo TODA 28 | */ 29 | final class VisitedFieldFinder extends ClassVisitor { 30 | private final class MethodVisitorExtension extends MethodVisitor { 31 | private MethodVisitorExtension(final int api) { 32 | super(api); 33 | } 34 | 35 | @Override 36 | public void visitFieldInsn(final int opcode, final String owner, final String name, final String desc) { 37 | visitedFieldName = name; 38 | // super.visitFieldInsn(opcode, owner, name, desc); 39 | } 40 | } 41 | 42 | private static final int API_VERSION = Opcodes.ASM5; 43 | private final String targetMethodName; 44 | private final String targetMethodDescriptor; 45 | 46 | private String visitedFieldName; 47 | 48 | VisitedFieldFinder(@Nonnull final String targetMethodName, @Nonnull final String targetMethodDescriptor) { 49 | super(API_VERSION); 50 | this.targetMethodName = checkNotNull(targetMethodName); 51 | this.targetMethodDescriptor = checkNotNull(targetMethodDescriptor); 52 | } 53 | 54 | @Override 55 | public String toString() { 56 | return "VisitedFieldFinder [targetMethodName=" + targetMethodName + ", targetMethodDescriptor=" 57 | + targetMethodDescriptor + ", visitedFieldName=" + visitedFieldName + "]"; 58 | } 59 | 60 | @CheckReturnValue 61 | @Nullable 62 | private String getVisitedFieldName() { 63 | return visitedFieldName; 64 | } 65 | 66 | @Override 67 | public MethodVisitor visitMethod(final int access, final String name, final String descriptor, 68 | final String signature, final String[] exceptions) { 69 | if (name.equals(targetMethodName) && descriptor.equals(targetMethodDescriptor)) { 70 | return new MethodVisitorExtension(API_VERSION); 71 | } else { 72 | // We do not have to analyze this method. 73 | // Returning null let ASM skip parsing this method. 74 | return null; 75 | } 76 | } 77 | 78 | // @Override 79 | // public FieldVisitor visitField(int access, String name, String desc, 80 | // String signature, Object value) { 81 | // return null; 82 | // 83 | // } 84 | 85 | // @Override 86 | // public void visitFieldInsn(int code, String owner, String name, String 87 | // description) { 88 | // visitedFieldName = name; 89 | // } 90 | 91 | @Nullable 92 | @CheckReturnValue 93 | static String findFieldWhichisVisitedInVisitingMethod(@Nonnull final AnnotationDetector detector) { 94 | final byte[] classByteCode = detector.getClassContext().getJavaClass().getBytes(); 95 | final ClassReader reader = new ClassReader(classByteCode); 96 | 97 | final FieldOrMethod targetMethod = detector.getMethod(); 98 | // note: bcel's #getSignature() method returns String like "(J)V", this 99 | // is named as "descriptor" in the context of ASM. 100 | // This is the reason why we call `targetMethod.getSignature()` to get 101 | // value for `targetMethodDescriptor` argument. 102 | final VisitedFieldFinder visitedFieldFinder = new VisitedFieldFinder(targetMethod.getName(), 103 | targetMethod.getSignature()); 104 | reader.accept(visitedFieldFinder, 0); 105 | return visitedFieldFinder.getVisitedFieldName(); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/jp/co/worksap/oss/findbugs/jsr305/BrokenImmutableClassDetector.java: -------------------------------------------------------------------------------- 1 | package jp.co.worksap.oss.findbugs.jsr305; 2 | 3 | import java.util.Map; 4 | 5 | import javax.annotation.Nonnull; 6 | import javax.annotation.Nullable; 7 | 8 | import org.apache.bcel.classfile.ElementValue; 9 | import org.apache.bcel.classfile.Field; 10 | import org.apache.bcel.classfile.JavaClass; 11 | 12 | import com.google.common.base.Objects; 13 | 14 | import edu.umd.cs.findbugs.BugInstance; 15 | import edu.umd.cs.findbugs.BugReporter; 16 | import edu.umd.cs.findbugs.bcel.AnnotationDetector; 17 | import edu.umd.cs.findbugs.internalAnnotations.DottedClassName; 18 | 19 | /** 20 | *

Detector to check immutability of class.

21 | *

Note that this detector does not ensure that "constructor stores deep-copied instance" and 22 | * "getter returns deep-copied instance". Use EI_EXPOSE_REP and EI_EXPOSE_REP2 to check it.

23 | * 24 | * @see http://findbugs.sourceforge.net/bugDescriptions.html#EI_EXPOSE_REP 25 | * @see http://findbugs.sourceforge.net/bugDescriptions.html#EI_EXPOSE_REP2 26 | * @author Kengo TODA 27 | */ 28 | public class BrokenImmutableClassDetector extends AnnotationDetector { 29 | 30 | private final BugReporter reporter; 31 | 32 | /** 33 | * Create a new BrokenImmutableClassDetector. 34 | * @param reporter the Bug Reporter to use. 35 | */ 36 | public BrokenImmutableClassDetector(@Nonnull final BugReporter reporter) { 37 | this.reporter = reporter; 38 | } 39 | 40 | @Override 41 | public void visitAnnotation(@DottedClassName String annotationClass, 42 | Map map, boolean runtimeVisible) { 43 | if (!Objects.equal(annotationClass, "javax.annotation.concurrent.Immutable")) { 44 | return; 45 | } 46 | 47 | JavaClass targetClass = getThisClass(); 48 | if (!targetClass.isFinal()) { 49 | reporter.reportBug(new BugInstance(this, "IMMUTABLE_CLASS_SHOULD_BE_FINAL", HIGH_PRIORITY).addClass(this)); 50 | } 51 | 52 | try { 53 | checkImmutability(targetClass); 54 | } catch (ClassNotFoundException e) { 55 | throw new IllegalStateException("Cannot find super class of " + targetClass.getClassName() + ". Check classpath.", e); 56 | } 57 | } 58 | 59 | private void checkImmutability(@Nullable final JavaClass immutableClass) throws ClassNotFoundException { 60 | if (immutableClass == null) { 61 | return; 62 | } 63 | for (final Field field : immutableClass.getFields()) { 64 | if (!field.isStatic() && !field.isFinal()) { 65 | reporter.reportBug(new BugInstance(this, "BROKEN_IMMUTABILITY", HIGH_PRIORITY) 66 | .addClass(immutableClass) 67 | .addString(field.getName()) 68 | .addString(immutableClass.getClassName()) 69 | .addString(getThisClass().getClassName())); 70 | } 71 | } 72 | checkImmutability(immutableClass.getSuperClass()); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/jp/co/worksap/oss/findbugs/jsr305/nullness/GenericsData.java: -------------------------------------------------------------------------------- 1 | package jp.co.worksap.oss.findbugs.jsr305.nullness; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collections; 5 | import java.util.HashMap; 6 | import java.util.List; 7 | import java.util.Map; 8 | import java.util.regex.Matcher; 9 | import java.util.regex.Pattern; 10 | 11 | import edu.umd.cs.findbugs.ba.XClass; 12 | import edu.umd.cs.findbugs.classfile.CheckedAnalysisException; 13 | import edu.umd.cs.findbugs.classfile.ClassDescriptor; 14 | 15 | public final class GenericsData { 16 | private final Map declaredGenerics; 17 | private final Map> superclassesGenerics; 18 | private final GenericsData enclossingClassData; 19 | private static final Pattern GENERICS_REFERENCE_PATTERN = Pattern.compile("([<;][-+]?)T([^;]+)(?=;)"); 20 | 21 | public GenericsData(final XClass clazz) throws CheckedAnalysisException { 22 | this(clazz, Collections.emptyList()); 23 | } 24 | 25 | public GenericsData(final XClass clazz, final List childBoundGenerics) throws CheckedAnalysisException { 26 | final String signature = clazz.getSourceSignature(); 27 | 28 | // Anonymous inner classes 29 | if (signature == null) { 30 | declaredGenerics = Collections.emptyMap(); 31 | superclassesGenerics = Collections.emptyMap(); 32 | enclossingClassData = null; 33 | return; 34 | } 35 | 36 | final ClassDescriptor enclosingClass = clazz.getImmediateEnclosingClass(); 37 | if (enclosingClass != null) { 38 | enclossingClassData = new GenericsData(enclosingClass.getXClass()); 39 | } else { 40 | enclossingClassData = null; 41 | } 42 | 43 | // Parse signature 44 | final Map declared = new HashMap(); 45 | final Map> superGenerics = new HashMap>(); 46 | int pos = 0; 47 | int lastConsumedPos = 0; 48 | 49 | // No declared generics 50 | if (signature.charAt(0) == '<') { 51 | int openGenerics = 1; 52 | pos++; 53 | lastConsumedPos++; 54 | 55 | while (openGenerics > 0) { 56 | switch (signature.charAt(pos++)) { 57 | case '<': 58 | openGenerics++; 59 | break; 60 | 61 | case '>': 62 | openGenerics--; 63 | break; 64 | 65 | case ';': 66 | if (openGenerics == 1) { 67 | final String generic[] = signature.substring(lastConsumedPos, pos - 1).split(":"); 68 | final String genericValue; 69 | 70 | if (!childBoundGenerics.isEmpty() && childBoundGenerics.size() >= declared.size()) { 71 | genericValue = childBoundGenerics.get(declared.size()); 72 | } else { 73 | genericValue = generic[1]; 74 | } 75 | 76 | declared.put(generic[0], genericValue); 77 | lastConsumedPos = pos; 78 | } 79 | } 80 | } 81 | 82 | lastConsumedPos = pos; 83 | } 84 | 85 | // From here on, we have "extends", and we are possibly binding generics 86 | while (pos < signature.length()) { 87 | switch (signature.charAt(pos++)) { 88 | case ';': 89 | // this class defines no generics, skip it 90 | lastConsumedPos = pos; 91 | break; 92 | 93 | case '<': 94 | final String className = signature.substring(lastConsumedPos, pos - 1); 95 | final List genericsList = new ArrayList(); 96 | lastConsumedPos = pos; 97 | int openGenerics = 1; 98 | 99 | while (openGenerics > 0) { 100 | switch (signature.charAt(pos++)) { 101 | case '<': 102 | openGenerics++; 103 | break; 104 | 105 | case '>': 106 | openGenerics--; 107 | break; 108 | 109 | case ';': 110 | if (openGenerics == 1) { 111 | genericsList.add(signature.substring(lastConsumedPos, pos - 1)); 112 | lastConsumedPos = pos; 113 | } 114 | } 115 | } 116 | 117 | superGenerics.put(className, genericsList); 118 | 119 | break; 120 | } 121 | } 122 | 123 | 124 | declaredGenerics = Collections.unmodifiableMap(declared); 125 | superclassesGenerics = Collections.unmodifiableMap(superGenerics); 126 | } 127 | 128 | private final String keyForSuperclass(final ClassDescriptor cd) { 129 | return "L" + cd.getClassName(); 130 | } 131 | 132 | public List getMappedSuperClassdata(final ClassDescriptor cd) { 133 | final List list = superclassesGenerics.get(keyForSuperclass(cd)); 134 | if (list == null) { 135 | return Collections.emptyList(); 136 | } 137 | 138 | final List ret = new ArrayList(list.size()); 139 | 140 | for (final String val : list) { 141 | if (val.charAt(0) == 'T') { 142 | final String genericName = val.substring(1); 143 | final String boundValue = declaredGenerics.get(genericName); 144 | if (boundValue != null) { 145 | ret.add(boundValue); 146 | } else { 147 | ret.add(getInheritedValue(genericName)); 148 | } 149 | } else { 150 | // Has this value any references to generics such as Ljava/util/List ?? 151 | final Matcher matcher = GENERICS_REFERENCE_PATTERN.matcher(val); 152 | String boundValue = val; 153 | while (matcher.find()) { 154 | boundValue = boundValue.replaceAll(Pattern.quote(matcher.group(1)) + "T" + matcher.group(2) + ";", 155 | Matcher.quoteReplacement(matcher.group(1)) + declaredGenerics.get(matcher.group(2)) + ";"); 156 | } 157 | ret.add(boundValue); 158 | } 159 | } 160 | 161 | return Collections.unmodifiableList(ret); 162 | } 163 | 164 | private String getInheritedValue(final String boundValue) { 165 | if (enclossingClassData == null) { 166 | return "Ljava/lang/Object"; // not really cool, but a good default 167 | } 168 | 169 | if (enclossingClassData.declaredGenerics.containsKey(boundValue)) { 170 | return enclossingClassData.declaredGenerics.get(boundValue); 171 | } 172 | 173 | // Keep looking up! 174 | return enclossingClassData.getInheritedValue(boundValue); 175 | } 176 | 177 | public Map getDeclaredGenerics() { 178 | return declaredGenerics; 179 | } 180 | 181 | @Override 182 | public String toString() { 183 | return "GenericsData [declaredGenerics=" + declaredGenerics 184 | + ", superclassesGenerics=" + superclassesGenerics + "]"; 185 | } 186 | } -------------------------------------------------------------------------------- /src/main/java/jp/co/worksap/oss/findbugs/jsr305/nullness/UnknownNullnessDetector.java: -------------------------------------------------------------------------------- 1 | package jp.co.worksap.oss.findbugs.jsr305.nullness; 2 | 3 | 4 | import java.util.HashSet; 5 | import java.util.Map.Entry; 6 | import java.util.Set; 7 | import java.util.regex.Matcher; 8 | import java.util.regex.Pattern; 9 | 10 | import javax.annotation.Nonnull; 11 | import javax.annotation.Nullable; 12 | 13 | import org.apache.bcel.classfile.Method; 14 | import org.apache.bcel.generic.ReferenceType; 15 | import org.apache.bcel.generic.Type; 16 | 17 | import edu.umd.cs.findbugs.BugInstance; 18 | import edu.umd.cs.findbugs.BugReporter; 19 | import edu.umd.cs.findbugs.BytecodeScanningDetector; 20 | import edu.umd.cs.findbugs.ba.XClass; 21 | import edu.umd.cs.findbugs.ba.XMethod; 22 | import edu.umd.cs.findbugs.ba.jsr305.JSR305NullnessAnnotations; 23 | import edu.umd.cs.findbugs.ba.jsr305.TypeQualifierAnnotation; 24 | import edu.umd.cs.findbugs.ba.jsr305.TypeQualifierApplications; 25 | import edu.umd.cs.findbugs.ba.jsr305.TypeQualifierValue; 26 | import edu.umd.cs.findbugs.classfile.CheckedAnalysisException; 27 | import edu.umd.cs.findbugs.classfile.ClassDescriptor; 28 | import edu.umd.cs.findbugs.classfile.Global; 29 | 30 | public class UnknownNullnessDetector extends BytecodeScanningDetector { 31 | 32 | private static final TypeQualifierValue NULLNESS_QUALIFIER 33 | = TypeQualifierValue.getValue(JSR305NullnessAnnotations.NONNULL, null); 34 | 35 | private static final Pattern ANONYMOUS_CLASSNAME_PATTERN = Pattern.compile("\\$[0-9]+$"); 36 | 37 | private final BugReporter bugReporter; 38 | 39 | /** 40 | * Creates a new UnknownNullnessDetector. 41 | * @param bugReporter the bug reporter to use. 42 | */ 43 | public UnknownNullnessDetector(@Nonnull final BugReporter bugReporter) { 44 | this.bugReporter = bugReporter; 45 | } 46 | 47 | @Override 48 | public void visit(final Method method) { 49 | final XMethod xMethod = getXMethod(); 50 | if (xMethod.isSynthetic()) { 51 | // Ignore methods not created by the developer himself 52 | return; 53 | } 54 | 55 | // Ignore constructors for anonymous classes, they can't be declared / overridden 56 | if ("".equals(xMethod.getName())) { 57 | final Matcher matcher = ANONYMOUS_CLASSNAME_PATTERN.matcher(getClassDescriptor().getClassName()); 58 | if (matcher.find()) { 59 | return; 60 | } 61 | } 62 | 63 | // Enums have several false positives we need to ignore... 64 | if (isEnumIgnoredMethod(xMethod)) { 65 | return; 66 | } 67 | 68 | /* 69 | * Ignore inherited methods.. Nullness should be declared upstream 70 | * This also prevents us from reporting on methods whose expected nullness we don't control, 71 | * such as Object.equals and List.add. 72 | */ 73 | if (findSuperMethods(xMethod).isEmpty()) { 74 | // Make sure our own annotations are in place 75 | detectUnknownNullnessOfParameter(method, NULLNESS_QUALIFIER); 76 | detectUnknowNullnessOfReturnedValue(method, NULLNESS_QUALIFIER); 77 | } 78 | } 79 | 80 | private boolean isEnumIgnoredMethod(@Nonnull final XMethod xMethod) { 81 | boolean checkForEnum = false; 82 | final String methodName = xMethod.getName(); 83 | final String signature = xMethod.getSignature(); 84 | 85 | // public static CCC[] values() 86 | if ("values".equals(methodName) && xMethod.isStatic() && xMethod.getNumParams() == 0) { 87 | checkForEnum = true; 88 | } 89 | 90 | // public static CCC valueOf(String) 91 | if ("valueOf".equals(methodName) && xMethod.isStatic() 92 | && signature.equals("(Ljava/lang/String;)L" + xMethod.getClassDescriptor().getClassName() + ";")) { 93 | checkForEnum = true; 94 | } 95 | 96 | if (checkForEnum) { 97 | return isCurrentClassAnEnum(); 98 | } 99 | 100 | return false; 101 | } 102 | 103 | private boolean isCurrentClassAnEnum() { 104 | // enums can't be put into hierarchies, they must extend java.lang.Enum directly 105 | 106 | final XClass xclass = getXClass(); 107 | if (xclass == null) { 108 | return false; 109 | } 110 | final ClassDescriptor superCD = xclass.getSuperclassDescriptor(); 111 | if ("java.lang.Enum".equals(superCD.getDottedClassName())) { 112 | return true; 113 | } 114 | 115 | return false; 116 | } 117 | 118 | private void detectUnknownNullnessOfParameter(@Nonnull final Method method, 119 | @Nonnull final TypeQualifierValue nullness) { 120 | Type[] argumentTypes = method.getArgumentTypes(); 121 | int initialIndex = 0; 122 | 123 | if ("".equals(method.getName())) { 124 | if (method.getSignature().startsWith("(Ljava/lang/String;I")) { 125 | // This may be an enum, in which case the first arg is inherited and can't be checked 126 | if (isCurrentClassAnEnum()) { 127 | initialIndex = 2; 128 | } 129 | } else if (!getXClass().isStatic() && method.getSignature() 130 | .startsWith("(L" + getXClass().getContainingScope().getClassDescriptor().getClassName() + ";")) { 131 | initialIndex = 1; 132 | } 133 | } 134 | 135 | for (int i = initialIndex; i < argumentTypes.length; ++i) { 136 | if (!(argumentTypes[i] instanceof ReferenceType) 137 | || getXMethod().isVariableSynthetic(i) 138 | || (i == argumentTypes.length - 1 && getXMethod().isVarArgs())) { 139 | continue; 140 | } 141 | 142 | TypeQualifierAnnotation annotation = TypeQualifierApplications.getEffectiveTypeQualifierAnnotation( 143 | getXMethod(), i, nullness); 144 | if (annotation == null) { 145 | bugReporter.reportBug(new BugInstance("UNKNOWN_NULLNESS_OF_PARAMETER", NORMAL_PRIORITY) 146 | .addClassAndMethod(this)); 147 | } 148 | } 149 | } 150 | 151 | @Nonnull 152 | private static Set findSuperMethods(@Nonnull final XMethod m) { 153 | /* 154 | We can't use {@code Hierarchy2.findMatchingMethod} since it considers return types, which is incorrect 155 | for Java, and just doesn't work with generics. 156 | */ 157 | final Set result = new HashSet(); 158 | 159 | final ClassDescriptor c = m.getClassDescriptor(); 160 | if (c != null) { 161 | try { 162 | final XClass xc = Global.getAnalysisCache().getClassAnalysis(XClass.class, c); 163 | final GenericsData gd = new GenericsData(xc); 164 | findSuperMethods(c, m, result, gd, gd); 165 | result.remove(m); 166 | } catch (final Throwable e) { 167 | // ignore it 168 | } 169 | } 170 | return result; 171 | } 172 | 173 | private static void findSuperMethods(@Nullable final ClassDescriptor c, @Nonnull final XMethod m, 174 | @Nonnull final Set accumulator, @Nonnull final GenericsData childGenericsData, 175 | @Nonnull final GenericsData originalGenericsData) { 176 | if (c == null) { 177 | return; 178 | } 179 | 180 | try { 181 | final XClass xc = Global.getAnalysisCache().getClassAnalysis(XClass.class, c); 182 | final GenericsData gd = new GenericsData(xc, childGenericsData.getMappedSuperClassdata(c)); 183 | 184 | for (final XMethod xm : xc.getXMethods()) { 185 | if (xm.isStatic() == m.isStatic() && xm.getName().equals(m.getName()) 186 | && signaturesMatches(xm, m, gd, originalGenericsData)) { 187 | if (accumulator.add(xm)) { 188 | // Found a match, we are done here 189 | break; 190 | } else { 191 | // We have alerady visited this class on another path 192 | return; 193 | } 194 | } 195 | } 196 | 197 | findSuperMethods(xc.getSuperclassDescriptor(), m, accumulator, gd, originalGenericsData); 198 | for (final ClassDescriptor i : xc.getInterfaceDescriptorList()) { 199 | findSuperMethods(i, m, accumulator, gd, originalGenericsData); 200 | } 201 | 202 | accumulator.add(m); 203 | } catch (final CheckedAnalysisException e) { 204 | // nothing to do 205 | } 206 | } 207 | 208 | private static boolean signaturesMatches(@Nonnull final XMethod superm, @Nonnull final XMethod m, 209 | @Nonnull final GenericsData gd, @Nonnull final GenericsData ogd) { 210 | // Are there generics? 211 | String signature = superm.getSourceSignature(); 212 | if (signature == null) { 213 | return getArgumentSignature(superm).equals(getArgumentSignature(m)); 214 | } 215 | 216 | final String actualSignature = m.getSourceSignature() == null ? m.getSignature() : m.getSourceSignature(); 217 | 218 | // Replace all generics 219 | return replaceGenericsInSignature(gd, signature).equals(replaceGenericsInSignature(ogd, actualSignature)); 220 | } 221 | 222 | private static String replaceGenericsInSignature(final GenericsData gd, final String signature) { 223 | String s = signature; 224 | for (final Entry entry : gd.getDeclaredGenerics().entrySet()) { 225 | s = s.replaceAll("T" + Pattern.quote(entry.getKey()) + ";", Matcher.quoteReplacement(entry.getValue()) + ";"); 226 | } 227 | return s; 228 | } 229 | 230 | @Nonnull 231 | private static String getArgumentSignature(@Nonnull final XMethod xm) { 232 | final String signature = xm.getSignature(); 233 | return signature.substring(0, signature.indexOf(')') + 1); 234 | } 235 | 236 | private void detectUnknowNullnessOfReturnedValue(@Nonnull final Method method, 237 | @Nonnull final TypeQualifierValue nullness) { 238 | if (!(method.getReturnType() instanceof ReferenceType)) { 239 | return; 240 | } 241 | 242 | final TypeQualifierAnnotation annotation = TypeQualifierApplications.getEffectiveTypeQualifierAnnotation( 243 | getXMethod(), nullness); 244 | if (annotation == null) { 245 | bugReporter.reportBug(new BugInstance("UNKNOWN_NULLNESS_OF_RETURNED_VALUE", NORMAL_PRIORITY) 246 | .addClassAndMethod(this)); 247 | } 248 | } 249 | } 250 | -------------------------------------------------------------------------------- /src/main/java/jp/co/worksap/oss/findbugs/junit/UndocumentedIgnoreDetector.java: -------------------------------------------------------------------------------- 1 | package jp.co.worksap.oss.findbugs.junit; 2 | 3 | import java.util.Map; 4 | 5 | import javax.annotation.Nonnull; 6 | 7 | import org.apache.bcel.classfile.ElementValue; 8 | 9 | import edu.umd.cs.findbugs.BugInstance; 10 | import edu.umd.cs.findbugs.BugReporter; 11 | import edu.umd.cs.findbugs.BytecodeScanningDetector; 12 | import edu.umd.cs.findbugs.internalAnnotations.DottedClassName; 13 | 14 | /*** 15 | * UndocumentedIgnoreDetector detects if test cases are being ignored 16 | * without an explanation. 17 | */ 18 | public class UndocumentedIgnoreDetector extends BytecodeScanningDetector { 19 | 20 | private final BugReporter bugReporter; 21 | 22 | /*** 23 | * Creates an UndocumentedIgnoreDetector. 24 | * @param bugReporter the bug reporter to use. 25 | */ 26 | public UndocumentedIgnoreDetector(@Nonnull final BugReporter bugReporter) { 27 | this.bugReporter = bugReporter; 28 | } 29 | 30 | @Override 31 | public void visitAnnotation(@DottedClassName final String annotationClass, 32 | final Map map, final boolean runtimeVisible) { 33 | if (!"org.junit.Ignore".equals(annotationClass)) { 34 | return; 35 | } 36 | 37 | final ElementValue reason = map.get("value"); 38 | if (reason == null || reason.stringifyValue().trim().isEmpty()) { 39 | final BugInstance bugInstance = new BugInstance("UNDOCUMENTED_IGNORE", 40 | HIGH_PRIORITY).addClass(this); 41 | if (visitingMethod()) { 42 | bugInstance.addMethod(this).addSourceLine(this); 43 | } 44 | bugReporter.reportBug(bugInstance); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/resources/metadata/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | 3 | -------------------------------------------------------------------------------- /src/main/resources/metadata/bugrank.txt: -------------------------------------------------------------------------------- 1 | 0 BugPattern FORBIDDEN_SYSTEM 2 | 0 BugPattern IMMUTABLE_CLASS_SHOULD_BE_FINAL 3 | 0 BugPattern BROKEN_IMMUTABILITY 4 | 0 BugPattern LONG_INDEX_NAME 5 | 0 BugPattern LONG_TABLE_NAME 6 | 0 BugPattern LONG_COLUMN_NAME 7 | 0 BugPattern UNKNOWN_NULLNESS_OF_PARAMETER 8 | 0 BugPattern UNKNOWN_NULLNESS_OF_RETURNED_VALUE 9 | 0 BugPattern UNDOCUMENTED_IGNORE 10 | 0 BugPattern IMPLICIT_NULLNESS 11 | 0 BugPattern IMPLICIT_LENGTH 12 | 0 BugPattern ILLEGAL_LENGTH 13 | 0 BugPattern USE_COLUMN_DEFINITION 14 | 0 BugPattern NULLABLE_PRIMITIVE 15 | 0 BugPattern GUAVA_UNEXPECTED_ACCESS_TO_VISIBLE_FOR_TESTING 16 | 0 BugPattern FINDBUGS_UNDOCUMENTED_SUPPRESS_WARNINGS 17 | 0 BugPattern MISSING_FIELD_IN_TO_STRING 18 | 0 BugPattern MISSING_TO_STRING_OVERRIDE 19 | 0 BugPattern DONT_OVERRIDE_EQUALS 20 | -------------------------------------------------------------------------------- /src/main/resources/metadata/findbugs.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /src/test/java/com/monits/findbugs/effectivejava/EqualsOverrideDetectorTest.java: -------------------------------------------------------------------------------- 1 | package com.monits.findbugs.effectivejava; 2 | 3 | import static org.mockito.Mockito.never; 4 | import static org.mockito.Mockito.spy; 5 | import static org.mockito.Mockito.verify; 6 | 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | 10 | import com.h3xstream.findbugs.test.BaseDetectorTest; 11 | import com.h3xstream.findbugs.test.EasyBugReporter; 12 | 13 | public class EqualsOverrideDetectorTest extends BaseDetectorTest { 14 | 15 | private static final String DONT_OVERRIDE_EQUALS = "DONT_OVERRIDE_EQUALS"; 16 | 17 | private EasyBugReporter reporter; 18 | 19 | @Before 20 | public void setup() { 21 | reporter = spy(new EasyBugReporter()); 22 | } 23 | 24 | @Test 25 | public void testNoEqualsOverride() throws Exception { 26 | // Locate test code 27 | final String[] files = { 28 | getClassFilePath("samples/effectivejava/item8/BaseConcreteClass"), 29 | }; 30 | 31 | // Run the analysis 32 | analyze(files, reporter); 33 | 34 | verify(reporter, never()).doReportBug( 35 | bugDefinition() 36 | .bugType(DONT_OVERRIDE_EQUALS) 37 | .build() 38 | ); 39 | } 40 | 41 | @Test 42 | public void testGoodOverride() throws Exception { 43 | // Locate test code 44 | final String[] files = { 45 | getClassFilePath("samples/effectivejava/item8/BaseNonEqualsConcreteClass"), 46 | getClassFilePath("samples/effectivejava/item8/GoodEqualsOverride"), 47 | }; 48 | 49 | // Run the analysis 50 | analyze(files, reporter); 51 | 52 | verify(reporter, never()).doReportBug( 53 | bugDefinition() 54 | .bugType(DONT_OVERRIDE_EQUALS) 55 | .build() 56 | ); 57 | } 58 | 59 | @Test 60 | public void testBadOverride() throws Exception { 61 | // Locate test code 62 | final String[] files = { 63 | getClassFilePath("samples/effectivejava/item8/BaseConcreteClass"), 64 | getClassFilePath("samples/effectivejava/item8/BadEqualsOverride"), 65 | }; 66 | 67 | // Run the analysis 68 | analyze(files, reporter); 69 | 70 | verify(reporter).doReportBug( 71 | bugDefinition() 72 | .bugType(DONT_OVERRIDE_EQUALS) 73 | .inClass("BadEqualsOverride") 74 | .build() 75 | ); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/test/java/com/monits/findbugs/jdk/InconsistentHashCodeEqualsDetectorTest.java: -------------------------------------------------------------------------------- 1 | package com.monits.findbugs.jdk; 2 | 3 | import static org.mockito.Mockito.never; 4 | import static org.mockito.Mockito.spy; 5 | import static org.mockito.Mockito.verify; 6 | 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | 10 | import com.h3xstream.findbugs.test.BaseDetectorTest; 11 | import com.h3xstream.findbugs.test.EasyBugReporter; 12 | 13 | public class InconsistentHashCodeEqualsDetectorTest extends BaseDetectorTest { 14 | 15 | private EasyBugReporter reporter; 16 | 17 | @Before 18 | public void setup() { 19 | reporter = spy(new EasyBugReporter()); 20 | } 21 | 22 | @Test 23 | public void testNoEqualsHashCodeErrors() throws Exception { 24 | final String[] files = { 25 | // Good implementation should not raise any errors 26 | getClassFilePath("samples/findbugs/GoodEqualsHashCodeImplementation"), 27 | // Classes with one overridden method should not raise any errors 28 | getClassFilePath("samples/findbugs/OnlyEqualsImplementation"), 29 | // Classes with neither methods overridden should not raise any errors 30 | getClassFilePath("samples/findbugs/NoEqualsHashCode"), 31 | }; 32 | 33 | analyze(files, reporter); 34 | 35 | verify(reporter, never()).doReportBug(bugDefinition() 36 | .bugType("HASHCHODE_HAS_MORE_FIELDS_THAN_EQUALS") 37 | .build() 38 | ); 39 | 40 | verify(reporter, never()).doReportBug(bugDefinition() 41 | .bugType("EQUALS_HAS_MORE_FIELDS_THAN_HASHCODE") 42 | .build() 43 | ); 44 | } 45 | 46 | @Test 47 | public void testSubclassOfBadClassHasNoErrors() throws Exception { 48 | final String[] files = { 49 | getClassFilePath("samples/findbugs/HashCodeContainsEquals"), 50 | getClassFilePath("samples/findbugs/SubclassOfBadEqualsHashCode"), 51 | }; 52 | 53 | analyze(files, reporter); 54 | 55 | // subclass must not have hashcode error ... 56 | verify(reporter, never()).doReportBug( 57 | bugDefinition() 58 | .bugType("HASHCHODE_HAS_MORE_FIELDS_THAN_EQUALS") 59 | .inClass("samples.findbugs.SubclassOfBadEqualsHashCode") 60 | .build() 61 | ); 62 | 63 | // ... nor equals error 64 | verify(reporter, never()).doReportBug( 65 | bugDefinition() 66 | .bugType("EQUALS_HAS_MORE_FIELDS_THAN_HASHCODE") 67 | .inClass("samples.findbugs.SubclassOfBadEqualsHashCode") 68 | .build() 69 | ); 70 | 71 | // superclass will rise hashcode error 72 | verify(reporter).doReportBug( 73 | bugDefinition() 74 | .bugType("HASHCHODE_HAS_MORE_FIELDS_THAN_EQUALS") 75 | .inClass("samples.findbugs.HashCodeContainsEquals") 76 | .atField("version") 77 | .build() 78 | ); 79 | } 80 | 81 | @Test 82 | public void testHashCodeHasMoreFieldsThanEquals() throws Exception { 83 | final String[] files = { 84 | getClassFilePath("samples/findbugs/HashCodeContainsEquals"), 85 | }; 86 | 87 | analyze(files, reporter); 88 | 89 | verify(reporter).doReportBug( 90 | bugDefinition() 91 | .bugType("HASHCHODE_HAS_MORE_FIELDS_THAN_EQUALS") 92 | .inClass("samples.findbugs.HashCodeContainsEquals") 93 | .inMethod("hashCode") 94 | .atField("version") 95 | .build() 96 | ); 97 | } 98 | 99 | @Test 100 | public void testEqualsHasMoreFieldsThanHashCode() throws Exception { 101 | final String[] files = { 102 | getClassFilePath("samples/findbugs/EqualsContainsHashCode"), 103 | }; 104 | 105 | analyze(files, reporter); 106 | 107 | verify(reporter).doReportBug( 108 | bugDefinition() 109 | .bugType("EQUALS_HAS_MORE_FIELDS_THAN_HASHCODE") 110 | .inClass("samples.findbugs.EqualsContainsHashCode") 111 | .inMethod("equals") 112 | .atField("version") 113 | .build() 114 | ); 115 | } 116 | 117 | @Test 118 | public void testEqualsHashCodeDistinctFields() throws Exception { 119 | final String[] files = { 120 | getClassFilePath("samples/findbugs/EqualsHashCodeDifferentFields"), 121 | }; 122 | 123 | analyze(files, reporter); 124 | 125 | verify(reporter).doReportBug( 126 | bugDefinition() 127 | .bugType("HASHCHODE_HAS_MORE_FIELDS_THAN_EQUALS") 128 | .inClass("samples.findbugs.EqualsHashCodeDifferentFields") 129 | .inMethod("hashCode") 130 | .atField("version") 131 | .build() 132 | ); 133 | 134 | verify(reporter).doReportBug( 135 | bugDefinition() 136 | .bugType("EQUALS_HAS_MORE_FIELDS_THAN_HASHCODE") 137 | .inClass("samples.findbugs.EqualsHashCodeDifferentFields") 138 | .inMethod("equals") 139 | .atField("id") 140 | .build() 141 | ); 142 | } 143 | 144 | } 145 | -------------------------------------------------------------------------------- /src/test/java/com/monits/findbugs/jdk/PatternCompileTest.java: -------------------------------------------------------------------------------- 1 | package com.monits.findbugs.jdk; 2 | 3 | import static org.mockito.Mockito.never; 4 | import static org.mockito.Mockito.spy; 5 | import static org.mockito.Mockito.verify; 6 | 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | 10 | import com.h3xstream.findbugs.test.BaseDetectorTest; 11 | import com.h3xstream.findbugs.test.EasyBugReporter; 12 | 13 | public class PatternCompileTest extends BaseDetectorTest { 14 | 15 | private EasyBugReporter reporter; 16 | 17 | @Before 18 | public void setup() { 19 | reporter = spy(new EasyBugReporter()); 20 | } 21 | 22 | @Test 23 | public void testReportNonStaticPatternCompile() throws Exception { 24 | // Locate test code 25 | final String[] files = { 26 | getClassFilePath("samples/findbugs/jdk/patterncompile/NonStaticPatternCompile"), 27 | }; 28 | 29 | // Run the analysis 30 | analyze(files, reporter); 31 | 32 | verify(reporter).doReportBug( 33 | bugDefinition() 34 | .bugType(NonStaticPatternCompileDetector.NON_STATIC_PATTERN_COMPILE_CALL) 35 | .inClass("NonStaticPatternCompile") 36 | .inMethod("testReportNonStaticPatternCompile") 37 | .build() 38 | ); 39 | } 40 | 41 | @Test 42 | public void testReportNonStaticPatternCompileWithStaticRegex() throws Exception { 43 | // Locate test code 44 | final String[] files = { 45 | getClassFilePath("samples/findbugs/jdk/patterncompile/NonStaticPatternCompile"), 46 | }; 47 | 48 | // Run the analysis 49 | analyze(files, reporter); 50 | 51 | verify(reporter).doReportBug( 52 | bugDefinition() 53 | .bugType(NonStaticPatternCompileDetector.NON_STATIC_PATTERN_COMPILE_CALL) 54 | .inClass("NonStaticPatternCompile") 55 | .inMethod("testReportNonStaticPatternCompileWithStaticRegex") 56 | .build() 57 | ); 58 | } 59 | 60 | @Test 61 | public void testNeverReportNonStaticPatternCompileWithNonFinalLocalRegex() throws Exception { 62 | // Locate test code 63 | final String[] files = { 64 | getClassFilePath("samples/findbugs/jdk/patterncompile/NonStaticPatternCompile"), 65 | }; 66 | 67 | // Run the analysis 68 | analyze(files, reporter); 69 | 70 | verify(reporter, never()).doReportBug( 71 | bugDefinition() 72 | .bugType(NonStaticPatternCompileDetector.NON_STATIC_PATTERN_COMPILE_CALL) 73 | .inClass("NonStaticPatternCompile") 74 | .inMethod("testNeverReportNonStaticPatternCompileWithNonFinalLocalRegex") 75 | .build() 76 | ); 77 | } 78 | 79 | @Test 80 | public void testReportNonStaticPatternCompileWithFinalLocalRegex() throws Exception { 81 | // Locate test code 82 | final String[] files = { 83 | getClassFilePath("samples/findbugs/jdk/patterncompile/NonStaticPatternCompile"), 84 | }; 85 | 86 | // Run the analysis 87 | analyze(files, reporter); 88 | 89 | verify(reporter).doReportBug( 90 | bugDefinition() 91 | .bugType(NonStaticPatternCompileDetector.NON_STATIC_PATTERN_COMPILE_CALL) 92 | .inClass("NonStaticPatternCompile") 93 | .inMethod("testReportNonStaticPatternCompileWithFinalLocalRegex") 94 | .build() 95 | ); 96 | } 97 | 98 | @Test 99 | public void testNeverReportNonStaticPatternCompileWithFinalRegexParameter() throws Exception { 100 | // Locate test code 101 | final String[] files = { 102 | getClassFilePath("samples/findbugs/jdk/patterncompile/NonStaticPatternCompile"), 103 | }; 104 | 105 | // Run the analysis 106 | analyze(files, reporter); 107 | 108 | verify(reporter, never()).doReportBug( 109 | bugDefinition() 110 | .bugType(NonStaticPatternCompileDetector.NON_STATIC_PATTERN_COMPILE_CALL) 111 | .inClass("NonStaticPatternCompile") 112 | .inMethod("testNeverReportNonStaticPatternCompileWithFinalRegexParameter") 113 | .build() 114 | ); 115 | } 116 | 117 | @Test 118 | public void testNeverReportNonStaticPatternCompileWithNonFinalRegexParameter() throws Exception { 119 | // Locate test code 120 | final String[] files = { 121 | getClassFilePath("samples/findbugs/jdk/patterncompile/NonStaticPatternCompile"), 122 | }; 123 | 124 | // Run the analysis 125 | analyze(files, reporter); 126 | 127 | verify(reporter, never()).doReportBug( 128 | bugDefinition() 129 | .bugType(NonStaticPatternCompileDetector.NON_STATIC_PATTERN_COMPILE_CALL) 130 | .inClass("NonStaticPatternCompile") 131 | .inMethod("testNeverReportNonStaticPatternCompileWithNonFinalRegexParameter") 132 | .build() 133 | ); 134 | } 135 | 136 | @Test 137 | public void testNeverReportNonStaticPatternCompileWithRegexFromObject() throws Exception { 138 | // Locate test code 139 | final String[] files = { 140 | getClassFilePath("samples/findbugs/jdk/patterncompile/NonStaticPatternCompile"), 141 | }; 142 | 143 | // Run the analysis 144 | analyze(files, reporter); 145 | 146 | verify(reporter, never()).doReportBug( 147 | bugDefinition() 148 | .bugType(NonStaticPatternCompileDetector.NON_STATIC_PATTERN_COMPILE_CALL) 149 | .inClass("NonStaticPatternCompile") 150 | .inMethod("testNeverReportNonStaticPatternCompileWithRegexFromObject") 151 | .build() 152 | ); 153 | } 154 | 155 | 156 | @Test 157 | public void testMethodWithStaticPatternCompile() throws Exception { 158 | // Locate test code 159 | final String[] files = { 160 | getClassFilePath("samples/findbugs/jdk/patterncompile/StaticPatternCompile"), 161 | }; 162 | 163 | // Run the analysis 164 | analyze(files, reporter); 165 | 166 | verify(reporter, never()).doReportBug( 167 | bugDefinition() 168 | .bugType(NonStaticPatternCompileDetector.NON_STATIC_PATTERN_COMPILE_CALL) 169 | .inClass("StaticPatternCompile") 170 | .inMethod("testStaticPatternCompile") 171 | .build() 172 | ); 173 | } 174 | 175 | @Test 176 | public void testStaticPatternCompileInClass() throws Exception { 177 | // Locate test code 178 | final String[] files = { 179 | getClassFilePath("samples/findbugs/jdk/patterncompile/StaticPatternCompile"), 180 | }; 181 | 182 | // Run the analysis 183 | analyze(files, reporter); 184 | 185 | verify(reporter, never()).doReportBug( 186 | bugDefinition() 187 | .bugType(NonStaticPatternCompileDetector.NON_STATIC_PATTERN_COMPILE_CALL) 188 | .inClass("StaticPatternCompile") 189 | .build() 190 | ); 191 | } 192 | 193 | @SuppressWarnings("PMD.JUnitTestsShouldIncludeAssert") 194 | @Test 195 | public void testPatternCompileWithAConcatenatedRegex() throws Exception { 196 | // Locate test code 197 | final String[] files = { 198 | getClassFilePath("samples/findbugs/jdk/patterncompile/NonStaticPatternCompile"), 199 | }; 200 | 201 | // Run the analysis 202 | analyze(files, reporter); 203 | 204 | verify(reporter, never()).doReportBug( 205 | bugDefinition() 206 | .bugType(NonStaticPatternCompileDetector.NON_STATIC_PATTERN_COMPILE_CALL) 207 | .inClass("NonStaticPatternCompile") 208 | .inMethod("testPatternCompileWithAConcatenatedRegex") 209 | .build() 210 | ); 211 | } 212 | } -------------------------------------------------------------------------------- /src/test/java/com/monits/findbugs/jdk/UselessStringValueOfTest.java: -------------------------------------------------------------------------------- 1 | package com.monits.findbugs.jdk; 2 | 3 | import static org.mockito.Mockito.never; 4 | import static org.mockito.Mockito.spy; 5 | import static org.mockito.Mockito.verify; 6 | 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | 10 | import com.h3xstream.findbugs.test.BaseDetectorTest; 11 | import com.h3xstream.findbugs.test.EasyBugReporter; 12 | 13 | public class UselessStringValueOfTest extends BaseDetectorTest { 14 | 15 | private EasyBugReporter reporter; 16 | 17 | @Before 18 | public void setup() { 19 | reporter = spy(new EasyBugReporter()); 20 | } 21 | 22 | @Test 23 | public void testUselessStringValueOfString() throws Exception { 24 | // Locate test code 25 | final String[] files = { 26 | getClassFilePath("samples/findbugs/jdk/UselessStringValueOfCall"), 27 | }; 28 | 29 | // Run the analysis 30 | analyze(files, reporter); 31 | 32 | verify(reporter).doReportBug( 33 | bugDefinition() 34 | .bugType(UselessStringValueOfCallDetector.USELESS_STRING_VALUEOF_CALL) 35 | .inClass("UselessStringValueOfCall") 36 | .inMethod("getStringValueOfString") 37 | .build() 38 | ); 39 | } 40 | 41 | @Test 42 | public void testUselessStringValueOfDummyString() throws Exception { 43 | // Locate test code 44 | final String[] files = { 45 | getClassFilePath("samples/findbugs/jdk/UselessStringValueOfCall"), 46 | }; 47 | 48 | // Run the analysis 49 | analyze(files, reporter); 50 | 51 | verify(reporter).doReportBug( 52 | bugDefinition() 53 | .bugType(UselessStringValueOfCallDetector.USELESS_STRING_VALUEOF_CALL) 54 | .inClass("UselessStringValueOfCall") 55 | .inMethod("getStringValueOfDummyString") 56 | .build() 57 | ); 58 | } 59 | 60 | @Test 61 | public void getStringValueOfPrimitiveInteger() throws Exception { 62 | // Locate test code 63 | final String[] files = { 64 | getClassFilePath("samples/findbugs/jdk/UselessStringValueOfCall"), 65 | }; 66 | 67 | // Run the analysis 68 | analyze(files, reporter); 69 | 70 | verify(reporter, never()).doReportBug( 71 | bugDefinition() 72 | .bugType(UselessStringValueOfCallDetector.USELESS_STRING_VALUEOF_CALL) 73 | .inClass("UselessStringValueOfCall") 74 | .inMethod("getStringValueOfPrimitiveInteger") 75 | .build() 76 | ); 77 | } 78 | 79 | @Test 80 | public void getString() throws Exception { 81 | // Locate test code 82 | final String[] files = { 83 | getClassFilePath("samples/findbugs/jdk/UselessStringValueOfCall"), 84 | }; 85 | 86 | // Run the analysis 87 | analyze(files, reporter); 88 | 89 | verify(reporter, never()).doReportBug( 90 | bugDefinition() 91 | .bugType(UselessStringValueOfCallDetector.USELESS_STRING_VALUEOF_CALL) 92 | .inClass("UselessStringValueOfCall") 93 | .inMethod("getString") 94 | .build() 95 | ); 96 | } 97 | 98 | @Test 99 | public void concatenatedStringFromParam() throws Exception { 100 | // Locate test code 101 | final String[] files = { 102 | getClassFilePath("samples/findbugs/jdk/UselessStringValueOfCall"), 103 | }; 104 | 105 | // Run the analysis 106 | analyze(files, reporter); 107 | 108 | verify(reporter, never()).doReportBug( 109 | bugDefinition() 110 | .bugType(UselessStringValueOfCallDetector.USELESS_STRING_VALUEOF_CALL) 111 | .inClass("UselessStringValueOfCall") 112 | .inMethod("concatenatedStringFromParam") 113 | .build() 114 | ); 115 | } 116 | 117 | @Test 118 | public void concatenatedLocalString() throws Exception { 119 | // Locate test code 120 | final String[] files = { 121 | getClassFilePath("samples/findbugs/jdk/UselessStringValueOfCall"), 122 | }; 123 | 124 | // Run the analysis 125 | analyze(files, reporter); 126 | 127 | verify(reporter, never()).doReportBug( 128 | bugDefinition() 129 | .bugType(UselessStringValueOfCallDetector.USELESS_STRING_VALUEOF_CALL) 130 | .inClass("UselessStringValueOfCall") 131 | .inMethod("concatenatedLocalString") 132 | .build() 133 | ); 134 | } 135 | 136 | @Test 137 | public void stringFromParamInValueOf() throws Exception { 138 | // Locate test code 139 | final String[] files = { 140 | getClassFilePath("samples/findbugs/jdk/UselessStringValueOfCall"), 141 | }; 142 | 143 | // Run the analysis 144 | analyze(files, reporter); 145 | 146 | verify(reporter).doReportBug( 147 | bugDefinition() 148 | .bugType(UselessStringValueOfCallDetector.USELESS_STRING_VALUEOF_CALL) 149 | .inClass("UselessStringValueOfCall") 150 | .inMethod("stringFromParamInValueOf") 151 | .build() 152 | ); 153 | } 154 | } -------------------------------------------------------------------------------- /src/test/java/jp/co/worksap/oss/findbugs/ForbiddenSystemDetectorTest.java: -------------------------------------------------------------------------------- 1 | package jp.co.worksap.oss.findbugs; 2 | 3 | import static org.mockito.Mockito.spy; 4 | import static org.mockito.Mockito.verify; 5 | 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | 9 | import com.h3xstream.findbugs.test.BaseDetectorTest; 10 | import com.h3xstream.findbugs.test.EasyBugReporter; 11 | 12 | public class ForbiddenSystemDetectorTest extends BaseDetectorTest { 13 | private EasyBugReporter reporter; 14 | 15 | @Before 16 | public void setup() { 17 | reporter = spy(new EasyBugReporter()); 18 | } 19 | 20 | @Test 21 | public void testUseSystemOutBug() throws Exception { 22 | // Locate test code 23 | final String[] files = { 24 | getClassFilePath("samples/system/UseSystemOut") 25 | }; 26 | 27 | // Run the analysis 28 | analyze(files, reporter); 29 | 30 | verify(reporter).doReportBug( 31 | bugDefinition() 32 | .bugType("FORBIDDEN_SYSTEM") 33 | .inClass("UseSystemOut") 34 | .build() 35 | ); 36 | } 37 | 38 | @Test 39 | public void testUseSystemErrBug() throws Exception { 40 | // Locate test code 41 | final String[] files = { 42 | getClassFilePath("samples/system/UseSystemErr") 43 | }; 44 | 45 | // Run the analysis 46 | analyze(files, reporter); 47 | 48 | verify(reporter).doReportBug( 49 | bugDefinition() 50 | .bugType("FORBIDDEN_SYSTEM") 51 | .inClass("UseSystemErr") 52 | .build() 53 | ); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/test/java/jp/co/worksap/oss/findbugs/findbugs/UndocumentedSuppressFBWarningsDetectorTest.java: -------------------------------------------------------------------------------- 1 | package jp.co.worksap.oss.findbugs.findbugs; 2 | 3 | import static org.mockito.Mockito.never; 4 | import static org.mockito.Mockito.spy; 5 | import static org.mockito.Mockito.verify; 6 | 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | 10 | import com.h3xstream.findbugs.test.BaseDetectorTest; 11 | import com.h3xstream.findbugs.test.EasyBugReporter; 12 | 13 | public class UndocumentedSuppressFBWarningsDetectorTest extends BaseDetectorTest { 14 | 15 | private EasyBugReporter reporter; 16 | 17 | @Before 18 | public void setup() { 19 | reporter = spy(new EasyBugReporter()); 20 | } 21 | 22 | @Test 23 | public void testDocumentedClasses() throws Exception { 24 | // Locate test code 25 | final String[] files = { 26 | getClassFilePath("samples/findbugs/DocumentedSuppressFBWarnings"), 27 | getClassFilePath("samples/findbugs/DocumentedSuppressWarnings") 28 | }; 29 | 30 | // Run the analysis 31 | analyze(files, reporter); 32 | 33 | verify(reporter, never()).doReportBug( 34 | bugDefinition() 35 | .bugType("FINDBUGS_UNDOCUMENTED_SUPPRESS_WARNINGS") 36 | .build() 37 | ); 38 | } 39 | 40 | @Test 41 | public void testUndocumentedClasses() throws Exception { 42 | // Locate test code 43 | final String[] files = { 44 | getClassFilePath("samples/findbugs/UndocumentedSuppressFBWarnings"), 45 | getClassFilePath("samples/findbugs/UndocumentedSuppressWarnings") 46 | }; 47 | 48 | // Run the analysis 49 | analyze(files, reporter); 50 | 51 | verify(reporter).doReportBug( 52 | bugDefinition() 53 | .bugType("FINDBUGS_UNDOCUMENTED_SUPPRESS_WARNINGS") 54 | .inClass("UndocumentedSuppressFBWarnings") 55 | .build() 56 | ); 57 | verify(reporter).doReportBug( 58 | bugDefinition() 59 | .bugType("FINDBUGS_UNDOCUMENTED_SUPPRESS_WARNINGS") 60 | .inClass("UndocumentedSuppressWarnings") 61 | .build() 62 | ); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/test/java/jp/co/worksap/oss/findbugs/guava/UnexpectedAccessDetectorTest.java: -------------------------------------------------------------------------------- 1 | package jp.co.worksap.oss.findbugs.guava; 2 | 3 | import static org.mockito.Mockito.never; 4 | import static org.mockito.Mockito.spy; 5 | import static org.mockito.Mockito.verify; 6 | 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | 10 | import com.h3xstream.findbugs.test.BaseDetectorTest; 11 | import com.h3xstream.findbugs.test.EasyBugReporter; 12 | 13 | 14 | /** 15 | * @author tolina GmbH 16 | * 17 | */ 18 | public class UnexpectedAccessDetectorTest extends BaseDetectorTest { 19 | private EasyBugReporter reporter; 20 | 21 | @Before 22 | public void setup() { 23 | reporter = spy(new EasyBugReporter()); 24 | } 25 | 26 | @Test 27 | public void testNormalMethod() throws Exception { 28 | // Locate test code 29 | final String[] files = { 30 | getClassFilePath("samples/guava/ClassWhichCallsNormalMethod"), 31 | getClassFilePath("samples/guava/MethodWithoutVisibleForTesting") 32 | }; 33 | 34 | // Run the analysis 35 | analyze(files, reporter); 36 | 37 | verify(reporter, never()).doReportBug( 38 | bugDefinition() 39 | .bugType("GUAVA_UNEXPECTED_ACCESS_TO_VISIBLE_FOR_TESTING") 40 | .build() 41 | ); 42 | } 43 | 44 | @Test 45 | public void testCallFromJUnit4Test() throws Exception { 46 | // Locate test code 47 | final String[] files = { 48 | getClassFilePath("samples/guava/JUnit4Test"), 49 | getClassFilePath("samples/guava/MethodWithVisibleForTesting") 50 | }; 51 | 52 | // Run the analysis 53 | analyze(files, reporter); 54 | 55 | verify(reporter, never()).doReportBug( 56 | bugDefinition() 57 | .bugType("GUAVA_UNEXPECTED_ACCESS_TO_VISIBLE_FOR_TESTING") 58 | .build() 59 | ); 60 | } 61 | 62 | @Test 63 | public void testCallFromJUnit3Test() throws Exception { 64 | // Locate test code 65 | final String[] files = { 66 | getClassFilePath("samples/guava/JUnit3Test"), 67 | getClassFilePath("samples/guava/MethodWithVisibleForTesting") 68 | }; 69 | 70 | // Run the analysis 71 | analyze(files, reporter); 72 | 73 | verify(reporter, never()).doReportBug( 74 | bugDefinition() 75 | .bugType("GUAVA_UNEXPECTED_ACCESS_TO_VISIBLE_FOR_TESTING") 76 | .build() 77 | ); 78 | } 79 | 80 | @Test 81 | public void testCallingAnnotatedMethod() throws Exception { 82 | // Locate test code 83 | final String[] files = { 84 | getClassFilePath("samples/guava/ClassWhichCallsVisibleMethodForTesting"), 85 | getClassFilePath("samples/guava/MethodWithVisibleForTesting") 86 | }; 87 | 88 | // Run the analysis 89 | analyze(files, reporter); 90 | 91 | verify(reporter).doReportBug( 92 | bugDefinition() 93 | .bugType("GUAVA_UNEXPECTED_ACCESS_TO_VISIBLE_FOR_TESTING") 94 | .inClass("ClassWhichCallsVisibleMethodForTesting") 95 | .build() 96 | ); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/test/java/jp/co/worksap/oss/findbugs/jpa/ColumnDefinitionTest.java: -------------------------------------------------------------------------------- 1 | package jp.co.worksap.oss.findbugs.jpa; 2 | 3 | import static org.mockito.Mockito.never; 4 | import static org.mockito.Mockito.spy; 5 | import static org.mockito.Mockito.verify; 6 | 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | 10 | import com.h3xstream.findbugs.test.BaseDetectorTest; 11 | import com.h3xstream.findbugs.test.EasyBugReporter; 12 | 13 | public class ColumnDefinitionTest extends BaseDetectorTest { 14 | 15 | private EasyBugReporter reporter; 16 | 17 | @Before 18 | public void setup() { 19 | reporter = spy(new EasyBugReporter()); 20 | } 21 | 22 | @Test 23 | public void testNormalClass() throws Exception { 24 | // Locate test code 25 | final String[] files = { 26 | getClassFilePath("samples/jpa/ShortColumnName") 27 | }; 28 | 29 | // Run the analysis 30 | analyze(files, reporter); 31 | 32 | verify(reporter, never()).doReportBug( 33 | bugDefinition() 34 | .bugType("USE_COLUMN_DEFINITION") 35 | .build() 36 | ); 37 | } 38 | 39 | @Test 40 | public void testWithColumnDefinition() throws Exception { 41 | // Locate test code 42 | final String[] files = { 43 | getClassFilePath("samples/jpa/UseColumnDefinition") 44 | }; 45 | 46 | // Run the analysis 47 | analyze(files, reporter); 48 | 49 | verify(reporter).doReportBug( 50 | bugDefinition() 51 | .bugType("USE_COLUMN_DEFINITION") 52 | .inClass("UseColumnDefinition") 53 | .build() 54 | ); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/test/java/jp/co/worksap/oss/findbugs/jpa/ColumnNameLengthTest.java: -------------------------------------------------------------------------------- 1 | package jp.co.worksap.oss.findbugs.jpa; 2 | 3 | import static org.mockito.Mockito.never; 4 | import static org.mockito.Mockito.spy; 5 | import static org.mockito.Mockito.verify; 6 | 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | 10 | import com.h3xstream.findbugs.test.BaseDetectorTest; 11 | import com.h3xstream.findbugs.test.EasyBugReporter; 12 | 13 | public class ColumnNameLengthTest extends BaseDetectorTest { 14 | private EasyBugReporter reporter; 15 | 16 | @Before 17 | public void setup() { 18 | reporter = spy(new EasyBugReporter()); 19 | } 20 | 21 | @Test 22 | public void testShortName() throws Exception { 23 | // Locate test code 24 | final String[] files = { 25 | getClassFilePath("samples/jpa/ShortColumnName") 26 | }; 27 | 28 | // Run the analysis 29 | analyze(files, reporter); 30 | 31 | verify(reporter, never()).doReportBug( 32 | bugDefinition() 33 | .bugType("LONG_COLUMN_NAME") 34 | .build() 35 | ); 36 | } 37 | 38 | @Test 39 | public void testShortNameWithoutAnnotationParameter() throws Exception { 40 | // Locate test code 41 | final String[] files = { 42 | getClassFilePath("samples/jpa/ShortColumnNameWithoutAnnotationParameter") 43 | }; 44 | 45 | // Run the analysis 46 | analyze(files, reporter); 47 | 48 | verify(reporter, never()).doReportBug( 49 | bugDefinition() 50 | .bugType("LONG_COLUMN_NAME") 51 | .build() 52 | ); 53 | } 54 | 55 | @Test 56 | public void testLongName() throws Exception { 57 | // Locate test code 58 | final String[] files = { 59 | getClassFilePath("samples/jpa/LongColumnName") 60 | }; 61 | 62 | // Run the analysis 63 | analyze(files, reporter); 64 | 65 | verify(reporter).doReportBug( 66 | bugDefinition() 67 | .bugType("LONG_COLUMN_NAME") 68 | .inClass("LongColumnName") 69 | .build() 70 | ); 71 | } 72 | 73 | @Test 74 | public void testLongNameWithoutAnnotationParameter() throws Exception { 75 | // Locate test code 76 | final String[] files = { 77 | getClassFilePath("samples/jpa/LongColumnNameWithoutAnnotationParameter") 78 | }; 79 | 80 | // Run the analysis 81 | analyze(files, reporter); 82 | 83 | verify(reporter).doReportBug( 84 | bugDefinition() 85 | .bugType("LONG_COLUMN_NAME") 86 | .inClass("LongColumnNameWithoutAnnotationParameter") 87 | .build() 88 | ); 89 | } 90 | 91 | @Test 92 | public void testLongColumnNameByAnnotatedMethod() throws Exception { 93 | // Locate test code 94 | final String[] files = { 95 | getClassFilePath("samples/jpa/LongColumnNameByAnnotatedMethod") 96 | }; 97 | 98 | // Run the analysis 99 | analyze(files, reporter); 100 | 101 | verify(reporter).doReportBug( 102 | bugDefinition() 103 | .bugType("LONG_COLUMN_NAME") 104 | .inClass("LongColumnNameByAnnotatedMethod") 105 | .build() 106 | ); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/test/java/jp/co/worksap/oss/findbugs/jpa/ImplicitLengthTest.java: -------------------------------------------------------------------------------- 1 | package jp.co.worksap.oss.findbugs.jpa; 2 | 3 | import static org.mockito.Mockito.never; 4 | import static org.mockito.Mockito.spy; 5 | import static org.mockito.Mockito.verify; 6 | 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | 10 | import com.h3xstream.findbugs.test.BaseDetectorTest; 11 | import com.h3xstream.findbugs.test.EasyBugReporter; 12 | 13 | public class ImplicitLengthTest extends BaseDetectorTest { 14 | private EasyBugReporter reporter; 15 | 16 | @Before 17 | public void setup() { 18 | reporter = spy(new EasyBugReporter()); 19 | } 20 | 21 | @Test 22 | public void testNegativeLength() throws Exception { 23 | // Locate test code 24 | final String[] files = { 25 | getClassFilePath("samples/jpa/ColumnWithNegativeLength") 26 | }; 27 | 28 | // Run the analysis 29 | analyze(files, reporter); 30 | 31 | verify(reporter).doReportBug( 32 | bugDefinition() 33 | .bugType("ILLEGAL_LENGTH") 34 | .inClass("ColumnWithNegativeLength") 35 | .build() 36 | ); 37 | } 38 | 39 | @Test 40 | public void testTooLongLength() throws Exception { 41 | // Locate test code 42 | final String[] files = { 43 | getClassFilePath("samples/jpa/ColumnWithTooLongLength"), 44 | getClassFilePath("samples/jpa/GetterWithTooLongLength") 45 | }; 46 | 47 | // Run the analysis 48 | analyze(files, reporter); 49 | 50 | verify(reporter).doReportBug( 51 | bugDefinition() 52 | .bugType("ILLEGAL_LENGTH") 53 | .inClass("ColumnWithTooLongLength") 54 | .build() 55 | ); 56 | verify(reporter).doReportBug( 57 | bugDefinition() 58 | .bugType("ILLEGAL_LENGTH") 59 | .inClass("GetterWithTooLongLength") 60 | .build() 61 | ); 62 | } 63 | 64 | @Test 65 | public void testLongLengthWithLob() throws Exception { 66 | // Locate test code 67 | final String[] files = { 68 | getClassFilePath("samples/jpa/ColumnWithLongLengthAndLob"), 69 | getClassFilePath("samples/jpa/GetterWithLongLengthAndLob") 70 | }; 71 | 72 | // Run the analysis 73 | analyze(files, reporter); 74 | 75 | verify(reporter, never()).doReportBug( 76 | bugDefinition() 77 | .bugType("LONG_COLUMN_NAME") 78 | .build() 79 | ); 80 | } 81 | 82 | @Test 83 | public void testExplicitLength() throws Exception { 84 | // Locate test code 85 | final String[] files = { 86 | getClassFilePath("samples/jpa/ColumnWithLength") 87 | }; 88 | 89 | // Run the analysis 90 | analyze(files, reporter); 91 | 92 | verify(reporter, never()).doReportBug( 93 | bugDefinition() 94 | .bugType("LONG_COLUMN_NAME") 95 | .build() 96 | ); 97 | } 98 | 99 | @Test 100 | public void testImplicitLength() throws Exception { 101 | // Locate test code 102 | final String[] files = { 103 | getClassFilePath("samples/jpa/ColumnWithoutElement"), 104 | getClassFilePath("samples/jpa/GetterWithoutElement") 105 | }; 106 | 107 | // Run the analysis 108 | analyze(files, reporter); 109 | 110 | verify(reporter).doReportBug( 111 | bugDefinition() 112 | .bugType("IMPLICIT_LENGTH") 113 | .inClass("ColumnWithoutElement") 114 | .build() 115 | ); 116 | verify(reporter).doReportBug( 117 | bugDefinition() 118 | .bugType("IMPLICIT_LENGTH") 119 | .inClass("GetterWithoutElement") 120 | .build() 121 | ); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/test/java/jp/co/worksap/oss/findbugs/jpa/ImplicitNullnessTest.java: -------------------------------------------------------------------------------- 1 | package jp.co.worksap.oss.findbugs.jpa; 2 | 3 | import static org.mockito.Mockito.never; 4 | import static org.mockito.Mockito.spy; 5 | import static org.mockito.Mockito.verify; 6 | 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | 10 | import com.h3xstream.findbugs.test.BaseDetectorTest; 11 | import com.h3xstream.findbugs.test.EasyBugReporter; 12 | 13 | public class ImplicitNullnessTest extends BaseDetectorTest { 14 | private EasyBugReporter reporter; 15 | 16 | @Before 17 | public void setup() { 18 | reporter = spy(new EasyBugReporter()); 19 | } 20 | 21 | @Test 22 | public void testExplicitNullness() throws Exception { 23 | // Locate test code 24 | final String[] files = { 25 | getClassFilePath("samples/jpa/ColumnWithNullable") 26 | }; 27 | 28 | // Run the analysis 29 | analyze(files, reporter); 30 | 31 | verify(reporter, never()).doReportBug( 32 | bugDefinition() 33 | .bugType("IMPLICIT_NULLNESS") 34 | .build() 35 | ); 36 | } 37 | 38 | @Test 39 | public void testImplicitNullness() throws Exception { 40 | // Locate test code 41 | final String[] files = { 42 | getClassFilePath("samples/jpa/ColumnWithoutElement"), 43 | getClassFilePath("samples/jpa/GetterWithoutElement") 44 | }; 45 | 46 | // Run the analysis 47 | analyze(files, reporter); 48 | 49 | verify(reporter).doReportBug( 50 | bugDefinition() 51 | .bugType("IMPLICIT_NULLNESS") 52 | .inClass("ColumnWithoutElement") 53 | .build() 54 | ); 55 | verify(reporter).doReportBug( 56 | bugDefinition() 57 | .bugType("IMPLICIT_NULLNESS") 58 | .inClass("GetterWithoutElement") 59 | .build() 60 | ); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/test/java/jp/co/worksap/oss/findbugs/jpa/IndexNameLengthTest.java: -------------------------------------------------------------------------------- 1 | package jp.co.worksap.oss.findbugs.jpa; 2 | 3 | import static org.mockito.Mockito.never; 4 | import static org.mockito.Mockito.spy; 5 | import static org.mockito.Mockito.verify; 6 | 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | 10 | import com.h3xstream.findbugs.test.BaseDetectorTest; 11 | import com.h3xstream.findbugs.test.EasyBugReporter; 12 | 13 | public class IndexNameLengthTest extends BaseDetectorTest { 14 | private EasyBugReporter reporter; 15 | 16 | @Before 17 | public void setup() { 18 | reporter = spy(new EasyBugReporter()); 19 | } 20 | 21 | @Test 22 | public void testShortNameWithHibernate() throws Exception { 23 | // Locate test code 24 | final String[] files = { 25 | getClassFilePath("samples/jpa/ShortIndexNameForHibernate") 26 | }; 27 | 28 | // Run the analysis 29 | analyze(files, reporter); 30 | 31 | verify(reporter, never()).doReportBug( 32 | bugDefinition() 33 | .bugType("LONG_INDEX_NAME") 34 | .build() 35 | ); 36 | } 37 | 38 | @Test 39 | public void testLongNameWithHibernate() throws Exception { 40 | // Locate test code 41 | final String[] files = { 42 | getClassFilePath("samples/jpa/LongIndexNameForHibernate") 43 | }; 44 | 45 | // Run the analysis 46 | analyze(files, reporter); 47 | 48 | verify(reporter).doReportBug( 49 | bugDefinition() 50 | .bugType("LONG_INDEX_NAME") 51 | .inClass("LongIndexNameForHibernate") 52 | .build() 53 | ); 54 | } 55 | 56 | @Test 57 | public void testShortNameWithOpenJPA() throws Exception { 58 | // Locate test code 59 | final String[] files = { 60 | getClassFilePath("samples/jpa/ShortIndexNameForOpenJPA") 61 | }; 62 | 63 | // Run the analysis 64 | analyze(files, reporter); 65 | 66 | verify(reporter, never()).doReportBug( 67 | bugDefinition() 68 | .bugType("LONG_INDEX_NAME") 69 | .build() 70 | ); 71 | } 72 | 73 | @Test 74 | public void testLongNameWithOpenJPA() throws Exception { 75 | // Locate test code 76 | final String[] files = { 77 | getClassFilePath("samples/jpa/LongIndexNameForOpenJPA") 78 | }; 79 | 80 | // Run the analysis 81 | analyze(files, reporter); 82 | 83 | verify(reporter).doReportBug( 84 | bugDefinition() 85 | .bugType("LONG_INDEX_NAME") 86 | .inClass("LongIndexNameForOpenJPA") 87 | .build() 88 | ); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/test/java/jp/co/worksap/oss/findbugs/jpa/NullablePrimitiveDetectorTest.java: -------------------------------------------------------------------------------- 1 | package jp.co.worksap.oss.findbugs.jpa; 2 | 3 | import static org.mockito.Mockito.never; 4 | import static org.mockito.Mockito.spy; 5 | import static org.mockito.Mockito.verify; 6 | 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | 10 | import com.h3xstream.findbugs.test.BaseDetectorTest; 11 | import com.h3xstream.findbugs.test.EasyBugReporter; 12 | 13 | public class NullablePrimitiveDetectorTest extends BaseDetectorTest { 14 | private EasyBugReporter reporter; 15 | 16 | @Before 17 | public void setup() { 18 | reporter = spy(new EasyBugReporter()); 19 | } 20 | 21 | @Test 22 | public void testNullableObject() throws Exception { 23 | // Locate test code 24 | final String[] files = { 25 | getClassFilePath("samples/jpa/UseColumnDefinition"), 26 | getClassFilePath("samples/jpa/ColumnWithoutElement") 27 | }; 28 | 29 | // Run the analysis 30 | analyze(files, reporter); 31 | 32 | verify(reporter, never()).doReportBug( 33 | bugDefinition() 34 | .bugType("NULLABLE_PRIMITIVE") 35 | .build() 36 | ); 37 | } 38 | 39 | @Test 40 | public void testNullablePrimitive() throws Exception { 41 | // Locate test code 42 | final String[] files = { 43 | getClassFilePath("samples/jpa/NullableBooleanColumn"), 44 | getClassFilePath("samples/jpa/NullableByteColumn"), 45 | getClassFilePath("samples/jpa/NullableShortColumn"), 46 | getClassFilePath("samples/jpa/NullableIntColumn"), 47 | getClassFilePath("samples/jpa/NullableLongColumn"), 48 | getClassFilePath("samples/jpa/NullableFloatColumn"), 49 | getClassFilePath("samples/jpa/NullableDoubleColumn"), 50 | getClassFilePath("samples/jpa/NullableBooleanGetter") 51 | }; 52 | 53 | // Run the analysis 54 | analyze(files, reporter); 55 | 56 | verify(reporter).doReportBug( 57 | bugDefinition() 58 | .bugType("NULLABLE_PRIMITIVE") 59 | .inClass("NullableBooleanColumn") 60 | .build() 61 | ); 62 | verify(reporter).doReportBug( 63 | bugDefinition() 64 | .bugType("NULLABLE_PRIMITIVE") 65 | .inClass("NullableByteColumn") 66 | .build() 67 | ); 68 | verify(reporter).doReportBug( 69 | bugDefinition() 70 | .bugType("NULLABLE_PRIMITIVE") 71 | .inClass("NullableShortColumn") 72 | .build() 73 | ); 74 | verify(reporter).doReportBug( 75 | bugDefinition() 76 | .bugType("NULLABLE_PRIMITIVE") 77 | .inClass("NullableIntColumn") 78 | .build() 79 | ); 80 | verify(reporter).doReportBug( 81 | bugDefinition() 82 | .bugType("NULLABLE_PRIMITIVE") 83 | .inClass("NullableLongColumn") 84 | .build() 85 | ); 86 | verify(reporter).doReportBug( 87 | bugDefinition() 88 | .bugType("NULLABLE_PRIMITIVE") 89 | .inClass("NullableFloatColumn") 90 | .build() 91 | ); 92 | verify(reporter).doReportBug( 93 | bugDefinition() 94 | .bugType("NULLABLE_PRIMITIVE") 95 | .inClass("NullableDoubleColumn") 96 | .build() 97 | ); 98 | verify(reporter).doReportBug( 99 | bugDefinition() 100 | .bugType("NULLABLE_PRIMITIVE") 101 | .inClass("NullableBooleanGetter") 102 | .build() 103 | ); 104 | } 105 | 106 | @Test 107 | public void testNonNullableObject() throws Exception { 108 | // Locate test code 109 | final String[] files = { 110 | getClassFilePath("samples/jpa/ColumnWithNullable") 111 | }; 112 | 113 | // Run the analysis 114 | analyze(files, reporter); 115 | 116 | verify(reporter, never()).doReportBug( 117 | bugDefinition() 118 | .bugType("NULLABLE_PRIMITIVE") 119 | .build() 120 | ); 121 | } 122 | 123 | @Test 124 | public void testNonNullableInt() throws Exception { 125 | // Locate test code 126 | final String[] files = { 127 | getClassFilePath("samples/jpa/NonNullablePrimitiveColumn") 128 | }; 129 | 130 | // Run the analysis 131 | analyze(files, reporter); 132 | 133 | verify(reporter, never()).doReportBug( 134 | bugDefinition() 135 | .bugType("NULLABLE_PRIMITIVE") 136 | .build() 137 | ); 138 | } 139 | 140 | } 141 | -------------------------------------------------------------------------------- /src/test/java/jp/co/worksap/oss/findbugs/jpa/TableNameLengthTest.java: -------------------------------------------------------------------------------- 1 | package jp.co.worksap.oss.findbugs.jpa; 2 | 3 | import static org.mockito.Mockito.never; 4 | import static org.mockito.Mockito.spy; 5 | import static org.mockito.Mockito.verify; 6 | 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | 10 | import com.h3xstream.findbugs.test.BaseDetectorTest; 11 | import com.h3xstream.findbugs.test.EasyBugReporter; 12 | 13 | public class TableNameLengthTest extends BaseDetectorTest { 14 | private EasyBugReporter reporter; 15 | 16 | @Before 17 | public void setup() { 18 | reporter = spy(new EasyBugReporter()); 19 | } 20 | 21 | @Test 22 | public void testShortName() throws Exception { 23 | // Locate test code 24 | final String[] files = { 25 | getClassFilePath("samples/jpa/ShortTableName") 26 | }; 27 | 28 | // Run the analysis 29 | analyze(files, reporter); 30 | 31 | verify(reporter, never()).doReportBug( 32 | bugDefinition() 33 | .bugType("LONG_TABLE_NAME") 34 | .build() 35 | ); 36 | } 37 | 38 | @Test 39 | public void testShortNameWithoutAnnotationParameter() throws Exception { 40 | // Locate test code 41 | final String[] files = { 42 | getClassFilePath("samples/jpa/ShortTableNameNoAnnotationPara") 43 | }; 44 | 45 | // Run the analysis 46 | analyze(files, reporter); 47 | 48 | verify(reporter, never()).doReportBug( 49 | bugDefinition() 50 | .bugType("LONG_TABLE_NAME") 51 | .build() 52 | ); 53 | } 54 | 55 | @Test 56 | public void testLongName() throws Exception { 57 | // Locate test code 58 | final String[] files = { 59 | getClassFilePath("samples/jpa/LongTableName") 60 | }; 61 | 62 | // Run the analysis 63 | analyze(files, reporter); 64 | 65 | verify(reporter).doReportBug( 66 | bugDefinition() 67 | .bugType("LONG_TABLE_NAME") 68 | .inClass("LongTableName") 69 | .build() 70 | ); 71 | } 72 | 73 | @Test 74 | public void testLongNameWithoutAnnotationParameter() throws Exception { 75 | // Locate test code 76 | final String[] files = { 77 | getClassFilePath("samples/jpa/LongTableNameWithoutAnnotationParameter") 78 | }; 79 | 80 | // Run the analysis 81 | analyze(files, reporter); 82 | 83 | verify(reporter).doReportBug( 84 | bugDefinition() 85 | .bugType("LONG_TABLE_NAME") 86 | .inClass("LongTableNameWithoutAnnotationParameter") 87 | .build() 88 | ); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/test/java/jp/co/worksap/oss/findbugs/jsr305/BrokenImmutableClassDetectorTest.java: -------------------------------------------------------------------------------- 1 | package jp.co.worksap.oss.findbugs.jsr305; 2 | 3 | 4 | import static org.mockito.Mockito.never; 5 | import static org.mockito.Mockito.spy; 6 | import static org.mockito.Mockito.verify; 7 | import static org.mockito.Mockito.times; 8 | 9 | import org.junit.Before; 10 | import org.junit.Test; 11 | 12 | import com.h3xstream.findbugs.test.BaseDetectorTest; 13 | import com.h3xstream.findbugs.test.EasyBugReporter; 14 | 15 | public class BrokenImmutableClassDetectorTest extends BaseDetectorTest { 16 | private EasyBugReporter reporter; 17 | 18 | @Before 19 | public void setup() { 20 | reporter = spy(new EasyBugReporter()); 21 | } 22 | 23 | @Test 24 | public void testGoodMutableClass() throws Exception { 25 | // Locate test code 26 | final String[] files = { 27 | getClassFilePath("samples/jsr305/GoodMutableClass") 28 | }; 29 | 30 | // Run the analysis 31 | analyze(files, reporter); 32 | 33 | verify(reporter, never()).doReportBug( 34 | bugDefinition() 35 | .bugType("IMMUTABLE_CLASS_SHOULD_BE_FINAL") 36 | .build() 37 | ); 38 | verify(reporter, never()).doReportBug( 39 | bugDefinition() 40 | .bugType("BROKEN_IMMUTABILITY") 41 | .build() 42 | ); 43 | } 44 | 45 | @Test 46 | public void testEnumIsImmutable() throws Exception { 47 | // Locate test code 48 | final String[] files = { 49 | getClassFilePath("samples/jsr305/ImmutableEnum") 50 | }; 51 | 52 | // Run the analysis 53 | analyze(files, reporter); 54 | 55 | verify(reporter, never()).doReportBug( 56 | bugDefinition() 57 | .bugType("IMMUTABLE_CLASS_SHOULD_BE_FINAL") 58 | .build() 59 | ); 60 | verify(reporter, never()).doReportBug( 61 | bugDefinition() 62 | .bugType("BROKEN_IMMUTABILITY") 63 | .build() 64 | ); 65 | } 66 | 67 | @Test 68 | public void testBadMutableClass() throws Exception { 69 | // Locate test code 70 | final String[] files = { 71 | getClassFilePath("samples/jsr305/BadImmutableClass"), 72 | getClassFilePath("samples/jsr305/ExtendsMutableClass") 73 | }; 74 | 75 | // Run the analysis 76 | analyze(files, reporter); 77 | 78 | verify(reporter, times(2)).doReportBug( 79 | bugDefinition() 80 | .bugType("BROKEN_IMMUTABILITY") 81 | .inClass("BadImmutableClass") 82 | .build() 83 | ); 84 | verify(reporter).doReportBug( 85 | bugDefinition() 86 | .bugType("IMMUTABLE_CLASS_SHOULD_BE_FINAL") 87 | .inClass("BadImmutableClass") 88 | .build() 89 | ); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/test/java/jp/co/worksap/oss/findbugs/junit/UndocumentedIgnoreDetectorTest.java: -------------------------------------------------------------------------------- 1 | package jp.co.worksap.oss.findbugs.junit; 2 | 3 | import static org.mockito.Mockito.never; 4 | import static org.mockito.Mockito.spy; 5 | import static org.mockito.Mockito.verify; 6 | 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | 10 | import com.h3xstream.findbugs.test.BaseDetectorTest; 11 | import com.h3xstream.findbugs.test.EasyBugReporter; 12 | 13 | public class UndocumentedIgnoreDetectorTest extends BaseDetectorTest { 14 | private EasyBugReporter reporter; 15 | 16 | @Before 17 | public void setup() { 18 | reporter = spy(new EasyBugReporter()); 19 | } 20 | 21 | @Test 22 | public void testIgnoreClassWithExplanation() throws Exception { 23 | // Locate test code 24 | final String[] files = { 25 | getClassFilePath("samples/junit/IgnoreClassWithExplanation") 26 | }; 27 | 28 | // Run the analysis 29 | analyze(files, reporter); 30 | 31 | verify(reporter, never()).doReportBug( 32 | bugDefinition() 33 | .bugType("UNDOCUMENTED_IGNORE") 34 | .build() 35 | ); 36 | } 37 | 38 | @Test 39 | public void testIgnoreMethodWithExplanation() throws Exception { 40 | // Locate test code 41 | final String[] files = { 42 | getClassFilePath("samples/junit/IgnoreMethodWithExplanation") 43 | }; 44 | 45 | // Run the analysis 46 | analyze(files, reporter); 47 | 48 | verify(reporter, never()).doReportBug( 49 | bugDefinition() 50 | .bugType("UNDOCUMENTED_IGNORE") 51 | .build() 52 | ); 53 | } 54 | 55 | @Test 56 | public void testIgnoreClassWithEmptyExplanation() throws Exception { 57 | // Locate test code 58 | final String[] files = { 59 | getClassFilePath("samples/junit/IgnoreClassWithEmptyExplanation") 60 | }; 61 | 62 | // Run the analysis 63 | analyze(files, reporter); 64 | 65 | verify(reporter).doReportBug( 66 | bugDefinition() 67 | .bugType("UNDOCUMENTED_IGNORE") 68 | .inClass("IgnoreClassWithEmptyExplanation") 69 | .build() 70 | ); 71 | } 72 | 73 | @Test 74 | public void testIgnoreMethodWithEmptyExplanation() throws Exception { 75 | // Locate test code 76 | final String[] files = { 77 | getClassFilePath("samples/junit/IgnoreMethodWithEmptyExplanation") 78 | }; 79 | 80 | // Run the analysis 81 | analyze(files, reporter); 82 | 83 | verify(reporter).doReportBug( 84 | bugDefinition() 85 | .bugType("UNDOCUMENTED_IGNORE") 86 | .inClass("IgnoreMethodWithEmptyExplanation") 87 | .build() 88 | ); 89 | } 90 | 91 | @Test 92 | public void testIgnoreClassWithoutExplanation() throws Exception { 93 | // Locate test code 94 | final String[] files = { 95 | getClassFilePath("samples/junit/IgnoreClassWithoutExplanation") 96 | }; 97 | 98 | // Run the analysis 99 | analyze(files, reporter); 100 | 101 | verify(reporter).doReportBug( 102 | bugDefinition() 103 | .bugType("UNDOCUMENTED_IGNORE") 104 | .inClass("IgnoreClassWithoutExplanation") 105 | .build() 106 | ); 107 | } 108 | 109 | @Test 110 | public void testIgnoreMethodWithoutExplanation() throws Exception { 111 | // Locate test code 112 | final String[] files = { 113 | getClassFilePath("samples/junit/IgnoreMethodWithoutExplanation") 114 | }; 115 | 116 | // Run the analysis 117 | analyze(files, reporter); 118 | 119 | verify(reporter).doReportBug( 120 | bugDefinition() 121 | .bugType("UNDOCUMENTED_IGNORE") 122 | .inClass("IgnoreMethodWithoutExplanation") 123 | .build() 124 | ); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/test/java/samples/effectivejava/item10/ArrayMissingToStringClass.java: -------------------------------------------------------------------------------- 1 | package samples.effectivejava.item10; 2 | 3 | import java.util.Arrays; 4 | 5 | import javax.annotation.Nonnull; 6 | 7 | public class ArrayMissingToStringClass { 8 | 9 | private final byte[] rawData; 10 | private final String name; 11 | 12 | public ArrayMissingToStringClass(@Nonnull final byte[] rawData, @Nonnull final String name) { 13 | super(); 14 | this.rawData = Arrays.copyOf(rawData, rawData.length); 15 | this.name = name; 16 | } 17 | 18 | @Override 19 | public String toString() { 20 | // Incomplete toString, not including rawData 21 | return "ArrayMissingToStringClass [name=" + name + "]"; 22 | } 23 | 24 | @Nonnull 25 | public byte[] getRawData() { 26 | return Arrays.copyOf(rawData, rawData.length); 27 | } 28 | 29 | @Nonnull 30 | public String getName() { 31 | return name; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/test/java/samples/effectivejava/item10/BadEnumCompositeClass.java: -------------------------------------------------------------------------------- 1 | package samples.effectivejava.item10; 2 | 3 | import javax.annotation.Nonnull; 4 | 5 | public class BadEnumCompositeClass { 6 | 7 | private final EnumWithState data; 8 | private final String name; 9 | 10 | public BadEnumCompositeClass(@Nonnull final String name, 11 | @Nonnull final EnumWithState data) { 12 | super(); 13 | this.name = name; 14 | this.data = data; 15 | } 16 | 17 | @Override 18 | public String toString() { 19 | // Good toString uses all fields 20 | return "BadEnumCompositeClass [name=" + name + "]"; 21 | } 22 | 23 | @Nonnull 24 | public EnumWithState getData() { 25 | return data; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/test/java/samples/effectivejava/item10/BadToStringCompositeInterestingClass.java: -------------------------------------------------------------------------------- 1 | package samples.effectivejava.item10; 2 | 3 | import javax.annotation.Nonnull; 4 | 5 | public class BadToStringCompositeInterestingClass { 6 | 7 | private final int number; 8 | private final String name; 9 | private final GoodToStringClass gtsc; 10 | 11 | public BadToStringCompositeInterestingClass(final int number, @Nonnull final String name, 12 | @Nonnull final GoodToStringClass gtsc) { 13 | super(); 14 | this.number = number; 15 | this.name = name; 16 | this.gtsc = gtsc; 17 | } 18 | 19 | @Override 20 | public String toString() { 21 | // Good toString uses all fields 22 | return "GoodToStringCompositeClass [number=" + number + ", name=" + name + "]"; 23 | } 24 | 25 | @Nonnull 26 | public GoodToStringClass getGtsc() { 27 | return gtsc; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/test/java/samples/effectivejava/item10/EnumWithState.java: -------------------------------------------------------------------------------- 1 | package samples.effectivejava.item10; 2 | 3 | public enum EnumWithState { 4 | ONE_VALUE(true), 5 | SECOND_VALUE(false); 6 | 7 | private final boolean innerVal; 8 | 9 | private EnumWithState(final boolean val) { 10 | innerVal = val; 11 | } 12 | 13 | public boolean isInnerVal() { 14 | return innerVal; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/test/java/samples/effectivejava/item10/FakeCompleteToStringClass.java: -------------------------------------------------------------------------------- 1 | package samples.effectivejava.item10; 2 | 3 | public class FakeCompleteToStringClass { 4 | 5 | private final int number; 6 | private final String name; 7 | 8 | public FakeCompleteToStringClass(final int number, final String name) { 9 | super(); 10 | this.number = number; 11 | this.name = name; 12 | } 13 | 14 | @Override 15 | public String toString() { 16 | System.out.println(name); // but we ARE accessing name for some other reason 17 | // Incomplete toString, not including name 18 | return "FakeCompleteToStringClass [number=" + number + "]"; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/samples/effectivejava/item10/GoodMissingArrayToStringClass.java: -------------------------------------------------------------------------------- 1 | package samples.effectivejava.item10; 2 | 3 | import java.util.Arrays; 4 | 5 | import javax.annotation.Nonnull; 6 | 7 | public class GoodMissingArrayToStringClass { 8 | 9 | private final NoFieldClass[] rawData; 10 | private final String name; 11 | 12 | public GoodMissingArrayToStringClass(@Nonnull final NoFieldClass[] rawData, @Nonnull final String name) { 13 | super(); 14 | this.rawData = Arrays.copyOf(rawData, rawData.length); 15 | this.name = name; 16 | } 17 | 18 | @Override 19 | public String toString() { 20 | // valid toString, NoFieldClass has no inner state, and needs not be present 21 | return "GoodMissingArrayToStringClass [name=" + name + "]"; 22 | } 23 | 24 | @Nonnull 25 | public NoFieldClass[] getRawData() { 26 | return Arrays.copyOf(rawData, rawData.length); 27 | } 28 | 29 | @Nonnull 30 | public String getName() { 31 | return name; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/test/java/samples/effectivejava/item10/GoodSuppressedToStringComposite.java: -------------------------------------------------------------------------------- 1 | package samples.effectivejava.item10; 2 | 3 | public class GoodSuppressedToStringComposite { 4 | 5 | private final SuppressedToStringClass field; 6 | 7 | public GoodSuppressedToStringComposite(final SuppressedToStringClass field) { 8 | this.field = field; 9 | } 10 | 11 | public SuppressedToStringClass getField() { 12 | return field; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/test/java/samples/effectivejava/item10/GoodToStringAllIgnoredFieldClass.java: -------------------------------------------------------------------------------- 1 | package samples.effectivejava.item10; 2 | 3 | import javax.annotation.Nonnull; 4 | 5 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 6 | 7 | public class GoodToStringAllIgnoredFieldClass { 8 | 9 | @SuppressFBWarnings(value = "MISSING_FIELD_IN_TO_STRING", 10 | justification = "Testing ignored fields") 11 | private final int number; 12 | 13 | @SuppressFBWarnings(value = "MISSING_FIELD_IN_TO_STRING", 14 | justification = "Testing ignored fields") 15 | private final String name; 16 | 17 | public GoodToStringAllIgnoredFieldClass(final int number, @Nonnull final String name) { 18 | super(); 19 | this.number = number; 20 | this.name = name; 21 | } 22 | 23 | // No toString override, all fields are ignored 24 | } 25 | -------------------------------------------------------------------------------- /src/test/java/samples/effectivejava/item10/GoodToStringClass.java: -------------------------------------------------------------------------------- 1 | package samples.effectivejava.item10; 2 | 3 | import javax.annotation.Nonnull; 4 | 5 | public class GoodToStringClass { 6 | 7 | private final int number; 8 | private final String name; 9 | 10 | public GoodToStringClass(final int number, @Nonnull final String name) { 11 | super(); 12 | this.number = number; 13 | this.name = name; 14 | } 15 | 16 | @Override 17 | public String toString() { 18 | // Good toString uses all fields 19 | return "GoodToStringClass [number=" + number + ", name=" + name + "]"; 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/test/java/samples/effectivejava/item10/GoodToStringCompositeClass.java: -------------------------------------------------------------------------------- 1 | package samples.effectivejava.item10; 2 | 3 | public class GoodToStringCompositeClass { 4 | 5 | private final int number; 6 | private final String name; 7 | private final NoFieldClass nfc; 8 | 9 | public GoodToStringCompositeClass(final int number, final String name, final NoFieldClass nfc) { 10 | super(); 11 | this.number = number; 12 | this.name = name; 13 | this.nfc = nfc; 14 | } 15 | 16 | @Override 17 | public String toString() { 18 | // Good toString uses all fields 19 | return "GoodToStringCompositeClass [number=" + number + ", name=" + name + "]"; 20 | } 21 | 22 | public NoFieldClass getNfc() { 23 | return nfc; 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/test/java/samples/effectivejava/item10/GoodToStringIgnoredFieldClass.java: -------------------------------------------------------------------------------- 1 | package samples.effectivejava.item10; 2 | 3 | import javax.annotation.Nonnull; 4 | 5 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 6 | 7 | public class GoodToStringIgnoredFieldClass { 8 | 9 | @SuppressFBWarnings(value = "MISSING_FIELD_IN_TO_STRING", 10 | justification = "Testing ignored fields") 11 | private final int number; 12 | 13 | private final String name; 14 | 15 | public GoodToStringIgnoredFieldClass(final int number, @Nonnull final String name) { 16 | super(); 17 | this.number = number; 18 | this.name = name; 19 | } 20 | 21 | @Override 22 | public String toString() { 23 | // Good toString uses all fields not being ignored 24 | return "GoodToStringClass [name=" + name + "]"; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/test/java/samples/effectivejava/item10/GoodToStringWithStaticClass.java: -------------------------------------------------------------------------------- 1 | package samples.effectivejava.item10; 2 | 3 | public class GoodToStringWithStaticClass { 4 | 5 | public final static String NAME = "GoodToStringWithStaticClass"; 6 | 7 | private final int number; 8 | 9 | public GoodToStringWithStaticClass(final int number) { 10 | super(); 11 | this.number = number; 12 | } 13 | 14 | @Override 15 | public String toString() { 16 | // Good toString uses all non-static fields 17 | return "GoodToStringClass [number=" + number + "]"; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/test/java/samples/effectivejava/item10/IncompleteToStringClass.java: -------------------------------------------------------------------------------- 1 | package samples.effectivejava.item10; 2 | 3 | import javax.annotation.Nonnull; 4 | 5 | public class IncompleteToStringClass { 6 | 7 | private final int number; 8 | private final String name; 9 | 10 | public IncompleteToStringClass(final int number, @Nonnull final String name) { 11 | super(); 12 | this.number = number; 13 | this.name = name; 14 | } 15 | 16 | @Override 17 | public String toString() { 18 | // Incomplete toString, not including name 19 | return "IncompleteToStringClas [number=" + number + "]"; 20 | } 21 | 22 | public int getNumber() { 23 | return number; 24 | } 25 | 26 | @Nonnull 27 | public String getName() { 28 | return name; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/test/java/samples/effectivejava/item10/InnerPublicStaticClass.java: -------------------------------------------------------------------------------- 1 | package samples.effectivejava.item10; 2 | 3 | 4 | // This class has no fields, it doesn't need a toString method 5 | public final class InnerPublicStaticClass { 6 | 7 | public void doNothing() { 8 | // we do nothing! 9 | } 10 | 11 | public static class MyInnerClass { 12 | 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/test/java/samples/effectivejava/item10/MissingToStringClass.java: -------------------------------------------------------------------------------- 1 | package samples.effectivejava.item10; 2 | 3 | import javax.annotation.Nonnull; 4 | 5 | public class MissingToStringClass { 6 | 7 | private final int number; 8 | private final String name; 9 | 10 | public MissingToStringClass(final int number, @Nonnull final String name) { 11 | super(); 12 | this.number = number; 13 | this.name = name; 14 | } 15 | 16 | public int getNumber() { 17 | return number; 18 | } 19 | 20 | public String getName() { 21 | return name; 22 | } 23 | 24 | // No toString method! 25 | } 26 | -------------------------------------------------------------------------------- /src/test/java/samples/effectivejava/item10/NoFieldClass.java: -------------------------------------------------------------------------------- 1 | package samples.effectivejava.item10; 2 | 3 | // This clas has no fields, it doesn't need a toString method 4 | public class NoFieldClass { 5 | 6 | public void doNothing() { 7 | // we do nothing! 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/test/java/samples/effectivejava/item10/NoToStringLibraryFieldClass.java: -------------------------------------------------------------------------------- 1 | package samples.effectivejava.item10; 2 | 3 | import javax.annotation.Nonnull; 4 | 5 | import de.ruedigermoeller.serialization.FSTConfiguration; 6 | 7 | public class NoToStringLibraryFieldClass { 8 | 9 | // A field of type FSTConfiguration, a library with no toString() method. 10 | private final FSTConfiguration aLibraryWithNoToString = FSTConfiguration.getDefaultConfiguration(); 11 | 12 | @Nonnull 13 | public FSTConfiguration getaLibraryWithNoToString() { 14 | return aLibraryWithNoToString; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/test/java/samples/effectivejava/item10/SuppressedToStringClass.java: -------------------------------------------------------------------------------- 1 | package samples.effectivejava.item10; 2 | 3 | import javax.annotation.Nonnull; 4 | 5 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 6 | 7 | @SuppressFBWarnings(value = "MISSING_TO_STRING_OVERRIDE", justification = "This is a test") 8 | public class SuppressedToStringClass { 9 | 10 | private final int number; 11 | private final String name; 12 | 13 | public SuppressedToStringClass(final int number, @Nonnull final String name) { 14 | super(); 15 | this.number = number; 16 | this.name = name; 17 | } 18 | 19 | public int getNumber() { 20 | return number; 21 | } 22 | 23 | public String getName() { 24 | return name; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/test/java/samples/effectivejava/item10/ToStringLibraryFieldClass.java: -------------------------------------------------------------------------------- 1 | package samples.effectivejava.item10; 2 | 3 | import javax.annotation.Nonnull; 4 | 5 | import de.ruedigermoeller.heapoff.structs.structtypes.StructString; 6 | 7 | public class ToStringLibraryFieldClass { 8 | 9 | // A field of type StructString, a library with a toString() method. 10 | private final StructString aLibraryWithToString = new StructString(1); 11 | 12 | @Nonnull 13 | public StructString getaLibraryWithToString() { 14 | return aLibraryWithToString; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/test/java/samples/effectivejava/item8/BadEqualsOverride.java: -------------------------------------------------------------------------------- 1 | package samples.effectivejava.item8; 2 | 3 | 4 | public class BadEqualsOverride extends BaseConcreteClass { 5 | private int version; 6 | 7 | public BadEqualsOverride() { 8 | super(BadEqualsOverride.class.getSimpleName()); 9 | version = 1; 10 | } 11 | 12 | public int getVersion() { 13 | return version; 14 | } 15 | 16 | public void setVersion(final int version) { 17 | this.version = version; 18 | } 19 | 20 | @Override 21 | public int hashCode() { 22 | final int prime = 31; 23 | int result = super.hashCode(); 24 | result = prime * result + version; 25 | return result; 26 | } 27 | 28 | @Override 29 | public boolean equals(final Object obj) { 30 | if (this == obj) { 31 | return true; 32 | } 33 | if (!super.equals(obj)) { 34 | return false; 35 | } 36 | if (getClass() != obj.getClass()) { 37 | return false; 38 | } 39 | final BadEqualsOverride other = (BadEqualsOverride) obj; 40 | if (version != other.version) { 41 | return false; 42 | } 43 | return true; 44 | } 45 | 46 | @Override 47 | public String toString() { 48 | return "BadEqualsOverride [version=" + version + "]"; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/test/java/samples/effectivejava/item8/BaseConcreteClass.java: -------------------------------------------------------------------------------- 1 | package samples.effectivejava.item8; 2 | 3 | import javax.annotation.Nonnull; 4 | 5 | public class BaseConcreteClass { 6 | 7 | private final String name; 8 | 9 | public BaseConcreteClass(@Nonnull final String name) { 10 | this.name = name; 11 | } 12 | 13 | @Override 14 | public int hashCode() { 15 | final int prime = 31; 16 | int result = 1; 17 | result = prime * result + ((name == null) ? 0 : name.hashCode()); 18 | return result; 19 | } 20 | 21 | @Override 22 | public boolean equals(final Object obj) { 23 | if (this == obj) { 24 | return true; 25 | } 26 | if (obj == null) { 27 | return false; 28 | } 29 | if (getClass() != obj.getClass()) { 30 | return false; 31 | } 32 | final BaseConcreteClass other = (BaseConcreteClass) obj; 33 | if (name == null) { 34 | if (other.name != null) { 35 | return false; 36 | } 37 | } else if (!name.equals(other.name)) { 38 | return false; 39 | } 40 | return true; 41 | } 42 | 43 | @Override 44 | public String toString() { 45 | return "BaseConcreteClass [name=" + name + "]"; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/samples/effectivejava/item8/BaseNonEqualsConcreteClass.java: -------------------------------------------------------------------------------- 1 | package samples.effectivejava.item8; 2 | 3 | import javax.annotation.Nonnull; 4 | 5 | public class BaseNonEqualsConcreteClass { 6 | 7 | private final String name; 8 | 9 | public BaseNonEqualsConcreteClass(@Nonnull final String name) { 10 | this.name = name; 11 | } 12 | 13 | @Override 14 | public String toString() { 15 | return "BaseNonEqualsConcreteClass [name=" + name + "]"; 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/test/java/samples/effectivejava/item8/GoodEqualsOverride.java: -------------------------------------------------------------------------------- 1 | package samples.effectivejava.item8; 2 | 3 | import javax.annotation.Nonnull; 4 | 5 | 6 | public class GoodEqualsOverride extends BaseNonEqualsConcreteClass { 7 | private String code; 8 | 9 | public GoodEqualsOverride() { 10 | super(GoodEqualsOverride.class.getSimpleName()); 11 | code = "example"; 12 | } 13 | 14 | @Nonnull 15 | public String getCode() { 16 | return code; 17 | } 18 | 19 | public void setVersion(@Nonnull final String code) { 20 | this.code = code; 21 | } 22 | 23 | @Override 24 | public int hashCode() { 25 | final int prime = 31; 26 | int result = super.hashCode(); 27 | result = prime * result + code.hashCode(); 28 | return result; 29 | } 30 | 31 | @Override 32 | public boolean equals(final Object obj) { 33 | if (this == obj) { 34 | return true; 35 | } 36 | if (!super.equals(obj)) { 37 | return false; 38 | } 39 | if (getClass() != obj.getClass()) { 40 | return false; 41 | } 42 | final GoodEqualsOverride other = (GoodEqualsOverride) obj; 43 | if (!code.equals(other.code)) { 44 | return false; 45 | } 46 | return true; 47 | } 48 | 49 | @Override 50 | public String toString() { 51 | return "GoodEqualsOverride [code=" + code + "]"; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/test/java/samples/findbugs/DocumentedSuppressFBWarnings.java: -------------------------------------------------------------------------------- 1 | package samples.findbugs; 2 | 3 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 4 | 5 | public class DocumentedSuppressFBWarnings { 6 | @SuppressFBWarnings(justification = "only for unit test.") 7 | public void method() { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/test/java/samples/findbugs/DocumentedSuppressWarnings.java: -------------------------------------------------------------------------------- 1 | package samples.findbugs; 2 | 3 | import edu.umd.cs.findbugs.annotations.SuppressWarnings; 4 | 5 | @java.lang.SuppressWarnings("deprecation") 6 | @SuppressWarnings(justification = "only for unit test.") 7 | public class DocumentedSuppressWarnings { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/test/java/samples/findbugs/EqualsContainsHashCode.java: -------------------------------------------------------------------------------- 1 | package samples.findbugs; 2 | 3 | public class EqualsContainsHashCode { 4 | 5 | private static final int LONG_SHIFT_VALUE = 32; 6 | 7 | private final long id; 8 | private final long version; 9 | 10 | public EqualsContainsHashCode(final long id, final long version) { 11 | this.id = id; 12 | this.version = version; 13 | } 14 | 15 | @Override 16 | public int hashCode() { 17 | final int prime = 31; 18 | int result = 1; 19 | result = prime * result + (int) (id ^ (id >>> LONG_SHIFT_VALUE)); 20 | return result; 21 | } 22 | 23 | @Override 24 | public boolean equals(final Object obj) { 25 | if (this == obj) { 26 | return true; 27 | } 28 | if (obj == null) { 29 | return false; 30 | } 31 | if (getClass() != obj.getClass()) { 32 | return false; 33 | } 34 | final EqualsContainsHashCode other = (EqualsContainsHashCode) obj; 35 | if (id != other.id) { 36 | return false; 37 | } 38 | if (version != other.version) { 39 | return false; 40 | } 41 | return true; 42 | } 43 | 44 | @Override 45 | public String toString() { 46 | return "EqualsContainsHashCode [id=" + id + ", version=" + version + "]"; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/test/java/samples/findbugs/EqualsHashCodeDifferentFields.java: -------------------------------------------------------------------------------- 1 | package samples.findbugs; 2 | 3 | public class EqualsHashCodeDifferentFields { 4 | 5 | private static final int LONG_SHIFT_VALUE = 32; 6 | private final long id; 7 | private final long version; 8 | private final long type; 9 | 10 | public EqualsHashCodeDifferentFields(final long id, final long version, final long type) { 11 | this.id = id; 12 | this.version = version; 13 | this.type = type; 14 | } 15 | 16 | @Override 17 | public int hashCode() { 18 | final int prime = 31; 19 | int result = 1; 20 | result = prime * result + (int) (type ^ (type >>> LONG_SHIFT_VALUE)); 21 | result = prime * result + (int) (version ^ (version >>> LONG_SHIFT_VALUE)); 22 | return result; 23 | } 24 | 25 | @Override 26 | public boolean equals(final Object obj) { 27 | if (this == obj) { 28 | return true; 29 | } 30 | if (obj == null) { 31 | return false; 32 | } 33 | if (getClass() != obj.getClass()) { 34 | return false; 35 | } 36 | final EqualsHashCodeDifferentFields other = (EqualsHashCodeDifferentFields) obj; 37 | if (id != other.id) { 38 | return false; 39 | } 40 | if (type != other.type) { 41 | return false; 42 | } 43 | return true; 44 | } 45 | 46 | @Override 47 | public String toString() { 48 | return "EqualsHashCodeDifferentFields [id=" + id + ", version=" + version + ", type=" + type + "]"; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/test/java/samples/findbugs/GoodEqualsHashCodeImplementation.java: -------------------------------------------------------------------------------- 1 | package samples.findbugs; 2 | 3 | import java.util.List; 4 | 5 | import javax.annotation.Nonnull; 6 | 7 | public class GoodEqualsHashCodeImplementation { 8 | 9 | private static final int LONG_SHIFT_VALUE = 32; 10 | private final long id; 11 | private final List versions; 12 | 13 | public GoodEqualsHashCodeImplementation(final long id, @Nonnull final List versions) { 14 | this.id = id; 15 | this.versions = versions; 16 | } 17 | 18 | @Override 19 | public int hashCode() { 20 | final int prime = 31; 21 | int result = 1; 22 | result = prime * result + (int) (id ^ (id >>> LONG_SHIFT_VALUE)); 23 | result = prime * result + ((versions == null) ? 0 : versions.hashCode()); 24 | return result; 25 | } 26 | 27 | @Override 28 | public boolean equals(final Object obj) { 29 | if (this == obj) { 30 | return true; 31 | } 32 | if (obj == null) { 33 | return false; 34 | } 35 | if (getClass() != obj.getClass()) { 36 | return false; 37 | } 38 | final GoodEqualsHashCodeImplementation other = (GoodEqualsHashCodeImplementation) obj; 39 | if (id != other.id) { 40 | return false; 41 | } 42 | if (versions == null) { 43 | if (other.versions != null) { 44 | return false; 45 | } 46 | } else if (!versions.equals(other.versions)) { 47 | return false; 48 | } 49 | return true; 50 | } 51 | 52 | @Override 53 | public String toString() { 54 | return "GoodEqualsHashCodeImplementation [id=" + id + ", versions=" + versions + "]"; 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/test/java/samples/findbugs/HashCodeContainsEquals.java: -------------------------------------------------------------------------------- 1 | package samples.findbugs; 2 | 3 | public class HashCodeContainsEquals { 4 | 5 | private static final int LONG_SHIFT_VALUE = 32; 6 | private final long id; 7 | private final long version; 8 | 9 | public HashCodeContainsEquals(final long id, final long version) { 10 | this.id = id; 11 | this.version = version; 12 | } 13 | 14 | @Override 15 | public int hashCode() { 16 | final int prime = 31; 17 | int result = 1; 18 | result = prime * result + (int) (id ^ (id >>> LONG_SHIFT_VALUE)); 19 | result = prime * result + (int) (version ^ (version >>> LONG_SHIFT_VALUE)); 20 | return result; 21 | } 22 | 23 | @Override 24 | public boolean equals(final Object obj) { 25 | if (this == obj) { 26 | return true; 27 | } 28 | if (obj == null) { 29 | return false; 30 | } 31 | if (getClass() != obj.getClass()) { 32 | return false; 33 | } 34 | final HashCodeContainsEquals other = (HashCodeContainsEquals) obj; 35 | if (id != other.id) { 36 | return false; 37 | } 38 | return true; 39 | } 40 | 41 | @Override 42 | public String toString() { 43 | return "HashCodeContainsEquals [id=" + id + ", version=" + version + "]"; 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/test/java/samples/findbugs/NoEqualsHashCode.java: -------------------------------------------------------------------------------- 1 | package samples.findbugs; 2 | 3 | import javax.annotation.Nonnull; 4 | 5 | public class NoEqualsHashCode { 6 | 7 | private final long id; 8 | private final String description; 9 | 10 | public NoEqualsHashCode(final long id, @Nonnull final String description) { 11 | this.id = id; 12 | this.description = description; 13 | } 14 | 15 | public long getId() { 16 | return id; 17 | } 18 | 19 | @Nonnull 20 | public String getDescription() { 21 | return description; 22 | } 23 | 24 | @Override 25 | public String toString() { 26 | return "NoEqualsHashCode [id=" + id + ", description=" + description + "]"; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/test/java/samples/findbugs/OnlyEqualsImplementation.java: -------------------------------------------------------------------------------- 1 | package samples.findbugs; 2 | 3 | import java.util.List; 4 | 5 | import javax.annotation.Nonnull; 6 | 7 | @SuppressWarnings({ "PMD.OverrideBothEqualsAndHashcode", "checkstyle:equalshashcode" }) 8 | public class OnlyEqualsImplementation { 9 | 10 | private final List versions; 11 | 12 | public OnlyEqualsImplementation(@Nonnull final List versions) { 13 | this.versions = versions; 14 | } 15 | 16 | @Override 17 | public boolean equals(final Object obj) { 18 | if (this == obj) { 19 | return true; 20 | } 21 | if (obj == null) { 22 | return false; 23 | } 24 | if (getClass() != obj.getClass()) { 25 | return false; 26 | } 27 | final OnlyEqualsImplementation other = (OnlyEqualsImplementation) obj; 28 | if (versions == null) { 29 | if (other.versions != null) { 30 | return false; 31 | } 32 | } else if (!versions.equals(other.versions)) { 33 | return false; 34 | } 35 | return true; 36 | } 37 | 38 | @Override 39 | public String toString() { 40 | return "OnlyEqualsImplementation [versions=" + versions + "]"; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/test/java/samples/findbugs/SubclassOfBadEqualsHashCode.java: -------------------------------------------------------------------------------- 1 | package samples.findbugs; 2 | 3 | import javax.annotation.Nonnull; 4 | 5 | public class SubclassOfBadEqualsHashCode extends HashCodeContainsEquals { 6 | 7 | private final String description; 8 | 9 | public SubclassOfBadEqualsHashCode(final long id, final long version, @Nonnull final String description) { 10 | super(id, version); 11 | this.description = description; 12 | } 13 | 14 | @Nonnull 15 | public String getDescription() { 16 | return description; 17 | } 18 | 19 | @Override 20 | @Nonnull 21 | public String toString() { 22 | return "SubclassOfBadEqualsHashCode [description=" + description + "]"; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/test/java/samples/findbugs/UndocumentedSuppressFBWarnings.java: -------------------------------------------------------------------------------- 1 | package samples.findbugs; 2 | 3 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 4 | 5 | public class UndocumentedSuppressFBWarnings { 6 | @SuppressFBWarnings("SF_SWITCH_NO_DEFAULT") 7 | public void method() { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/test/java/samples/findbugs/UndocumentedSuppressWarnings.java: -------------------------------------------------------------------------------- 1 | package samples.findbugs; 2 | 3 | import edu.umd.cs.findbugs.annotations.SuppressWarnings; 4 | 5 | @java.lang.SuppressWarnings("deprecation") 6 | @SuppressWarnings("SF_SWITCH_NO_DEFAULT") 7 | public class UndocumentedSuppressWarnings { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/test/java/samples/findbugs/jdk/UselessStringValueOfCall.java: -------------------------------------------------------------------------------- 1 | package samples.findbugs.jdk; 2 | 3 | import javax.annotation.Nonnull; 4 | 5 | public class UselessStringValueOfCall { 6 | 7 | private final DummyObject dummy = new DummyObject(); 8 | 9 | @Nonnull 10 | public String getStringValueOfString() { 11 | return String.valueOf("some string"); 12 | } 13 | 14 | @Nonnull 15 | public String getStringValueOfDummyString() { 16 | return String.valueOf(dummy.getString()); 17 | } 18 | 19 | @Nonnull 20 | public String getString() { 21 | return "other string"; 22 | } 23 | 24 | @Nonnull 25 | public String getStringValueOfPrimitiveInteger() { 26 | return String.valueOf(2); 27 | } 28 | 29 | /** 30 | * Test concatenated string 31 | * @param text the text to concatenate 32 | */ 33 | public void concatenatedStringFromParam(@Nonnull final String text) { 34 | new StringBuilder("some text").append(text + " other text"); 35 | } 36 | 37 | /** 38 | * Test concatenated local string 39 | */ 40 | public void concatenatedLocalString() { 41 | final String text = " dummy "; 42 | new StringBuilder("some").append(text + "text"); 43 | } 44 | 45 | /** 46 | * Test string from parameter in a String.valueOf 47 | * @param text the text to concatenate 48 | * @return the string concatenated 49 | */ 50 | @Nonnull 51 | public String stringFromParamInValueOf(@Nonnull final String text) { 52 | return String.valueOf(text + " some text").toString(); 53 | } 54 | 55 | private static class DummyObject { 56 | public String getString() { 57 | return "dummy string"; 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /src/test/java/samples/findbugs/jdk/patterncompile/NonStaticPatternCompile.java: -------------------------------------------------------------------------------- 1 | package samples.findbugs.jdk.patterncompile; 2 | 3 | import java.util.regex.Matcher; 4 | import java.util.regex.Pattern; 5 | 6 | import javax.annotation.Nonnull; 7 | 8 | public class NonStaticPatternCompile { 9 | 10 | private static final String STRING_REGEX = "[ade]"; 11 | 12 | /** 13 | * Test non static Pattern.compile 14 | * @return a matcher 15 | */ 16 | @Nonnull 17 | public Matcher testReportNonStaticPatternCompile() { 18 | final String test = "abecedario"; 19 | final Pattern ptrn = Pattern.compile("[abc]"); 20 | return ptrn.matcher(test); 21 | } 22 | 23 | /** 24 | * Test non static Pattern.compile with static regex 25 | * @return a matcher 26 | */ 27 | @Nonnull 28 | public Matcher testReportNonStaticPatternCompileWithStaticRegex() { 29 | final String test = "cabecera"; 30 | final Pattern patcom = Pattern.compile(STRING_REGEX); 31 | return patcom.matcher(test); 32 | } 33 | 34 | /** 35 | * Test non static Pattern.compile with local regex 36 | * @return a matcher 37 | */ 38 | @Nonnull 39 | public Matcher testReportNonStaticPatternCompileWithFinalLocalRegex() { 40 | final String regex = "[bcd]"; 41 | final String test = "cabecera"; 42 | final String regex2 = regex + "[e]"; 43 | final Pattern pattern = Pattern.compile(regex2); 44 | return pattern.matcher(test); 45 | } 46 | 47 | /** 48 | * Test non static Pattern.compile with non-final local regex 49 | * @return a matcher 50 | */ 51 | @SuppressWarnings({"checkstyle:finallocalvariable", "PMD.LocalVariableCouldBeFinal"}) 52 | @Nonnull 53 | public Matcher testNeverReportNonStaticPatternCompileWithNonFinalLocalRegex() { 54 | final String regex = "[fgt]"; 55 | String regex2 = regex; 56 | final String test = "foddgata"; 57 | final Pattern pattern = Pattern.compile(regex2); 58 | return pattern.matcher(test); 59 | } 60 | 61 | /** 62 | * Test non static Pattern.compile with final regex parameter 63 | * @param regex the regex 64 | * @return a matcher 65 | */ 66 | @Nonnull 67 | public Matcher testNeverReportNonStaticPatternCompileWithFinalRegexParameter(@Nonnull final String regex) { 68 | final Pattern ptrn = Pattern.compile(regex); 69 | return ptrn.matcher("test"); 70 | } 71 | 72 | /** 73 | * Test non static Pattern.compile with non final regex parameter 74 | * @param regex the regex 75 | * @return a matcher 76 | */ 77 | @SuppressWarnings("checkstyle:finalparameters") 78 | @Nonnull 79 | public Matcher testNeverReportNonStaticPatternCompileWithNonFinalRegexParameter(@Nonnull String regex) { 80 | final Pattern ptrn = Pattern.compile(regex); 81 | return ptrn.matcher("test"); 82 | } 83 | 84 | /** 85 | * Test non static Pattern.compile with regex from object 86 | * @return a matcher 87 | */ 88 | @Nonnull 89 | public Matcher testNeverReportNonStaticPatternCompileWithRegexFromObject() { 90 | final String test = "vecindario"; 91 | final DummyRegex dummyRegex = new DummyRegex(); 92 | final Pattern pattern = Pattern.compile(dummyRegex.getRegex()); 93 | return pattern.matcher(test); 94 | } 95 | 96 | /** 97 | * Test pattern compile with a concatenated regex 98 | * @param email the email 99 | * @return The pattern with the compiled regex 100 | */ 101 | @Nonnull 102 | public Pattern testPatternCompileWithAConcatenatedRegex(@Nonnull final String email) { 103 | final String[] parts = email.split("@"); 104 | return Pattern.compile("^" + parts[0] + "\\+([^@]+)@" + parts[1] + "$"); 105 | } 106 | 107 | private static class DummyRegex { 108 | public String getRegex() { 109 | return "[cdv]"; 110 | } 111 | } 112 | } -------------------------------------------------------------------------------- /src/test/java/samples/findbugs/jdk/patterncompile/StaticPatternCompile.java: -------------------------------------------------------------------------------- 1 | package samples.findbugs.jdk.patterncompile; 2 | 3 | import java.util.regex.Matcher; 4 | import java.util.regex.Pattern; 5 | 6 | import javax.annotation.Nonnull; 7 | 8 | public class StaticPatternCompile { 9 | 10 | private static final String STRING_REGEX = "[def]"; 11 | private static final Pattern PATTERN = Pattern.compile(STRING_REGEX); 12 | 13 | /** 14 | * Test static Pattern.compile 15 | * @return a matcher 16 | */ 17 | @Nonnull 18 | public Matcher testStaticPatternCompile() { 19 | final String test = "defecto"; 20 | return PATTERN.matcher(test); 21 | } 22 | } -------------------------------------------------------------------------------- /src/test/java/samples/guava/ClassWhichCallsNormalMethod.java: -------------------------------------------------------------------------------- 1 | package samples.guava; 2 | 3 | public class ClassWhichCallsNormalMethod { 4 | public void method() { 5 | new MethodWithoutVisibleForTesting().method(); 6 | } 7 | 8 | public void anotherMethod() { 9 | System.out.println("this method invoking has no problem, because package isn't same."); 10 | method(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/samples/guava/ClassWhichCallsVisibleMethodForTesting.java: -------------------------------------------------------------------------------- 1 | package samples.guava; 2 | 3 | public class ClassWhichCallsVisibleMethodForTesting { 4 | public void method() { 5 | new MethodWithVisibleForTesting().method(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/test/java/samples/guava/JUnit3Test.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (c) tolina GmbH, 2014 3 | */ 4 | package samples.guava; 5 | 6 | import jp.co.worksap.oss.findbugs.guava.UnexpectedAccessDetector; 7 | import junit.framework.TestCase; 8 | 9 | import org.junit.Ignore; 10 | 11 | /** 12 | * Class used only for {@link UnexpectedAccessDetector}-tests 13 | * @author Juan Martin Sotuyo Dodero 14 | * 15 | */ 16 | @Ignore("Not a real test, but used as test sample") 17 | public class JUnit3Test extends TestCase { 18 | 19 | void testCallVisibleForTestingMethod() { 20 | new MethodWithVisibleForTesting().method(); 21 | } 22 | } -------------------------------------------------------------------------------- /src/test/java/samples/guava/JUnit4Test.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (c) tolina GmbH, 2014 3 | */ 4 | package samples.guava; 5 | 6 | import org.junit.After; 7 | import org.junit.Before; 8 | import org.junit.BeforeClass; 9 | import org.junit.Ignore; 10 | import org.junit.Test; 11 | 12 | /** 13 | * Class used only for 14 | * {@link jp.co.worksap.oss.findbugs.guava.UnexpectedAccessDetector}-tests 15 | * 16 | * @author tolina GmbH 17 | * 18 | */ 19 | @Ignore("Not a real test, but used as test sample") 20 | public class JUnit4Test { 21 | 22 | @Before 23 | void setUp() { 24 | new MethodWithVisibleForTesting().method(); 25 | } 26 | 27 | @BeforeClass 28 | void onlyOnce() { 29 | new MethodWithVisibleForTesting().method(); 30 | } 31 | 32 | @After 33 | void tearDown() { 34 | new MethodWithVisibleForTesting().method(); 35 | } 36 | 37 | @Test 38 | void test() { 39 | new MethodWithVisibleForTesting().method(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/test/java/samples/guava/MethodWithVisibleForTesting.java: -------------------------------------------------------------------------------- 1 | package samples.guava; 2 | 3 | import com.google.common.annotations.VisibleForTesting; 4 | 5 | public class MethodWithVisibleForTesting { 6 | @VisibleForTesting 7 | void method() { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/test/java/samples/guava/MethodWithoutVisibleForTesting.java: -------------------------------------------------------------------------------- 1 | package samples.guava; 2 | 3 | public class MethodWithoutVisibleForTesting { 4 | void method() { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/test/java/samples/jpa/ColumnWithLength.java: -------------------------------------------------------------------------------- 1 | package samples.jpa; 2 | 3 | import javax.persistence.Column; 4 | 5 | public class ColumnWithLength { 6 | @Column(length = 100) 7 | private String name; 8 | 9 | public String getName() { 10 | return name; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/samples/jpa/ColumnWithLongLengthAndLob.java: -------------------------------------------------------------------------------- 1 | package samples.jpa; 2 | 3 | import javax.persistence.Column; 4 | import javax.persistence.Lob; 5 | 6 | public class ColumnWithLongLengthAndLob { 7 | @Lob 8 | @Column(length = 10000) 9 | private String name; 10 | 11 | public String getName() { 12 | return name; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/test/java/samples/jpa/ColumnWithNegativeLength.java: -------------------------------------------------------------------------------- 1 | package samples.jpa; 2 | 3 | import javax.persistence.Column; 4 | 5 | public class ColumnWithNegativeLength { 6 | @Column(length = -1) 7 | private String name; 8 | 9 | public String getName() { 10 | return name; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/samples/jpa/ColumnWithNullable.java: -------------------------------------------------------------------------------- 1 | package samples.jpa; 2 | 3 | import javax.persistence.Column; 4 | 5 | public class ColumnWithNullable { 6 | @Column(nullable = false) 7 | private String name; 8 | 9 | public String getName() { 10 | return name; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/samples/jpa/ColumnWithTooLongLength.java: -------------------------------------------------------------------------------- 1 | package samples.jpa; 2 | 3 | import javax.persistence.Column; 4 | 5 | public class ColumnWithTooLongLength { 6 | @Column(length = 10000) 7 | private String name; 8 | 9 | public String getName() { 10 | return name; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/samples/jpa/ColumnWithoutElement.java: -------------------------------------------------------------------------------- 1 | package samples.jpa; 2 | 3 | import javax.persistence.Column; 4 | 5 | public class ColumnWithoutElement { 6 | @Column 7 | private String name; 8 | 9 | public String getName() { 10 | return name; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/samples/jpa/GetterWithLongLengthAndLob.java: -------------------------------------------------------------------------------- 1 | package samples.jpa; 2 | 3 | import javax.persistence.Column; 4 | import javax.persistence.Lob; 5 | 6 | public class GetterWithLongLengthAndLob { 7 | private String name; 8 | 9 | @Lob 10 | @Column(length = 10000) 11 | public String getName() { 12 | return name; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/test/java/samples/jpa/GetterWithTooLongLength.java: -------------------------------------------------------------------------------- 1 | package samples.jpa; 2 | 3 | import javax.persistence.Column; 4 | 5 | public class GetterWithTooLongLength { 6 | private String name; 7 | 8 | @Column(length = 10000) 9 | public String getName() { 10 | return name; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/samples/jpa/GetterWithoutElement.java: -------------------------------------------------------------------------------- 1 | package samples.jpa; 2 | 3 | import javax.persistence.Column; 4 | 5 | public class GetterWithoutElement { 6 | private String name; 7 | 8 | @Column 9 | public String getName() { 10 | return name; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/samples/jpa/LongColumnName.java: -------------------------------------------------------------------------------- 1 | package samples.jpa; 2 | 3 | import javax.persistence.Column; 4 | import javax.persistence.Entity; 5 | import javax.persistence.Id; 6 | 7 | @Entity 8 | public class LongColumnName { 9 | @Id 10 | private long id; 11 | 12 | @Column(name = "columnname_longer_than_30_bytes") 13 | private String value; 14 | 15 | public long getId() { 16 | return id; 17 | } 18 | 19 | public void setId(long id) { 20 | this.id = id; 21 | } 22 | 23 | public String getValue() { 24 | return value; 25 | } 26 | 27 | public void setValue(String value) { 28 | this.value = value; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/test/java/samples/jpa/LongColumnNameByAnnotatedMethod.java: -------------------------------------------------------------------------------- 1 | package samples.jpa; 2 | 3 | import javax.persistence.Column; 4 | import javax.persistence.Entity; 5 | import javax.persistence.Id; 6 | 7 | @Entity 8 | public class LongColumnNameByAnnotatedMethod { 9 | @Id 10 | private long id; 11 | 12 | private String longColumnNameByAnnotatedMethod; 13 | 14 | public long getId() { 15 | return id; 16 | } 17 | 18 | public void setId(long id) { 19 | this.id = id; 20 | } 21 | 22 | @Column 23 | public String getSomething() { // Plugin has to alert even if method name is short 24 | return longColumnNameByAnnotatedMethod; 25 | } 26 | 27 | public void setLongColumnNameByAnnotatedMethod( 28 | String longColumnNameByAnnotatedMethod) { 29 | this.longColumnNameByAnnotatedMethod = longColumnNameByAnnotatedMethod; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/test/java/samples/jpa/LongColumnNameWithoutAnnotationParameter.java: -------------------------------------------------------------------------------- 1 | package samples.jpa; 2 | 3 | import javax.persistence.Column; 4 | import javax.persistence.Entity; 5 | import javax.persistence.Id; 6 | 7 | @Entity 8 | public class LongColumnNameWithoutAnnotationParameter { 9 | @Id 10 | private long id; 11 | 12 | @Column 13 | private String longColumnNameWithoutAnnotationParameter; 14 | 15 | public long getId() { 16 | return id; 17 | } 18 | 19 | public void setId(long id) { 20 | this.id = id; 21 | } 22 | 23 | public String getLongColumnNameWithoutAnnotationParameter() { 24 | return longColumnNameWithoutAnnotationParameter; 25 | } 26 | 27 | public void setLongColumnNameWithoutAnnotationParameter( 28 | String longColumnNameWithoutAnnotationParameter) { 29 | this.longColumnNameWithoutAnnotationParameter = longColumnNameWithoutAnnotationParameter; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/test/java/samples/jpa/LongIndexNameForHibernate.java: -------------------------------------------------------------------------------- 1 | package samples.jpa; 2 | 3 | import javax.persistence.Column; 4 | import javax.persistence.Entity; 5 | import javax.persistence.Id; 6 | 7 | import org.hibernate.annotations.Index; 8 | 9 | @Entity 10 | public class LongIndexNameForHibernate { 11 | @Id 12 | private long id; 13 | 14 | @Column(nullable = true) 15 | @Index(name = "index_name_longer_than_30_bytes") 16 | private String value; 17 | 18 | public long getId() { 19 | return id; 20 | } 21 | 22 | public void setId(long id) { 23 | this.id = id; 24 | } 25 | 26 | public String getValue() { 27 | return value; 28 | } 29 | 30 | public void setValue(String value) { 31 | this.value = value; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/samples/jpa/LongIndexNameForOpenJPA.java: -------------------------------------------------------------------------------- 1 | package samples.jpa; 2 | 3 | import javax.persistence.Column; 4 | import javax.persistence.Entity; 5 | import javax.persistence.Id; 6 | 7 | import org.apache.openjpa.persistence.jdbc.Index; 8 | 9 | @Entity 10 | public class LongIndexNameForOpenJPA { 11 | @Id 12 | private long id; 13 | 14 | @Column(nullable = true) 15 | @Index(name = "index_name_longer_than_30_bytes") 16 | private String value; 17 | 18 | public long getId() { 19 | return id; 20 | } 21 | 22 | public void setId(long id) { 23 | this.id = id; 24 | } 25 | 26 | public String getValue() { 27 | return value; 28 | } 29 | 30 | public void setValue(String value) { 31 | this.value = value; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/samples/jpa/LongTableName.java: -------------------------------------------------------------------------------- 1 | package samples.jpa; 2 | 3 | import javax.persistence.Entity; 4 | import javax.persistence.Id; 5 | 6 | @Entity(name="table_name_longer_than_30_bytes") 7 | public class LongTableName { 8 | 9 | @Id 10 | private long id; 11 | 12 | public LongTableName(long id) { 13 | this.id = id; 14 | } 15 | 16 | public long getId() { 17 | return id; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/samples/jpa/LongTableNameWithoutAnnotationParameter.java: -------------------------------------------------------------------------------- 1 | package samples.jpa; 2 | 3 | import javax.persistence.Entity; 4 | import javax.persistence.Id; 5 | 6 | @Entity 7 | public class LongTableNameWithoutAnnotationParameter { 8 | 9 | @Id 10 | private long id; 11 | 12 | public LongTableNameWithoutAnnotationParameter(long id) { 13 | this.id = id; 14 | } 15 | 16 | public long getId() { 17 | return id; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/samples/jpa/NonNullablePrimitiveColumn.java: -------------------------------------------------------------------------------- 1 | package samples.jpa; 2 | 3 | import javax.persistence.Column; 4 | import javax.persistence.Entity; 5 | 6 | @Entity 7 | public class NonNullablePrimitiveColumn { 8 | @Column(nullable = false) 9 | private boolean booleanValue; 10 | 11 | @Column(nullable = false) 12 | private byte byteValue; 13 | 14 | @Column(nullable = false) 15 | private short shortValue; 16 | 17 | @Column(nullable = false) 18 | private int intValue; 19 | 20 | @Column(nullable = false) 21 | private long longValue; 22 | 23 | @Column(nullable = false) 24 | private float floatValue; 25 | 26 | @Column(nullable = false) 27 | private double doubleValue; 28 | } 29 | -------------------------------------------------------------------------------- /src/test/java/samples/jpa/NullableBooleanColumn.java: -------------------------------------------------------------------------------- 1 | package samples.jpa; 2 | 3 | import javax.persistence.Column; 4 | import javax.persistence.Entity; 5 | 6 | @Entity 7 | public class NullableBooleanColumn { 8 | @Column(nullable = true) 9 | private boolean booleanValue; 10 | } 11 | -------------------------------------------------------------------------------- /src/test/java/samples/jpa/NullableBooleanGetter.java: -------------------------------------------------------------------------------- 1 | package samples.jpa; 2 | 3 | import javax.persistence.Column; 4 | import javax.persistence.Entity; 5 | 6 | @Entity 7 | public class NullableBooleanGetter { 8 | private boolean booleanValue; 9 | 10 | @Column(nullable = true) 11 | public boolean isBooleanValue() { 12 | return booleanValue; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/test/java/samples/jpa/NullableByteColumn.java: -------------------------------------------------------------------------------- 1 | package samples.jpa; 2 | 3 | import javax.persistence.Column; 4 | import javax.persistence.Entity; 5 | 6 | @Entity 7 | public class NullableByteColumn { 8 | @Column(nullable = true) 9 | private byte byteValue; 10 | } 11 | -------------------------------------------------------------------------------- /src/test/java/samples/jpa/NullableDoubleColumn.java: -------------------------------------------------------------------------------- 1 | package samples.jpa; 2 | 3 | import javax.persistence.Column; 4 | import javax.persistence.Entity; 5 | 6 | @Entity 7 | public class NullableDoubleColumn { 8 | @Column(nullable = true) 9 | private double doubleValue; 10 | } 11 | -------------------------------------------------------------------------------- /src/test/java/samples/jpa/NullableFloatColumn.java: -------------------------------------------------------------------------------- 1 | package samples.jpa; 2 | 3 | import javax.persistence.Column; 4 | import javax.persistence.Entity; 5 | 6 | @Entity 7 | public class NullableFloatColumn { 8 | @Column(nullable = true) 9 | private float floatValue; 10 | } 11 | -------------------------------------------------------------------------------- /src/test/java/samples/jpa/NullableIntColumn.java: -------------------------------------------------------------------------------- 1 | package samples.jpa; 2 | 3 | import javax.persistence.Column; 4 | import javax.persistence.Entity; 5 | 6 | @Entity 7 | public class NullableIntColumn { 8 | @Column(nullable = true) 9 | private int intValue; 10 | } 11 | -------------------------------------------------------------------------------- /src/test/java/samples/jpa/NullableLongColumn.java: -------------------------------------------------------------------------------- 1 | package samples.jpa; 2 | 3 | import javax.persistence.Column; 4 | import javax.persistence.Entity; 5 | 6 | @Entity 7 | public class NullableLongColumn { 8 | @Column(nullable = true) 9 | private long longValue; 10 | } 11 | -------------------------------------------------------------------------------- /src/test/java/samples/jpa/NullableShortColumn.java: -------------------------------------------------------------------------------- 1 | package samples.jpa; 2 | 3 | import javax.persistence.Column; 4 | import javax.persistence.Entity; 5 | 6 | @Entity 7 | public class NullableShortColumn { 8 | @Column(nullable = true) 9 | private short shortValue; 10 | } 11 | -------------------------------------------------------------------------------- /src/test/java/samples/jpa/ShortColumnName.java: -------------------------------------------------------------------------------- 1 | package samples.jpa; 2 | 3 | import javax.persistence.Column; 4 | import javax.persistence.Entity; 5 | import javax.persistence.Id; 6 | 7 | @Entity 8 | public class ShortColumnName { 9 | @Id 10 | private long id; 11 | 12 | @Column(name = "col_name_shorter_than_31_bytes") 13 | private String value; 14 | 15 | public long getId() { 16 | return id; 17 | } 18 | 19 | public void setId(long id) { 20 | this.id = id; 21 | } 22 | 23 | public String getValue() { 24 | return value; 25 | } 26 | 27 | public void setValue(String value) { 28 | this.value = value; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/test/java/samples/jpa/ShortColumnNameWithoutAnnotationParameter.java: -------------------------------------------------------------------------------- 1 | package samples.jpa; 2 | 3 | import javax.persistence.Column; 4 | import javax.persistence.Entity; 5 | import javax.persistence.Id; 6 | 7 | @Entity 8 | public class ShortColumnNameWithoutAnnotationParameter { 9 | @Id 10 | private long id; 11 | 12 | @Column 13 | private String shortColumnNameNoAnnotationPrm; 14 | 15 | private String another; 16 | 17 | public long getId() { 18 | return id; 19 | } 20 | 21 | public void setId(long id) { 22 | this.id = id; 23 | } 24 | 25 | public String getShortColumnNameNoAnnotationPrm() { 26 | return shortColumnNameNoAnnotationPrm; 27 | } 28 | 29 | public void setShortColumnNameNoAnnotationPrm( 30 | String shortColumnNameNoAnnotationPrm) { 31 | this.shortColumnNameNoAnnotationPrm = shortColumnNameNoAnnotationPrm; 32 | } 33 | 34 | @Column 35 | public String getAnother() { 36 | return another; 37 | } 38 | 39 | public void setAnother(String another) { 40 | this.another = another; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/test/java/samples/jpa/ShortIndexNameForHibernate.java: -------------------------------------------------------------------------------- 1 | package samples.jpa; 2 | 3 | import javax.persistence.Column; 4 | import javax.persistence.Entity; 5 | import javax.persistence.Id; 6 | 7 | import org.hibernate.annotations.Index; 8 | 9 | @Entity 10 | public class ShortIndexNameForHibernate { 11 | @Id 12 | private long id; 13 | 14 | @Column(nullable = true) 15 | @Index(name = "idx_name_shorter_than_31_bytes") 16 | private String value; 17 | 18 | public long getId() { 19 | return id; 20 | } 21 | 22 | public void setId(long id) { 23 | this.id = id; 24 | } 25 | 26 | public String getValue() { 27 | return value; 28 | } 29 | 30 | public void setValue(String value) { 31 | this.value = value; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/samples/jpa/ShortIndexNameForOpenJPA.java: -------------------------------------------------------------------------------- 1 | package samples.jpa; 2 | 3 | import javax.persistence.Column; 4 | import javax.persistence.Entity; 5 | import javax.persistence.Id; 6 | 7 | import org.apache.openjpa.persistence.jdbc.Index; 8 | 9 | @Entity 10 | public class ShortIndexNameForOpenJPA { 11 | @Id 12 | private long id; 13 | 14 | @Column(nullable = true) 15 | @Index(name = "idx_name_shorter_than_31_bytes") 16 | private String value; 17 | 18 | public long getId() { 19 | return id; 20 | } 21 | 22 | public void setId(long id) { 23 | this.id = id; 24 | } 25 | 26 | public String getValue() { 27 | return value; 28 | } 29 | 30 | public void setValue(String value) { 31 | this.value = value; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/samples/jpa/ShortTableName.java: -------------------------------------------------------------------------------- 1 | package samples.jpa; 2 | 3 | import javax.persistence.Entity; 4 | import javax.persistence.Id; 5 | 6 | @Entity(name="tbl_name_shorter_than_31_bytes") 7 | public class ShortTableName { 8 | 9 | @Id 10 | private long id; 11 | 12 | public ShortTableName(long id) { 13 | this.id = id; 14 | } 15 | 16 | public long getId() { 17 | return id; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/samples/jpa/ShortTableNameNoAnnotationPara.java: -------------------------------------------------------------------------------- 1 | package samples.jpa; 2 | 3 | import javax.persistence.Entity; 4 | import javax.persistence.Id; 5 | 6 | @Entity 7 | public class ShortTableNameNoAnnotationPara { 8 | 9 | @Id 10 | private long id; 11 | 12 | public ShortTableNameNoAnnotationPara(long id) { 13 | this.id = id; 14 | } 15 | 16 | public long getId() { 17 | return id; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/samples/jpa/UseColumnDefinition.java: -------------------------------------------------------------------------------- 1 | package samples.jpa; 2 | 3 | import javax.persistence.Column; 4 | import javax.persistence.Entity; 5 | import javax.persistence.Id; 6 | 7 | @Entity 8 | public class UseColumnDefinition { 9 | @Id 10 | private long id; 11 | 12 | @Column(columnDefinition = "NVARCHAR2(100)") 13 | private String value; 14 | 15 | public long getId() { 16 | return id; 17 | } 18 | 19 | public void setId(long id) { 20 | this.id = id; 21 | } 22 | 23 | public String getValue() { 24 | return value; 25 | } 26 | 27 | public void setValue(String value) { 28 | this.value = value; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/test/java/samples/jsr305/BadImmutableClass.java: -------------------------------------------------------------------------------- 1 | package samples.jsr305; 2 | 3 | import javax.annotation.concurrent.Immutable; 4 | import javax.annotation.concurrent.NotThreadSafe; 5 | 6 | @NotThreadSafe 7 | @Immutable // marked as immutable, but field is not final 8 | public class BadImmutableClass { 9 | public String value; 10 | } 11 | -------------------------------------------------------------------------------- /src/test/java/samples/jsr305/ExtendsMutableClass.java: -------------------------------------------------------------------------------- 1 | package samples.jsr305; 2 | 3 | import javax.annotation.concurrent.Immutable; 4 | 5 | 6 | @Immutable // this class looks immutable, but it extends mutable class 7 | public final class ExtendsMutableClass extends BadImmutableClass { 8 | private final String string; 9 | 10 | public ExtendsMutableClass(String string) { 11 | this.string = string; 12 | } 13 | 14 | public String getString() { 15 | return string; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/test/java/samples/jsr305/GoodMutableClass.java: -------------------------------------------------------------------------------- 1 | package samples.jsr305; 2 | 3 | // Not marked as immutable 4 | public class GoodMutableClass { 5 | public String value; 6 | } 7 | -------------------------------------------------------------------------------- /src/test/java/samples/jsr305/ImmutableEnum.java: -------------------------------------------------------------------------------- 1 | package samples.jsr305; 2 | 3 | import javax.annotation.concurrent.Immutable; 4 | 5 | @Immutable 6 | public enum ImmutableEnum { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/test/java/samples/jsr305/nullness/AnnotatedArgument.java: -------------------------------------------------------------------------------- 1 | package samples.jsr305.nullness; 2 | 3 | import javax.annotation.Nullable; 4 | 5 | public class AnnotatedArgument { 6 | public void methodWithNullableArgument(@Nullable Object value) {} 7 | } 8 | -------------------------------------------------------------------------------- /src/test/java/samples/jsr305/nullness/AnnotatedArgumentsEnum.java: -------------------------------------------------------------------------------- 1 | package samples.jsr305.nullness; 2 | 3 | import javax.annotation.Nonnull; 4 | 5 | public enum AnnotatedArgumentsEnum { 6 | VALUE("whatever"); 7 | 8 | private AnnotatedArgumentsEnum(@Nonnull final String value) { 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/samples/jsr305/nullness/AnnotatedClass.java: -------------------------------------------------------------------------------- 1 | package samples.jsr305.nullness; 2 | 3 | import javax.annotation.ParametersAreNonnullByDefault; 4 | 5 | @ParametersAreNonnullByDefault 6 | public class AnnotatedClass { 7 | public void method(Object value) {} 8 | } 9 | -------------------------------------------------------------------------------- /src/test/java/samples/jsr305/nullness/AnnotatedInnerClassArguments.java: -------------------------------------------------------------------------------- 1 | package samples.jsr305.nullness; 2 | 3 | import javax.annotation.Nonnull; 4 | 5 | 6 | public class AnnotatedInnerClassArguments { 7 | 8 | public class Inner { 9 | public void method(@Nonnull Object value) {} 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/samples/jsr305/nullness/AnnotatedMethod.java: -------------------------------------------------------------------------------- 1 | package samples.jsr305.nullness; 2 | 3 | import javax.annotation.ParametersAreNonnullByDefault; 4 | 5 | public class AnnotatedMethod { 6 | @ParametersAreNonnullByDefault 7 | public void methodAnnotatedWithByDefault(Object value) {} 8 | } 9 | -------------------------------------------------------------------------------- /src/test/java/samples/jsr305/nullness/AnnotatedReturnValue.java: -------------------------------------------------------------------------------- 1 | package samples.jsr305.nullness; 2 | 3 | import javax.annotation.Nullable; 4 | 5 | public class AnnotatedReturnValue { 6 | @Nullable 7 | public Object method() { 8 | return null; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/test/java/samples/jsr305/nullness/AnonymousClassConstructor.java: -------------------------------------------------------------------------------- 1 | package samples.jsr305.nullness; 2 | 3 | import java.util.Comparator; 4 | 5 | import javax.annotation.Nonnull; 6 | 7 | 8 | 9 | public class AnonymousClassConstructor { 10 | 11 | public void method() { 12 | @SuppressWarnings("unused") 13 | final Comparator comparator = new Comparator() { 14 | 15 | @Override 16 | public int compare(@Nonnull Long o1, @Nonnull Long o2) { 17 | return 0; 18 | } 19 | }; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/test/java/samples/jsr305/nullness/ChildOfUnannotatedBoundGenerics.java: -------------------------------------------------------------------------------- 1 | package samples.jsr305.nullness; 2 | 3 | import java.util.Iterator; 4 | 5 | public class ChildOfUnannotatedBoundGenerics extends UnannotatedBoundGenerics implements Iterable { 6 | 7 | @Override 8 | public int compareTo(final UnannotatedBoundGenerics o) { 9 | return 0; 10 | } 11 | 12 | @Override 13 | public Iterator iterator() { 14 | return null; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/test/java/samples/jsr305/nullness/ComplexGenerics.java: -------------------------------------------------------------------------------- 1 | package samples.jsr305.nullness; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.concurrent.TimeUnit; 6 | 7 | import javax.annotation.concurrent.GuardedBy; 8 | 9 | import rx.Observable; 10 | import rx.Scheduler; 11 | import rx.Subscriber; 12 | import rx.Subscription; 13 | import rx.exceptions.Exceptions; 14 | import rx.functions.Action0; 15 | import rx.observers.SerializedSubscriber; 16 | import rx.schedulers.Schedulers; 17 | 18 | /** 19 | * Rx Operator: Store items in a buffer, emits items if the buffer is full or by timeout. 20 | * If the buffer is full the operator will emit the items. 21 | * If the timeout is consumed and the buffer is not empty the items will be emitted. 22 | * onComplete emits remaining items. 23 | * onError will not emit any remaining items. 24 | */ 25 | public final class ComplexGenerics implements Observable.Operator, T> { 26 | private final long timeout; 27 | private final TimeUnit unit; 28 | private final int count; 29 | private final Scheduler scheduler; 30 | private Subscription timeoutSubscription; 31 | 32 | /** 33 | * @param count the maximum size of the buffer. Once a buffer reaches this size, it is emitted 34 | * @param timeout the amount of time all chunks must be actively collect values before being emitted 35 | * @param unit the {@link TimeUnit} defining the unit of time for the timeout 36 | */ 37 | public ComplexGenerics(final int count, final long timeout, final TimeUnit unit) { 38 | this(count, timeout, unit, Schedulers.computation()); 39 | } 40 | 41 | /** 42 | * @param count the maximum size of the buffer. Once a buffer reaches this size, it is emitted 43 | * @param timeout the amount of time all chunks must be actively collect values before being emitted 44 | * @param unit the {@link TimeUnit} defining the unit of time for the timeout 45 | * @param scheduler the {@link Scheduler} to use for timeout 46 | */ 47 | public ComplexGenerics(final int count, final long timeout, final TimeUnit unit, final Scheduler scheduler) { 48 | this.count = count; 49 | this.timeout = timeout >= 0 ? timeout : 0; 50 | this.unit = unit; 51 | this.scheduler = scheduler; 52 | } 53 | 54 | @Override 55 | public Subscriber call(final Subscriber> child) { 56 | final Scheduler.Worker inner = scheduler.createWorker(); 57 | final SerializedSubscriber> serialized = new SerializedSubscriber<>(child); 58 | final BufferSubscriber bufferSubscriber = new BufferSubscriber(serialized, inner); 59 | bufferSubscriber.add(inner); 60 | child.add(bufferSubscriber); 61 | return bufferSubscriber; 62 | } 63 | 64 | private final class BufferSubscriber extends Subscriber { 65 | /* default */ final Subscriber> child; 66 | /* default */ final Scheduler.Worker inner; 67 | /* default */ List chunk; 68 | /* default */ boolean done; 69 | 70 | BufferSubscriber(final Subscriber> child, final Scheduler.Worker inner) { 71 | this.child = child; 72 | this.inner = inner; 73 | this.chunk = new ArrayList<>(); 74 | } 75 | 76 | @Override 77 | public void onNext(final T t) { 78 | synchronized (this) { 79 | unsubscribeTimeout(); 80 | if (done) { 81 | return; 82 | } 83 | chunk.add(t); 84 | if (chunk.size() == count) { 85 | emit(); 86 | } else { 87 | scheduleTimeout(); 88 | } 89 | } 90 | } 91 | 92 | @Override 93 | public void onError(final Throwable e) { 94 | synchronized (this) { 95 | unsubscribeTimeout(); 96 | done = true; 97 | chunk = null; 98 | child.onError(e); 99 | unsubscribe(); 100 | } 101 | } 102 | 103 | @Override 104 | public void onCompleted() { 105 | synchronized (this) { 106 | unsubscribeTimeout(); 107 | emit(); 108 | done = true; 109 | chunk = null; 110 | child.onCompleted(); 111 | unsubscribe(); 112 | } 113 | } 114 | 115 | private void unsubscribeTimeout() { 116 | if (timeoutSubscription != null && !timeoutSubscription.isUnsubscribed()) { 117 | timeoutSubscription.unsubscribe(); 118 | } 119 | } 120 | 121 | private void scheduleTimeout() { 122 | timeoutSubscription = inner.schedule(new Action0() { 123 | @Override 124 | public void call() { 125 | synchronized (this) { 126 | emit(); 127 | } 128 | } 129 | }, timeout, unit); 130 | } 131 | 132 | @GuardedBy("this") 133 | private void emit() { 134 | if (done) { 135 | return; 136 | } 137 | final List toEmit; 138 | toEmit = chunk; 139 | chunk = new ArrayList<>(); 140 | if (toEmit != null && !toEmit.isEmpty()) { 141 | try { 142 | child.onNext(toEmit); 143 | } catch (final Throwable t) { 144 | Exceptions.throwOrReport(t, this); 145 | } 146 | } 147 | } 148 | 149 | @Override 150 | public String toString() { 151 | return "BufferSubscriber{" 152 | + "child=" + child 153 | + ", inner=" + inner 154 | + ", chunk=" + chunk 155 | + ", done=" + done 156 | + '}'; 157 | } 158 | } 159 | 160 | @Override 161 | public String toString() { 162 | return "BufferTimeoutOperator{" 163 | + "timeout=" + timeout 164 | + ", unit=" + unit 165 | + ", count=" + count 166 | + ", scheduler=" + scheduler 167 | + ", timeoutSubscription=" + timeoutSubscription 168 | + '}'; 169 | } 170 | } -------------------------------------------------------------------------------- /src/test/java/samples/jsr305/nullness/NoAnnotation.java: -------------------------------------------------------------------------------- 1 | package samples.jsr305.nullness; 2 | 3 | public class NoAnnotation { 4 | public void method(Object value) {} 5 | } 6 | -------------------------------------------------------------------------------- /src/test/java/samples/jsr305/nullness/PrimitiveArgument.java: -------------------------------------------------------------------------------- 1 | package samples.jsr305.nullness; 2 | 3 | public class PrimitiveArgument { 4 | public void method(int value) {} 5 | } 6 | -------------------------------------------------------------------------------- /src/test/java/samples/jsr305/nullness/StandardEnum.java: -------------------------------------------------------------------------------- 1 | package samples.jsr305.nullness; 2 | 3 | public enum StandardEnum { 4 | PROCESS, CULTURE, AVAILABLE_DEVICES, 5 | EQUIPEMENT_REQUEST, SUPPORT_REQUEST, 6 | DEVICE_REQUEST, MANAGEMENT_ORDERS; 7 | } 8 | -------------------------------------------------------------------------------- /src/test/java/samples/jsr305/nullness/UnannotatedBoundGenerics.java: -------------------------------------------------------------------------------- 1 | package samples.jsr305.nullness; 2 | 3 | public class UnannotatedBoundGenerics implements Comparable { 4 | 5 | @Override 6 | public int compareTo(final UnannotatedBoundGenerics o) { 7 | return 0; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/test/java/samples/jsr305/nullness/UnannotatedEnumLookAlike.java: -------------------------------------------------------------------------------- 1 | package samples.jsr305.nullness; 2 | 3 | 4 | public class UnannotatedEnumLookAlike { 5 | 6 | /* 7 | * The signature of this method matches that of an enum's values(), 8 | * but it's not and should be reported 9 | */ 10 | public static UnannotatedEnumLookAlike[] values() { 11 | return null; 12 | } 13 | 14 | /* 15 | * The signature of this method matches that of an enum's valueOf(String), 16 | * but it's not and should be reported 17 | */ 18 | public static UnannotatedEnumLookAlike valueOf(final String s) { 19 | return null; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/test/java/samples/jsr305/nullness/UnannotatedExtendingLibClass.java: -------------------------------------------------------------------------------- 1 | package samples.jsr305.nullness; 2 | 3 | import java.io.IOException; 4 | 5 | import de.ruedigermoeller.heapoff.FSTCompressed; 6 | import de.ruedigermoeller.serialization.FSTConfiguration; 7 | 8 | 9 | public class UnannotatedExtendingLibClass extends FSTCompressed { 10 | 11 | @Override 12 | public void set(String object) throws IOException { 13 | super.set(object); 14 | } 15 | 16 | @Override 17 | protected void storeArray(byte[] buffer, int written) { 18 | } 19 | 20 | @Override 21 | protected FSTConfiguration getConf() { 22 | return null; 23 | } 24 | 25 | @Override 26 | public byte[] getArray() { 27 | return null; 28 | } 29 | 30 | @Override 31 | public int getLen() { 32 | return 0; 33 | } 34 | 35 | @Override 36 | public int getOffset() { 37 | return 0; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/test/java/samples/jsr305/nullness/UnannotatedExtendingLibClassPropagatingGenerics.java: -------------------------------------------------------------------------------- 1 | package samples.jsr305.nullness; 2 | 3 | import de.ruedigermoeller.heapoff.FSTByteBufferOffheap; 4 | import de.ruedigermoeller.heapoff.FSTOffHeapMap; 5 | 6 | 7 | public class UnannotatedExtendingLibClassPropagatingGenerics extends FSTOffHeapMap { 8 | 9 | public UnannotatedExtendingLibClassPropagatingGenerics( 10 | final FSTByteBufferOffheap heap) { 11 | super(heap); 12 | } 13 | 14 | @Override 15 | public Long put(String key, Long value) { 16 | return super.put(key, value); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/test/java/samples/jsr305/nullness/UnannotatedIndirectGenericBinding.java: -------------------------------------------------------------------------------- 1 | package samples.jsr305.nullness; 2 | 3 | import java.util.HashMap; 4 | 5 | 6 | // Used generic types are purposely evil 7 | public class UnannotatedIndirectGenericBinding extends HashMap { 8 | 9 | private static final long serialVersionUID = 7026748794809094126L; 10 | 11 | public static class Bounded extends UnannotatedIndirectGenericBinding { 12 | 13 | private static final long serialVersionUID = 3142640311424915721L; 14 | 15 | @Override 16 | public Long put(final String key, final Long value) { 17 | return super.put(key, value); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/samples/jsr305/nullness/UnannotatedInnerClassArguments.java: -------------------------------------------------------------------------------- 1 | package samples.jsr305.nullness; 2 | 3 | 4 | 5 | public class UnannotatedInnerClassArguments { 6 | 7 | public class Inner { 8 | public void method(Object value) {} 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/test/java/samples/jsr305/nullness/UnannotatedReturnValue.java: -------------------------------------------------------------------------------- 1 | package samples.jsr305.nullness; 2 | 3 | public class UnannotatedReturnValue { 4 | public Object method() { 5 | return null; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/test/java/samples/jsr305/nullness/UnannotatedVarargs.java: -------------------------------------------------------------------------------- 1 | package samples.jsr305.nullness; 2 | 3 | 4 | public class UnannotatedVarargs { 5 | public void methodWithNullableArgument(Object... values) {} 6 | } 7 | -------------------------------------------------------------------------------- /src/test/java/samples/jsr305/nullness/annotatedpackage/AnnotatedPackage.java: -------------------------------------------------------------------------------- 1 | package samples.jsr305.nullness.annotatedpackage; 2 | 3 | public class AnnotatedPackage { 4 | public void method(Object value) {} 5 | } 6 | -------------------------------------------------------------------------------- /src/test/java/samples/jsr305/nullness/annotatedpackage/package-info.java: -------------------------------------------------------------------------------- 1 | @javax.annotation.ParametersAreNonnullByDefault 2 | package samples.jsr305.nullness.annotatedpackage; 3 | -------------------------------------------------------------------------------- /src/test/java/samples/junit/IgnoreClassWithEmptyExplanation.java: -------------------------------------------------------------------------------- 1 | package samples.junit; 2 | 3 | import org.junit.Ignore; 4 | 5 | @Ignore("") 6 | public class IgnoreClassWithEmptyExplanation { 7 | } 8 | -------------------------------------------------------------------------------- /src/test/java/samples/junit/IgnoreClassWithExplanation.java: -------------------------------------------------------------------------------- 1 | package samples.junit; 2 | 3 | import org.junit.Ignore; 4 | 5 | @Ignore("Good explanation to tell the reason.") 6 | public class IgnoreClassWithExplanation { 7 | } 8 | -------------------------------------------------------------------------------- /src/test/java/samples/junit/IgnoreClassWithoutExplanation.java: -------------------------------------------------------------------------------- 1 | package samples.junit; 2 | 3 | import org.junit.Ignore; 4 | 5 | @Ignore 6 | public class IgnoreClassWithoutExplanation { 7 | } 8 | -------------------------------------------------------------------------------- /src/test/java/samples/junit/IgnoreMethodWithEmptyExplanation.java: -------------------------------------------------------------------------------- 1 | package samples.junit; 2 | 3 | import org.junit.Ignore; 4 | 5 | public class IgnoreMethodWithEmptyExplanation { 6 | @Ignore("") 7 | public void method() { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/test/java/samples/junit/IgnoreMethodWithExplanation.java: -------------------------------------------------------------------------------- 1 | package samples.junit; 2 | 3 | import org.junit.Ignore; 4 | 5 | public class IgnoreMethodWithExplanation { 6 | @Ignore("Good explanation to tell the reason.") 7 | public void method() { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/test/java/samples/junit/IgnoreMethodWithoutExplanation.java: -------------------------------------------------------------------------------- 1 | package samples.junit; 2 | 3 | import org.junit.Ignore; 4 | 5 | public class IgnoreMethodWithoutExplanation { 6 | @Ignore 7 | public void method() { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/test/java/samples/system/UseSystemErr.java: -------------------------------------------------------------------------------- 1 | package samples.system; 2 | 3 | public class UseSystemErr { 4 | 5 | public void test() { 6 | System.err.println("hello world"); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/test/java/samples/system/UseSystemOut.java: -------------------------------------------------------------------------------- 1 | package samples.system; 2 | 3 | public class UseSystemOut { 4 | 5 | public void test() { 6 | System.out.println("hello world"); 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/test/resources/fst-1.63.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monits/findbugs-plugin/6f12771c65be30b2a96d24bab8d6c07f40822fc9/src/test/resources/fst-1.63.jar -------------------------------------------------------------------------------- /src/test/resources/rxjava-1.0.16.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monits/findbugs-plugin/6f12771c65be30b2a96d24bab8d6c07f40822fc9/src/test/resources/rxjava-1.0.16.jar --------------------------------------------------------------------------------