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}