001package apps; 002 003/** 004 * Check how SpotBugs (formally FindBugs) and annotations interact. 005 * <p> 006 * Note: This deliberately causes SpotBugs warnings! Do not remove or annotate them! 007 * Instead, when past its useful point, just comment out the body of the 008 * class so as to leave the code examples present. 009 * <p> 010 * Tests Nonnull, Nullable and CheckForNull from 011 * javax.annotation annotations. 012 * <p> 013 * This has no main() because it's not expected to run: Many methods will certainly 014 * throw a NullPointerException right away. The idea is for SpotBugs to 015 * find those in static analysis. This is in java/src, instead of java/test, 016 * so that our usual CI infrastructure builds it. 017 * <p> 018 * Annotations are explicitly qualified (instead of using 'import') to make it 019 * completely clear which is being used at each point. That makes this the 020 * code less readable, so it's not recommended for general use. 021 * <p> 022 * The "ja" prefix means that javax annotations are used. "no" means un-annotated. 023 * <p> 024 * A previous version (Git SHA 4049c5d690) also had "fb" as a prefix 025 * for using the edu.umd.cs.findbugs.annotations form of annotations. 026 * There were found to work exactly the same as the (preferred) javax.annotation forms, 027 * including when intermixed with each other. 028 * <p> 029 * The comments are the warnings thrown (and not thrown) by SpotBugs 4.52 030 * <p> 031 * Summary: <ul> 032 * <li>Parameter declaration handling: 033 * <ul> 034 * <li>@Nonnull means that references are assumed OK 035 * <li>Both @CheckForNull and @Nullable are checked for dereferences 036 * <li>Parameters with no annotation are not checked (i.e. acts like @Nonnull, no null checks required before dereferencing) 037 * </ul> 038 * <li>Passing explicit null parameters 039 * <ul> 040 * <li>@Nonnull will flag a passed null 041 * <li>@CheckForNull and @Nullable accept a passed null (but previously flagged any dereferences in the method declaration, i.e. that the annotation wasn't OK) 042 * <li>No annotation results in a NP_NULL_PARAM_DEREF_ALL_TARGETS_DANGEROUS warning from analyzing the body of the method, effectively working like @Nonnull with a different error 043 * </ul> 044 * <li>Parameter passing of return values isn't always checked by SpotBugs. 045 * For example, a @CheckForNull return value is accepted for an @Nonnull parameter. 046 * Perhaps this will improve with time. 047 * <li>Return values are properly checked for @CheckForNull, but not for @Nullable. 048 * <ul> 049 * <li>A @CheckForNull return value is flagged if it's dereferenced. 050 * <li>A @Nullable return value is <u>not</u> flagged if it's dereferenced. 051 * <li>Return values without annotation are also not flagged when dereferenced. 052 * </ul> 053 * </ul> 054 * Bottom line: When flagging return values, use @CheckForNull. 055 * @see apps.CheckerFrameworkCheck 056 * @author Bob Jacobsen 2016, 2019, 2021 057 */ 058public class FindBugsCheck { 059 060 void test() { // something that has to be executed on an object 061 System.out.println("test "+this.getClass()); 062 } 063 064 // comment out the rest of the file to avoid SpotBugs counting these deliberate warnings 065 066/* 067 068 // Test runtime null checks 069 public void checkNullWithAssert(@javax.annotation.Nonnull FindBugsCheck param) { 070 assert param != null; 071 param.test(); 072 } 073 public void checkNullWithObjects(@javax.annotation.Nonnull FindBugsCheck param) { 074 java.util.Objects.requireNonNull(param, "checking parameter for non-null"); 075 param.test(); 076 } 077 public void checkNullWithIf(@javax.annotation.Nonnull FindBugsCheck param) { 078 if (param == null) log.error("checking parameter for non-null"); // should be RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE: Redundant nullcheck of value known to be non-null 079 param.test(); 080 } 081 082 // Test checking with no annotations 083 084 public FindBugsCheck noAnnotationReturn() { 085 return null; 086 } 087 public void noAnnotationParm(FindBugsCheck p) { 088 p.test(); 089 } 090 public void noAnnotationTest() { 091 FindBugsCheck p; 092 093 noAnnotationReturn().test(); 094 p = noAnnotationReturn(); 095 p.test(); 096 097 noAnnotationParm(this); 098 noAnnotationParm(null); // Null passed for non-null parameter NP_NULL_PARAM_DEREF_ALL_TARGETS_DANGEROUS 099 100 noAnnotationParm(noAnnotationReturn()); // maybe should be flagged? 101 p = noAnnotationReturn(); 102 noAnnotationParm(p); 103 104 noAnnotationParm(jaNonnullReturn()); 105 p = jaNonnullReturn(); 106 noAnnotationParm(p); 107 108 noAnnotationParm(jaNullableReturn()); // maybe should be flagged? 109 p = jaNullableReturn(); 110 noAnnotationParm(p); 111 112 noAnnotationParm(jaCheckForNullReturn()); // maybe should be flagged? 113 p = jaCheckForNullReturn(); 114 noAnnotationParm(p); 115 } 116 117 // Test checking Nonnull annotations 118 119 @javax.annotation.Nonnull public FindBugsCheck jaNonnullReturn() { 120 return null; // may return null, but is declared @Nonnull NP_NONNULL_RETURN_VIOLATION 121 } 122 public void jaNonNullParm(@javax.annotation.Nonnull FindBugsCheck p) { 123 p.test(); 124 } 125 public void jaTestNonnull() { 126 FindBugsCheck p; 127 128 jaNonnullReturn().test(); 129 p = jaNonnullReturn(); 130 if (p!=null) p.test(); // Redundant nullcheck of p, which is known to be non-null RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE 131 132 jaNonNullParm(this); 133 jaNonNullParm(null); // Null passed for non-null parameter NP_NONNULL_PARAM_VIOLATION 134 135 jaNonNullParm(noAnnotationReturn()); // should be flagged 136 p = noAnnotationReturn(); 137 jaNonNullParm(p); 138 139 jaNonNullParm(jaNonnullReturn()); 140 p = jaNonnullReturn(); 141 jaNonNullParm(p); 142 143 jaNonNullParm(jaNullableReturn()); // should be flagged 144 p = jaNullableReturn(); 145 jaNonNullParm(p); 146 147 jaNonNullParm(jaCheckForNullReturn()); // should be flagged 148 p = jaCheckForNullReturn(); 149 jaNonNullParm(p); 150 151 } 152 153 // Test checking Nullable annotations 154 155 @javax.annotation.Nullable public FindBugsCheck jaNullableReturn() { 156 return null; 157 } 158 public void jaNullableParm(@javax.annotation.Nullable FindBugsCheck p) { 159 p.test(); // p must be non-null but is marked as nullable NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE 160 } 161 public void jaTestNullable() { 162 FindBugsCheck p; 163 164 jaNullableReturn().test(); // should be flagged 165 p = jaNullableReturn(); 166 if (p!=null) p.test(); 167 168 jaNullableParm(this); 169 jaNullableParm(null); 170 171 jaNullableParm(noAnnotationReturn()); 172 p = noAnnotationReturn(); 173 jaNullableParm(p); 174 175 jaNullableParm(jaNonnullReturn()); 176 p = jaNonnullReturn(); 177 jaNullableParm(p); 178 179 jaNullableParm(jaNullableReturn()); 180 p = jaNullableReturn(); 181 jaNullableParm(p); 182 183 jaNullableParm(jaCheckForNullReturn()); 184 p = jaCheckForNullReturn(); 185 jaNullableParm(p); 186 187 } 188 189 // Test CheckForNull 190 191 @javax.annotation.CheckForNull public FindBugsCheck jaCheckForNullReturn() { 192 return null; 193 } 194 public void jaCheckForNullParm(@javax.annotation.CheckForNull FindBugsCheck p) { 195 p.test(); // p must be non-null but is marked as nullable NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE 196 } 197 public void jaTestCheckForNull() { 198 FindBugsCheck p; 199 200 jaCheckForNullReturn().test(); // Possible null pointer dereference NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE 201 p = jaCheckForNullReturn(); 202 if (p!=null) p.test(); 203 204 jaCheckForNullParm(this); 205 jaCheckForNullParm(null); 206 207 jaCheckForNullParm(noAnnotationReturn()); 208 p = noAnnotationReturn(); 209 jaCheckForNullParm(p); 210 211 jaCheckForNullParm(jaNonnullReturn()); 212 p = jaNonnullReturn(); 213 jaCheckForNullParm(p); 214 215 jaCheckForNullParm(jaNullableReturn()); 216 p = jaNullableReturn(); 217 jaCheckForNullParm(p); 218 219 jaCheckForNullParm(jaCheckForNullReturn()); 220 p = jaCheckForNullReturn(); 221 jaCheckForNullParm(p); 222 } 223 224 225 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(FindBugsCheck.class); 226 227*/ 228 // end of commenting out file 229}