21
21
import java .util .ArrayDeque ;
22
22
import java .util .BitSet ;
23
23
import java .util .Deque ;
24
+ import java .util .HashMap ;
25
+ import java .util .Map ;
24
26
import java .util .Set ;
25
27
26
28
import javax .annotation .Nullable ;
35
37
import org .apache .bcel .classfile .JavaClass ;
36
38
import org .apache .bcel .classfile .Method ;
37
39
40
+ import com .mebigfatguy .fbcontrib .collect .MethodInfo ;
41
+ import com .mebigfatguy .fbcontrib .collect .Statistics ;
38
42
import com .mebigfatguy .fbcontrib .utils .BugType ;
39
43
import com .mebigfatguy .fbcontrib .utils .FQMethod ;
40
44
import com .mebigfatguy .fbcontrib .utils .SignatureBuilder ;
46
50
import edu .umd .cs .findbugs .BytecodeScanningDetector ;
47
51
import edu .umd .cs .findbugs .OpcodeStack ;
48
52
import edu .umd .cs .findbugs .OpcodeStack .CustomUserValue ;
53
+ import edu .umd .cs .findbugs .SourceLineAnnotation ;
49
54
import edu .umd .cs .findbugs .ba .ClassContext ;
55
+ import edu .umd .cs .findbugs .ba .SignatureParser ;
50
56
import edu .umd .cs .findbugs .ba .XMethod ;
51
57
52
58
/**
55
61
@ CustomUserValue
56
62
public class OptionalIssues extends BytecodeScanningDetector {
57
63
64
+ private enum OptionalType {
65
+ PLAIN ,
66
+ BOXED
67
+ }
68
+
69
+ private static final String OPTIONAL_SIGNATURE = "Ljava/util/Optional;" ;
58
70
private static Set <String > BOXED_OPTIONAL_TYPES = UnmodifiableSet .create ("Ljava/lang/Integer;" , "Ljava/lang/Long;" ,
59
71
"Ljava/lang/Double;" );
60
72
@@ -99,6 +111,8 @@ OPTIONAL_OR_ELSE_METHOD, new FQMethod("java/util/OptionalDouble", "orElse", "(D)
99
111
private OpcodeStack stack ;
100
112
private JavaClass currentClass ;
101
113
private Deque <ActiveStackOp > activeStackOps ;
114
+ private Map <OpcodeStack .Item , SourceLineAnnotation > boxedItems = new HashMap <>();
115
+ private boolean methodIsConstrained ;
102
116
103
117
static {
104
118
INVOKE_OPS .set (Const .INVOKEINTERFACE );
@@ -155,7 +169,21 @@ public void visitClassContext(ClassContext classContext) {
155
169
public void visitCode (Code obj ) {
156
170
stack .resetForMethodEntry (this );
157
171
activeStackOps .clear ();
172
+ boxedItems .clear ();
173
+ methodIsConstrained = false ;
174
+
175
+ String returnType = new SignatureParser (getMethodSig ()).getReturnTypeSignature ();
176
+ if (OPTIONAL_SIGNATURE .equals (returnType )) {
177
+ MethodInfo mi = Statistics .getStatistics ().getMethodStatistics (getClassName (), getMethodName (), getMethodSig ());
178
+ methodIsConstrained = mi != null && mi .isDerived ();
179
+ }
158
180
super .visitCode (obj );
181
+
182
+ for (SourceLineAnnotation slAnno : boxedItems .values ()) {
183
+ bugReporter .reportBug (
184
+ new BugInstance (this , BugType .OI_OPTIONAL_ISSUES_PRIMITIVE_VARIANT_PREFERRED .name (),
185
+ LOW_PRIORITY ).addClass (this ).addMethod (this ).addSourceLine (slAnno ));
186
+ }
159
187
}
160
188
161
189
/**
@@ -168,7 +196,7 @@ public void visitCode(Code obj) {
168
196
@ Override
169
197
public void sawOpcode (int seen ) {
170
198
FQMethod curCalledMethod = null ;
171
- Boolean sawPlainOptional = null ;
199
+ OptionalType optionalType = null ;
172
200
173
201
try {
174
202
switch (seen ) {
@@ -207,9 +235,7 @@ public void sawOpcode(int seen) {
207
235
OpcodeStack .Item itm = stack .getStackItem (0 );
208
236
String itmSig = itm .getSignature ();
209
237
if (BOXED_OPTIONAL_TYPES .contains (itmSig )) {
210
- bugReporter .reportBug (
211
- new BugInstance (this , BugType .OI_OPTIONAL_ISSUES_PRIMITIVE_VARIANT_PREFERRED .name (),
212
- LOW_PRIORITY ).addClass (this ).addMethod (this ).addSourceLine (this ));
238
+ optionalType = OptionalType .BOXED ;
213
239
}
214
240
}
215
241
}
@@ -245,7 +271,7 @@ public void sawOpcode(int seen) {
245
271
}
246
272
}
247
273
if (OPTIONAL_OR_ELSE_METHOD .equals (curCalledMethod )) {
248
- sawPlainOptional = Boolean . TRUE ;
274
+ ; optionalType = OptionalType . PLAIN ;
249
275
}
250
276
} else if (OR_ELSE_GET_METHODS .contains (curCalledMethod )) {
251
277
if (!activeStackOps .isEmpty ()) {
@@ -276,12 +302,18 @@ public void sawOpcode(int seen) {
276
302
}
277
303
}
278
304
if (OPTIONAL_OR_ELSE_GET_METHOD .equals (curCalledMethod )) {
279
- sawPlainOptional = Boolean . TRUE ;
305
+ optionalType = OptionalType . PLAIN ;
280
306
}
281
307
} else if (OPTIONAL_GET_METHOD .equals (curCalledMethod )) {
282
- sawPlainOptional = Boolean . TRUE ;
308
+ optionalType = OptionalType . PLAIN ;
283
309
}
284
310
break ;
311
+
312
+ case Const .ARETURN :
313
+ if (methodIsConstrained && stack .getStackDepth () > 0 ) {
314
+ boxedItems .remove (stack .getStackItem (0 ));
315
+ }
316
+ break ;
285
317
}
286
318
} catch (ClassNotFoundException e ) {
287
319
bugReporter .reportMissingClass (e );
@@ -295,9 +327,13 @@ public void sawOpcode(int seen) {
295
327
while (activeStackOps .size () > stackDepth ) {
296
328
activeStackOps .removeFirst ();
297
329
}
298
- if (sawPlainOptional != null ) {
330
+ if (optionalType != null ) {
299
331
OpcodeStack .Item itm = stack .getStackItem (0 );
300
- itm .setUserValue (sawPlainOptional );
332
+ itm .setUserValue (optionalType );
333
+ if (optionalType == OptionalType .BOXED ) {
334
+ boxedItems .put (itm , SourceLineAnnotation .fromVisitedInstruction (OptionalIssues .this .getClassContext (),
335
+ OptionalIssues .this , getPC ()));
336
+ }
301
337
}
302
338
}
303
339
}
0 commit comments