KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > edu > umd > cs > findbugs > detect > FindNullDeref


1 /*
2  * FindBugs - Find bugs in Java programs
3  * Copyright (C) 2003-2005 University of Maryland
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18  */

19
20 package edu.umd.cs.findbugs.detect;
21
22 import java.util.BitSet JavaDoc;
23 import java.util.HashSet JavaDoc;
24 import java.util.Iterator JavaDoc;
25 import java.util.LinkedList JavaDoc;
26 import java.util.List JavaDoc;
27 import java.util.Set JavaDoc;
28 import java.util.SortedSet JavaDoc;
29 import java.util.TreeSet JavaDoc;
30
31 import org.apache.bcel.Constants;
32 import org.apache.bcel.classfile.JavaClass;
33 import org.apache.bcel.classfile.Method;
34 import org.apache.bcel.generic.ATHROW;
35 import org.apache.bcel.generic.ConstantPoolGen;
36 import org.apache.bcel.generic.Instruction;
37 import org.apache.bcel.generic.InstructionHandle;
38 import org.apache.bcel.generic.InstructionTargeter;
39 import org.apache.bcel.generic.InvokeInstruction;
40 import org.apache.bcel.generic.MethodGen;
41 import org.apache.bcel.generic.PUTFIELD;
42 import org.apache.bcel.generic.ReturnInstruction;
43 import org.apache.bcel.generic.Type;
44
45 import edu.umd.cs.findbugs.BugAnnotation;
46 import edu.umd.cs.findbugs.BugInstance;
47 import edu.umd.cs.findbugs.BugReporter;
48 import edu.umd.cs.findbugs.Detector;
49 import edu.umd.cs.findbugs.FieldAnnotation;
50 import edu.umd.cs.findbugs.FindBugsAnalysisFeatures;
51 import edu.umd.cs.findbugs.LocalVariableAnnotation;
52 import edu.umd.cs.findbugs.SourceLineAnnotation;
53 import edu.umd.cs.findbugs.SystemProperties;
54 import edu.umd.cs.findbugs.annotations.NonNull;
55 import edu.umd.cs.findbugs.ba.AnalysisContext;
56 import edu.umd.cs.findbugs.ba.BasicBlock;
57 import edu.umd.cs.findbugs.ba.CFG;
58 import edu.umd.cs.findbugs.ba.CFGBuilderException;
59 import edu.umd.cs.findbugs.ba.ClassContext;
60 import edu.umd.cs.findbugs.ba.DataflowAnalysisException;
61 import edu.umd.cs.findbugs.ba.DataflowValueChooser;
62 import edu.umd.cs.findbugs.ba.Edge;
63 import edu.umd.cs.findbugs.ba.Frame;
64 import edu.umd.cs.findbugs.ba.Hierarchy;
65 import edu.umd.cs.findbugs.ba.JavaClassAndMethod;
66 import edu.umd.cs.findbugs.ba.Location;
67 import edu.umd.cs.findbugs.ba.MissingClassException;
68 import edu.umd.cs.findbugs.ba.NullnessAnnotation;
69 import edu.umd.cs.findbugs.ba.NullnessAnnotationDatabase;
70 import edu.umd.cs.findbugs.ba.SignatureConverter;
71 import edu.umd.cs.findbugs.ba.XFactory;
72 import edu.umd.cs.findbugs.ba.XField;
73 import edu.umd.cs.findbugs.ba.XMethod;
74 import edu.umd.cs.findbugs.ba.interproc.PropertyDatabase;
75 import edu.umd.cs.findbugs.ba.npe.IsNullValue;
76 import edu.umd.cs.findbugs.ba.npe.IsNullValueDataflow;
77 import edu.umd.cs.findbugs.ba.npe.IsNullValueFrame;
78 import edu.umd.cs.findbugs.ba.npe.NullDerefAndRedundantComparisonCollector;
79 import edu.umd.cs.findbugs.ba.npe.NullDerefAndRedundantComparisonFinder;
80 import edu.umd.cs.findbugs.ba.npe.ParameterNullnessProperty;
81 import edu.umd.cs.findbugs.ba.npe.ParameterNullnessPropertyDatabase;
82 import edu.umd.cs.findbugs.ba.npe.RedundantBranch;
83 import edu.umd.cs.findbugs.ba.type.TypeDataflow;
84 import edu.umd.cs.findbugs.ba.type.TypeFrame;
85 import edu.umd.cs.findbugs.ba.vna.AvailableLoad;
86 import edu.umd.cs.findbugs.ba.vna.ValueNumber;
87 import edu.umd.cs.findbugs.ba.vna.ValueNumberDataflow;
88 import edu.umd.cs.findbugs.ba.vna.ValueNumberFrame;
89 import edu.umd.cs.findbugs.classfile.FieldDescriptor;
90 import edu.umd.cs.findbugs.props.GeneralWarningProperty;
91 import edu.umd.cs.findbugs.props.WarningPropertySet;
92 import edu.umd.cs.findbugs.props.WarningPropertyUtil;
93 import edu.umd.cs.findbugs.visitclass.Util;
94
95 /**
96  * A Detector to find instructions where a NullPointerException
97  * might be raised. We also look for useless reference comparisons
98  * involving null and non-null values.
99  *
100  * @author David Hovemeyer
101  * @author William Pugh
102  * @see edu.umd.cs.findbugs.ba.npe.IsNullValueAnalysis
103  */

104 public class FindNullDeref
105         implements Detector, NullDerefAndRedundantComparisonCollector {
106
107     private static final boolean DEBUG = SystemProperties.getBoolean("fnd.debug");
108     private static final boolean DEBUG_NULLARG = SystemProperties.getBoolean("fnd.debug.nullarg");
109     private static final boolean DEBUG_NULLRETURN = SystemProperties.getBoolean("fnd.debug.nullreturn");
110     private static final boolean REPORT_SAFE_METHOD_TARGETS = true;
111
112     private static final String JavaDoc METHOD = SystemProperties.getProperty("fnd.method");
113     private static final String JavaDoc CLASS = SystemProperties.getProperty("fnd.class");
114     
115     // Fields
116
private BugReporter bugReporter;
117     
118     // Cached database stuff
119
private ParameterNullnessPropertyDatabase unconditionalDerefParamDatabase;
120     private boolean checkedDatabases = false;
121
122     
123     // Transient state
124
private ClassContext classContext;
125     private Method method;
126     private IsNullValueDataflow invDataflow;
127     private BitSet JavaDoc previouslyDeadBlocks;
128     private NullnessAnnotation methodAnnotation;
129
130     public FindNullDeref(BugReporter bugReporter) {
131         this.bugReporter = bugReporter;
132     }
133
134     public void visitClassContext(ClassContext classContext) {
135         this.classContext = classContext;
136         
137         String JavaDoc currentMethod = null;
138
139         JavaClass jclass = classContext.getJavaClass();
140         String JavaDoc className = jclass.getClassName();
141         if (CLASS != null && !className.equals(CLASS)) return;
142         Method[] methodList = jclass.getMethods();
143         for (Method method : methodList) {
144             try {
145                 if (method.isAbstract() || method.isNative()
146                         || method.getCode() == null)
147                     continue;
148                 
149                 MethodGen mg = classContext.getMethodGen(method);
150                 if (mg == null) {
151                     continue;
152                 }
153                 currentMethod = SignatureConverter.convertMethodSignature(mg);
154
155
156                 if (METHOD != null && !method.getName().equals(METHOD))
157                     continue;
158                 if (DEBUG)
159                     System.out
160                             .println("Checking for NP in " + currentMethod);
161                 analyzeMethod(classContext, method);
162             } catch (MissingClassException e) {
163                 bugReporter.reportMissingClass(e.getClassNotFoundException());
164             } catch (DataflowAnalysisException e) {
165                 bugReporter.logError("While analyzing " + currentMethod + ": FindNullDeref caught dae exception", e);
166             } catch (CFGBuilderException e) {
167                 bugReporter.logError("While analyzing " + currentMethod + ": FindNullDeref caught cfgb exception", e);
168             }
169
170         }
171     }
172
173     private void analyzeMethod(ClassContext classContext, Method method)
174             throws CFGBuilderException, DataflowAnalysisException {
175         if (DEBUG || DEBUG_NULLARG)
176             System.out.println("Pre FND ");
177
178         MethodGen methodGen = classContext.getMethodGen(method);
179         if (methodGen == null)
180             return;
181         if (!checkedDatabases) {
182             checkDatabases();
183             checkedDatabases = true;
184         }
185         
186         this.method = method;
187         this.methodAnnotation = getMethodNullnessAnnotation();
188
189
190         if (DEBUG || DEBUG_NULLARG)
191             System.out.println("FND: " + SignatureConverter.convertMethodSignature(methodGen));
192
193         this.previouslyDeadBlocks = findPreviouslyDeadBlocks();
194         
195         // Get the IsNullValueDataflow for the method from the ClassContext
196
invDataflow = classContext.getIsNullValueDataflow(method);
197         
198         // Create a NullDerefAndRedundantComparisonFinder object to do the actual
199
// work. It will call back to report null derefs and redundant null comparisons
200
// through the NullDerefAndRedundantComparisonCollector interface we implement.
201
NullDerefAndRedundantComparisonFinder worker = new NullDerefAndRedundantComparisonFinder(
202                 classContext,
203                 method,
204                 this);
205         worker.execute();
206
207
208         checkCallSitesAndReturnInstructions();
209         
210     }
211
212     /**
213      * Find set of blocks which were known to be dead before doing the
214      * null pointer analysis.
215      *
216      * @return set of previously dead blocks, indexed by block id
217      * @throws CFGBuilderException
218      * @throws DataflowAnalysisException
219      */

220     private BitSet JavaDoc findPreviouslyDeadBlocks() throws DataflowAnalysisException, CFGBuilderException {
221         BitSet JavaDoc deadBlocks = new BitSet JavaDoc();
222         ValueNumberDataflow vnaDataflow = classContext.getValueNumberDataflow(method);
223         for (Iterator JavaDoc<BasicBlock> i = vnaDataflow.getCFG().blockIterator(); i.hasNext();) {
224             BasicBlock block = i.next();
225             ValueNumberFrame vnaFrame = vnaDataflow.getStartFact(block);
226             if (vnaFrame.isTop()) {
227                 deadBlocks.set(block.getId());
228             }
229         }
230         
231         return deadBlocks;
232     }
233
234     /**
235      * Check whether or not the various interprocedural databases we can
236      * use exist and are nonempty.
237      */

238     private void checkDatabases() {
239         AnalysisContext analysisContext = AnalysisContext
240                 .currentAnalysisContext();
241         unconditionalDerefParamDatabase = analysisContext
242                 .getUnconditionalDerefParamDatabase();
243         if (false) {
244         XFactory xFactory = AnalysisContext.currentXFactory();
245         
246         for (XMethod xMethod : unconditionalDerefParamDatabase.getKeys()) {
247             XMethod m2 = xFactory.intern(xMethod);
248             if (!xMethod.equals(m2)) {
249                 System.out.println("WWW: " + xMethod);
250                 System.out.println(" ->: " + m2);
251             }
252         }
253         }
254     }
255
256     private<
257         DatabaseType extends PropertyDatabase<?,?>> boolean isDatabaseNonEmpty(DatabaseType database) {
258         return database != null && !database.isEmpty();
259     }
260
261     /**
262      * See if the currently-visited method declares a @NonNull annotation,
263      * or overrides a method which declares a @NonNull annotation.
264      */

265     private NullnessAnnotation getMethodNullnessAnnotation() {
266         
267         if (method.getSignature().indexOf(")L") >= 0 || method.getSignature().indexOf(")[") >= 0 ) {
268             if (DEBUG_NULLRETURN) {
269                 System.out.println("Checking return annotation for " +
270                         SignatureConverter.convertMethodSignature(classContext.getJavaClass(), method));
271             }
272             
273             XMethod m = XFactory.createXMethod(classContext.getJavaClass(), method);
274             return AnalysisContext.currentAnalysisContext().getNullnessAnnotationDatabase()
275             .getResolvedAnnotation(m, false);
276         }
277         return NullnessAnnotation.UNKNOWN_NULLNESS;
278     }
279
280     private void checkCallSitesAndReturnInstructions()
281             throws CFGBuilderException, DataflowAnalysisException {
282         ConstantPoolGen cpg = classContext.getConstantPoolGen();
283         TypeDataflow typeDataflow = classContext.getTypeDataflow(method);
284         
285         for (Iterator JavaDoc<Location> i = classContext.getCFG(method).locationIterator(); i.hasNext();) {
286             Location location = i.next();
287             Instruction ins = location.getHandle().getInstruction();
288             try {
289                 if (ins instanceof InvokeInstruction) {
290                     examineCallSite(location, cpg, typeDataflow);
291                 } else if (methodAnnotation == NullnessAnnotation.NONNULL && ins.getOpcode() == Constants.ARETURN) {
292                     examineReturnInstruction(location);
293                 } else if (ins instanceof PUTFIELD) {
294                     examinePutfieldInstruction(location, (PUTFIELD) ins, cpg);
295                 }
296             } catch (ClassNotFoundException JavaDoc e) {
297                 bugReporter.reportMissingClass(e);
298             }
299         }
300     }
301
302     private void examineCallSite(
303             Location location,
304             ConstantPoolGen cpg,
305             TypeDataflow typeDataflow)
306             throws DataflowAnalysisException, CFGBuilderException, ClassNotFoundException JavaDoc {
307         
308         InvokeInstruction invokeInstruction = (InvokeInstruction)
309             location.getHandle().getInstruction();
310         
311         
312         String JavaDoc methodName = invokeInstruction.getName(cpg);
313         String JavaDoc signature = invokeInstruction.getSignature(cpg);
314         
315         // Don't check equals() calls.
316
// If an equals() call unconditionally dereferences the parameter,
317
// it is the fault of the method, not the caller.
318
if (methodName.equals("equals") && signature.equals("(Ljava/lang/Object;)Z"))
319             return;
320         
321         int returnTypeStart = signature.indexOf(')');
322         if (returnTypeStart < 0)
323             return;
324         String JavaDoc paramList = signature.substring(0, returnTypeStart + 1);
325         
326         if (paramList.equals("()") ||
327                 (paramList.indexOf("L") < 0 && paramList.indexOf('[') < 0))
328             // Method takes no arguments, or takes no reference arguments
329
return;
330
331         // See if any null arguments are passed
332
IsNullValueFrame frame =
333             classContext.getIsNullValueDataflow(method).getFactAtLocation(location);
334         if (!frame.isValid())
335             return;
336         BitSet JavaDoc nullArgSet = frame.getArgumentSet(invokeInstruction, cpg, new DataflowValueChooser<IsNullValue>() {
337             public boolean choose(IsNullValue value) {
338                 // Only choose non-exception values.
339
// Values null on an exception path might be due to
340
// infeasible control flow.
341
return value.mightBeNull() && !value.isException() && !value.isReturnValue();
342             }
343         });
344         BitSet JavaDoc definitelyNullArgSet = frame.getArgumentSet(invokeInstruction, cpg, new DataflowValueChooser<IsNullValue>() {
345             public boolean choose(IsNullValue value) {
346                 return value.isDefinitelyNull();
347             }
348         });
349         if (nullArgSet.isEmpty())
350             return;
351         if (DEBUG_NULLARG) {
352             System.out.println("Null arguments passed: " + nullArgSet);
353             System.out.println("Frame is: " + frame);
354             System.out.println("# arguments: " + frame.getNumArguments(invokeInstruction, cpg));
355             XMethod xm = XFactory.createXMethod(invokeInstruction, cpg);
356             System.out.print("Signature: " + xm.getSignature());
357         }
358         
359         if (unconditionalDerefParamDatabase != null) {
360             checkUnconditionallyDereferencedParam(location, cpg, typeDataflow, invokeInstruction, nullArgSet, definitelyNullArgSet);
361         }
362         
363         
364         if (DEBUG_NULLARG) {
365             System.out.println("Checking nonnull params");
366         }
367         checkNonNullParam(location, cpg, typeDataflow, invokeInstruction, nullArgSet, definitelyNullArgSet);
368         
369     }
370     
371     private void examinePutfieldInstruction(Location location, PUTFIELD ins, ConstantPoolGen cpg) throws DataflowAnalysisException, CFGBuilderException {
372         
373         IsNullValueDataflow invDataflow = classContext.getIsNullValueDataflow(method);
374         IsNullValueFrame frame = invDataflow.getFactAtLocation(location);
375         if (!frame.isValid())
376             return;
377         IsNullValue tos = frame.getTopValue();
378         if (tos.mightBeNull()) {
379             XField field = XFactory.createXField(ins, cpg);
380             NullnessAnnotation annotation = AnalysisContext.currentAnalysisContext().getNullnessAnnotationDatabase().getResolvedAnnotation(field, false);
381             if (annotation == NullnessAnnotation.NONNULL) {
382         
383             MethodGen methodGen = classContext.getMethodGen(method);
384             String JavaDoc sourceFile = classContext.getJavaClass().getSourceFileName();
385                         
386             BugInstance warning = new BugInstance("NP_STORE_INTO_NONNULL_FIELD", tos.isDefinitelyNull() ?
387                     HIGH_PRIORITY : NORMAL_PRIORITY)
388                 .addClassAndMethod(methodGen, sourceFile)
389                 .addField(field)
390                 .addSourceLine(classContext, methodGen, sourceFile, location.getHandle());
391             
392             bugReporter.reportBug(warning);
393             }
394         }
395     }
396     private void examineReturnInstruction(Location location) throws DataflowAnalysisException, CFGBuilderException {
397         if (DEBUG_NULLRETURN) {
398             System.out.println("Checking null return at " + location);
399         }
400         
401         IsNullValueDataflow invDataflow = classContext.getIsNullValueDataflow(method);
402         IsNullValueFrame frame = invDataflow.getFactAtLocation(location);
403         if (!frame.isValid())
404             return;
405         IsNullValue tos = frame.getTopValue();
406         if (tos.mightBeNull()) {
407             MethodGen methodGen = classContext.getMethodGen(method);
408             String JavaDoc sourceFile = classContext.getJavaClass().getSourceFileName();
409                         
410             String JavaDoc bugPattern = "NP_NONNULL_RETURN_VIOLATION";
411             String JavaDoc methodName = method.getName();
412             if (methodName.equals("clone"))
413                 bugPattern = "NP_CLONE_COULD_RETURN_NULL";
414             else if (methodName.equals("toString"))
415                 bugPattern = "NP_TOSTRING_COULD_RETURN_NULL";
416             BugInstance warning = new BugInstance(bugPattern, tos.isDefinitelyNull() ?
417                     HIGH_PRIORITY : NORMAL_PRIORITY)
418                 .addClassAndMethod(methodGen, sourceFile)
419                 .addSourceLine(classContext, methodGen, sourceFile, location.getHandle());
420             
421             bugReporter.reportBug(warning);
422         }
423     }
424
425     private void checkUnconditionallyDereferencedParam(
426             Location location,
427             ConstantPoolGen cpg,
428             TypeDataflow typeDataflow,
429             InvokeInstruction invokeInstruction,
430             BitSet JavaDoc nullArgSet, BitSet JavaDoc definitelyNullArgSet) throws DataflowAnalysisException, ClassNotFoundException JavaDoc {
431         
432         boolean caught = inCatchNullBlock(location);
433         if (caught && skipIfInsideCatchNull()) return;
434     
435         // See what methods might be called here
436
TypeFrame typeFrame = typeDataflow.getFactAtLocation(location);
437         Set JavaDoc<JavaClassAndMethod> targetMethodSet = Hierarchy.resolveMethodCallTargets(invokeInstruction, typeFrame, cpg);
438         if (DEBUG_NULLARG) {
439             System.out.println("Possibly called methods: " + targetMethodSet);
440         }
441         
442         // See if any call targets unconditionally dereference one of the null arguments
443
BitSet JavaDoc unconditionallyDereferencedNullArgSet = new BitSet JavaDoc();
444         List JavaDoc<JavaClassAndMethod> dangerousCallTargetList = new LinkedList JavaDoc<JavaClassAndMethod>();
445         List JavaDoc<JavaClassAndMethod> veryDangerousCallTargetList = new LinkedList JavaDoc<JavaClassAndMethod>();
446         for (JavaClassAndMethod targetMethod : targetMethodSet) {
447             if (DEBUG_NULLARG) {
448                 System.out.println("For target method " + targetMethod);
449             }
450             
451             ParameterNullnessProperty property = unconditionalDerefParamDatabase.getProperty(targetMethod.toXMethod());
452             if (property == null)
453                 continue;
454             if (DEBUG_NULLARG) {
455                 System.out.println("\tUnconditionally dereferenced params: " + property);
456             }
457             
458             BitSet JavaDoc targetUnconditionallyDereferencedNullArgSet =
459                 property.getViolatedParamSet(nullArgSet);
460             
461             if (targetUnconditionallyDereferencedNullArgSet.isEmpty())
462                 continue;
463             
464             dangerousCallTargetList.add(targetMethod);
465             
466             unconditionallyDereferencedNullArgSet.or(targetUnconditionallyDereferencedNullArgSet);
467             
468             if (!property.getViolatedParamSet(definitelyNullArgSet).isEmpty())
469                 veryDangerousCallTargetList.add(targetMethod);
470         }
471         
472         if (dangerousCallTargetList.isEmpty())
473             return;
474         
475         WarningPropertySet propertySet = new WarningPropertySet();
476
477         // See if there are any safe targets
478
Set JavaDoc<JavaClassAndMethod> safeCallTargetSet = new HashSet JavaDoc<JavaClassAndMethod>();
479         safeCallTargetSet.addAll(targetMethodSet);
480         safeCallTargetSet.removeAll(dangerousCallTargetList);
481         if (safeCallTargetSet.isEmpty()) {
482             propertySet.addProperty(NullArgumentWarningProperty.ALL_DANGEROUS_TARGETS);
483             if (dangerousCallTargetList.size() == 1) {
484                 propertySet.addProperty(NullArgumentWarningProperty.MONOMORPHIC_CALL_SITE);
485             }
486         }
487         
488         // Call to private method? In theory there should be only one possible target.
489
boolean privateCall =
490                safeCallTargetSet.isEmpty()
491             && dangerousCallTargetList.size() == 1
492             && dangerousCallTargetList.get(0).getMethod().isPrivate();
493         
494         MethodGen methodGen = classContext.getMethodGen(method);
495         String JavaDoc sourceFile = classContext.getJavaClass().getSourceFileName();
496         
497         String JavaDoc bugType;
498         int priority;
499         if (privateCall
500                 || invokeInstruction.getOpcode() == Constants.INVOKESTATIC
501                 || invokeInstruction.getOpcode() == Constants.INVOKESPECIAL) {
502             bugType = "NP_NULL_PARAM_DEREF_NONVIRTUAL";
503             priority = HIGH_PRIORITY;
504         } else if (safeCallTargetSet.isEmpty()) {
505             bugType = "NP_NULL_PARAM_DEREF_ALL_TARGETS_DANGEROUS";
506             priority = NORMAL_PRIORITY;
507         } else {
508             bugType = "NP_NULL_PARAM_DEREF";
509             priority = LOW_PRIORITY;
510         }
511         
512         if (caught) priority++;
513         if (dangerousCallTargetList.size() > veryDangerousCallTargetList.size())
514             priority++;
515         else
516             propertySet.addProperty(NullArgumentWarningProperty.ACTUAL_PARAMETER_GUARANTEED_NULL);
517         
518         BugInstance warning = new BugInstance(bugType, priority)
519                 .addClassAndMethod(methodGen, sourceFile)
520                 .addMethod(XFactory.createXMethod(invokeInstruction, cpg)).describe("METHOD_CALLED")
521                 .addSourceLine(classContext, methodGen, sourceFile, location.getHandle())
522                 ;
523         
524         // Check which params might be null
525
addParamAnnotations(definitelyNullArgSet, unconditionallyDereferencedNullArgSet, propertySet, warning);
526
527         // Add annotations for dangerous method call targets
528
for (JavaClassAndMethod dangerousCallTarget : veryDangerousCallTargetList) {
529             warning.addMethod(dangerousCallTarget).describe("METHOD_DANGEROUS_TARGET_ACTUAL_GUARANTEED_NULL");
530         }
531         dangerousCallTargetList.removeAll(veryDangerousCallTargetList);
532 // Add annotations for dangerous method call targets
533
for (JavaClassAndMethod dangerousCallTarget : dangerousCallTargetList) {
534             warning.addMethod(dangerousCallTarget).describe("METHOD_DANGEROUS_TARGET");
535         }
536
537         // Add safe method call targets.
538
// This is useful to see which other call targets the analysis
539
// considered.
540
for (JavaClassAndMethod safeMethod : safeCallTargetSet) {
541             warning.addMethod(safeMethod).describe("METHOD_SAFE_TARGET");
542         }
543         
544         decorateWarning(location, propertySet, warning);
545         bugReporter.reportBug(warning);
546     }
547
548     private void decorateWarning(Location location, WarningPropertySet propertySet, BugInstance warning) {
549         if (FindBugsAnalysisFeatures.isRelaxedMode()) {
550             WarningPropertyUtil.addPropertiesForLocation(propertySet, classContext, method, location);
551         }
552         propertySet.decorateBugInstance(warning);
553     }
554
555     private void addParamAnnotations(
556             BitSet JavaDoc definitelyNullArgSet,
557             BitSet JavaDoc violatedParamSet,
558             WarningPropertySet propertySet,
559             BugInstance warning) {
560         for (int i = 0; i < 32; ++i) {
561             if (violatedParamSet.get(i)) {
562                 boolean definitelyNull = definitelyNullArgSet.get(i);
563                 
564                 if (definitelyNull) {
565                     propertySet.addProperty(NullArgumentWarningProperty.ARG_DEFINITELY_NULL);
566                 }
567
568                 // Note: we report params as being indexed starting from 1, not 0
569
warning.addInt(i + 1).describe(
570                         definitelyNull ? "INT_NULL_ARG" : "INT_MAYBE_NULL_ARG");
571             }
572         }
573     }
574
575     /**
576      * We have a method invocation in which a possibly or definitely null
577      * parameter is passed. Check it against the library of nonnull annotations.
578      *
579      * @param location
580      * @param cpg
581      * @param typeDataflow
582      * @param invokeInstruction
583      * @param nullArgSet
584      * @param definitelyNullArgSet
585      * @throws ClassNotFoundException
586      */

587     private void checkNonNullParam(
588             Location location,
589             ConstantPoolGen cpg,
590             TypeDataflow typeDataflow,
591             InvokeInstruction invokeInstruction,
592             BitSet JavaDoc nullArgSet,
593             BitSet JavaDoc definitelyNullArgSet) throws ClassNotFoundException JavaDoc {
594         
595         boolean caught = inCatchNullBlock(location);
596         if (caught && skipIfInsideCatchNull()) return;
597         
598         XMethod m = XFactory.createXMethod(invokeInstruction, cpg);
599
600         NullnessAnnotationDatabase db
601         = AnalysisContext.currentAnalysisContext().getNullnessAnnotationDatabase();
602         for(int i=nullArgSet.nextSetBit(0); i>=0; i=nullArgSet.nextSetBit(i+1)) {
603             int paramNum = 0;
604             
605           String JavaDoc signature = invokeInstruction.getSignature(cpg);
606           Type[] args = Type.getArgumentTypes(signature);
607           int words =0;
608           while (words < i)
609               words += args[paramNum++].getSize();
610     
611             if (db.parameterMustBeNonNull(m, paramNum)) {
612                 boolean definitelyNull = definitelyNullArgSet.get(i);
613                 if (DEBUG_NULLARG) {
614                 System.out.println("QQQ2: " + i + " -- " + paramNum + " is null");
615                 System.out.println("QQQ nullArgSet: " + nullArgSet);
616                 System.out.println("QQQ dnullArgSet: " + definitelyNullArgSet);
617                 }
618                 
619                 MethodGen methodGen = classContext.getMethodGen(method);
620                 String JavaDoc sourceFile = classContext.getJavaClass().getSourceFileName();
621                 
622                 int priority = definitelyNull ? HIGH_PRIORITY : NORMAL_PRIORITY;
623                 if (caught) priority++;
624                 BugInstance warning = new BugInstance("NP_NONNULL_PARAM_VIOLATION",
625                         priority)
626                         .addClassAndMethod(methodGen, sourceFile)
627                         .addMethod(m).describe("METHOD_CALLED")
628                         .addInt(i).describe("INT_NONNULL_PARAM")
629                         .addSourceLine(classContext, methodGen, sourceFile, location.getHandle());
630                 
631                 bugReporter.reportBug(warning);
632             }
633         }
634         
635     }
636
637     public void report() {
638     }
639
640     public boolean skipIfInsideCatchNull() {
641         return classContext.getJavaClass().getClassName().indexOf("Test") >= 0
642           || method.getName().indexOf("test") >= 0 || method.getName().indexOf("Test") >= 0;
643     }
644     public void foundNullDeref(ClassContext classContext, Location location, ValueNumber valueNumber, IsNullValue refValue, ValueNumberFrame vnaFrame) {
645         WarningPropertySet propertySet = new WarningPropertySet();
646         if (valueNumber.hasFlag(ValueNumber.CONSTANT_CLASS_OBJECT)) return;
647         
648         boolean onExceptionPath = refValue.isException();
649         if (onExceptionPath) {
650             propertySet.addProperty(GeneralWarningProperty.ON_EXCEPTION_PATH);
651         }
652         int pc = location.getHandle().getPosition();
653         BugAnnotation variable = findAnnotationFromValueNumber(method, location, valueNumber, vnaFrame);
654         
655         boolean duplicated = false;
656         try {
657             CFG cfg = classContext.getCFG(method);
658             duplicated = cfg.getLocationsContainingInstructionWithOffset(pc).size() > 1;
659         } catch (CFGBuilderException e) {
660         }
661
662         boolean caught = inCatchNullBlock(location);
663         if (caught && skipIfInsideCatchNull()) return;
664
665         if (!duplicated && refValue.isDefinitelyNull()) {
666             String JavaDoc type = onExceptionPath ? "NP_ALWAYS_NULL_EXCEPTION" : "NP_ALWAYS_NULL";
667             int priority = onExceptionPath ? NORMAL_PRIORITY : HIGH_PRIORITY;
668             if (caught) priority++;
669             reportNullDeref(propertySet, classContext, method, location, type, priority, variable);
670         } else if (refValue.isNullOnSomePath() || duplicated && refValue.isDefinitelyNull()) {
671             String JavaDoc type = "NP_NULL_ON_SOME_PATH";
672             int priority = NORMAL_PRIORITY;
673             if (caught) priority++;
674             if (onExceptionPath) type = "NP_NULL_ON_SOME_PATH_EXCEPTION";
675             else if (refValue.isReturnValue())
676                 type = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE";
677             else if (refValue.isParamValue()) {
678                 if (method.getName().equals("equals")
679                         && method.getSignature().equals("(Ljava/lang/Object;)Z")) {
680                     if (caught) return;
681                     type = "NP_EQUALS_SHOULD_HANDLE_NULL_ARGUMENT";
682                     
683                 } else
684                 type = "NP_ARGUMENT_MIGHT_BE_NULL";
685             }
686             
687             if (DEBUG) System.out.println("Reporting null on some path: value=" + refValue);
688             reportNullDeref(propertySet, classContext, method, location, type, priority, variable);
689         }
690     }
691
692     /**
693      * @param method TODO
694      * @param location
695      * @param valueNumber
696      * @param vnaFrame
697      * @return
698      */

699      public static BugAnnotation findAnnotationFromValueNumber(Method method, Location location, ValueNumber valueNumber, ValueNumberFrame vnaFrame) {
700          LocalVariableAnnotation ann = findLocalAnnotationFromValueNumber(method, location, valueNumber, vnaFrame);
701          if (ann != null && ann.isSignificant()) return ann;
702          FieldAnnotation field = findFieldAnnotationFromValueNumber(method, location, valueNumber, vnaFrame);
703          if (field != null) return field;
704          return ann;
705      }
706      
707      public static FieldAnnotation findFieldAnnotationFromValueNumber(Method method, Location location, ValueNumber valueNumber, ValueNumberFrame vnaFrame) {
708          XField field = findXFieldFromValueNumber(method, location, valueNumber, vnaFrame);
709          if (field == null) return null;
710          return FieldAnnotation.fromXField(field);
711      }
712          
713      public static XField findXFieldFromValueNumber(Method method, Location location, ValueNumber valueNumber, ValueNumberFrame vnaFrame) {
714          if (vnaFrame == null || vnaFrame.isBottom() || vnaFrame.isTop()) return null;
715
716          AvailableLoad load = vnaFrame.getLoad(valueNumber);
717          if (load != null) {
718              return load.getField();
719          }
720          return null;
721      }
722
723      public static LocalVariableAnnotation findLocalAnnotationFromValueNumber(Method method, Location location, ValueNumber valueNumber, ValueNumberFrame vnaFrame) {
724
725          if (vnaFrame == null || vnaFrame.isBottom() || vnaFrame.isTop()) return null;
726
727          LocalVariableAnnotation localAnnotation = null;
728          for(int i = 0; i < vnaFrame.getNumLocals(); i++) {
729              if (valueNumber.equals(vnaFrame.getValue(i))) {
730                  if (DEBUG) System.out.println("Found it in local " + i);
731                  InstructionHandle handle = location.getHandle();
732                  int position1 = handle.getPrev().getPosition();
733                  int position2 = handle.getPosition();
734                  localAnnotation = LocalVariableAnnotation.getLocalVariableAnnotation(method, i, position1, position2);
735                  if (localAnnotation != null) return localAnnotation;
736              }
737          }
738          return null;
739      }
740
741     private void reportNullDeref(
742             WarningPropertySet propertySet,
743             ClassContext classContext,
744             Method method,
745             Location location,
746             String JavaDoc type,
747             int priority, BugAnnotation variable) {
748         MethodGen methodGen = classContext.getMethodGen(method);
749         String JavaDoc sourceFile = classContext.getJavaClass().getSourceFileName();
750
751         BugInstance bugInstance = new BugInstance(this, type, priority)
752                 .addClassAndMethod(methodGen, sourceFile);
753         if (variable != null)
754             bugInstance.add(variable);
755         else bugInstance.add(new LocalVariableAnnotation("?",-1,-1));
756         bugInstance.addSourceLine(classContext, methodGen, sourceFile, location.getHandle());
757
758         if (FindBugsAnalysisFeatures.isRelaxedMode()) {
759             WarningPropertyUtil.addPropertiesForLocation(propertySet, classContext, method, location);
760             propertySet.decorateBugInstance(bugInstance);
761         }
762         
763         bugReporter.reportBug(bugInstance);
764     }
765
766     public static boolean isThrower(BasicBlock target) {
767         InstructionHandle ins = target.getFirstInstruction();
768         int maxCount = 7;
769         while (ins != null) {
770             if (maxCount-- <= 0) break;
771             Instruction i = ins.getInstruction();
772             if (i instanceof ATHROW) {
773                 return true;
774             }
775             if (i instanceof InstructionTargeter
776                     || i instanceof ReturnInstruction) return false;
777             ins = ins.getNext();
778         }
779         return false;
780     }
781     public void foundRedundantNullCheck(Location location, RedundantBranch redundantBranch) {
782         String JavaDoc sourceFile = classContext.getJavaClass().getSourceFileName();
783         MethodGen methodGen = classContext.getMethodGen(method);
784         
785         boolean isChecked = redundantBranch.firstValue.isChecked();
786         boolean wouldHaveBeenAKaboom = redundantBranch.firstValue.wouldHaveBeenAKaboom();
787         Location locationOfKaBoom = redundantBranch.firstValue.getLocationOfKaBoom();
788          
789         boolean createdDeadCode = false;
790         boolean infeasibleEdgeSimplyThrowsException = false;
791         Edge infeasibleEdge = redundantBranch.infeasibleEdge;
792         if (infeasibleEdge != null) {
793             if (DEBUG) System.out.println("Check if " + redundantBranch + " creates dead code");
794             BasicBlock target = infeasibleEdge.getTarget();
795
796             if (DEBUG) System.out.println("Target block is " + (target.isExceptionThrower() ? " exception thrower" : " not exception thrower"));
797             // If the block is empty, it probably doesn't matter that it was killed.
798
// FIXME: really, we should crawl the immediately reachable blocks
799
// starting at the target block to see if any of them are dead and nonempty.
800
boolean empty = !target.isExceptionThrower() &&
801                 (target.isEmpty() || isGoto(target.getFirstInstruction().getInstruction()));
802             if (!empty) {
803                 try {
804                 if (classContext.getCFG(method).getNumIncomingEdges(target) > 1) {
805                     if (DEBUG) System.out.println("Target of infeasible edge has multiple incoming edges");
806                     empty = true;
807                 }}
808                 catch (CFGBuilderException e) {
809                     assert true; // ignore it
810
}
811             }
812             if (DEBUG) System.out.println("Target block is " + (empty ? "empty" : "not empty"));
813
814             if (!empty) {
815                 if (isThrower(target)) infeasibleEdgeSimplyThrowsException = true;
816         
817             }
818             if (!empty && !previouslyDeadBlocks.get(target.getId())) {
819                 if (DEBUG) System.out.println("target was alive previously");
820                 // Block was not dead before the null pointer analysis.
821
// See if it is dead now by inspecting the null value frame.
822
// If it's TOP, then the block became dead.
823
IsNullValueFrame invFrame = invDataflow.getStartFact(target);
824                 createdDeadCode = invFrame.isTop();
825                 if (DEBUG) System.out.println("target is now " + (createdDeadCode ? "dead" : "alive"));
826
827             }
828         }
829         
830         int priority;
831         boolean valueIsNull = true;
832         String JavaDoc warning;
833         if (redundantBranch.secondValue == null) {
834             if (redundantBranch.firstValue.isDefinitelyNull() ) {
835                 warning = "RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE";
836                 priority = NORMAL_PRIORITY;
837             }
838             else {
839                 warning = "RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE";
840                 valueIsNull = false;
841                 priority = isChecked ? NORMAL_PRIORITY : LOW_PRIORITY;
842             }
843
844         } else {
845             boolean bothNull = redundantBranch.firstValue.isDefinitelyNull() && redundantBranch.secondValue.isDefinitelyNull();
846             if (redundantBranch.secondValue.isChecked()) isChecked = true;
847             if (redundantBranch.secondValue.wouldHaveBeenAKaboom()) {
848                 wouldHaveBeenAKaboom = true;
849                 locationOfKaBoom = redundantBranch.secondValue.getLocationOfKaBoom();
850             }
851             if (bothNull) {
852                 warning = "RCN_REDUNDANT_COMPARISON_TWO_NULL_VALUES";
853                 priority = NORMAL_PRIORITY;
854             }
855             else {
856                 warning = "RCN_REDUNDANT_COMPARISON_OF_NULL_AND_NONNULL_VALUE";
857                 priority = isChecked ? NORMAL_PRIORITY : LOW_PRIORITY;
858             }
859
860         }
861         
862         if (wouldHaveBeenAKaboom) {
863             priority = HIGH_PRIORITY;
864             warning = "RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE";
865             if (locationOfKaBoom == null) throw new NullPointerException JavaDoc("location of KaBoom is null");
866         }
867         
868         if (DEBUG) System.out.println(createdDeadCode + " " + infeasibleEdgeSimplyThrowsException + " " + valueIsNull + " " + priority);
869         if (createdDeadCode && !infeasibleEdgeSimplyThrowsException) {
870             priority += 0;
871         } else if (createdDeadCode && infeasibleEdgeSimplyThrowsException) {
872             // throw clause
873
if (valueIsNull)
874                 priority += 0;
875             else
876                 priority += 1;
877         } else {
878             // didn't create any dead code
879
priority += 1;
880         }
881
882
883         if (DEBUG) {
884             System.out.println("RCN" + priority + " "
885                     + redundantBranch.firstValue + " =? "
886                     + redundantBranch.secondValue
887                     + " : " + warning
888             );
889
890             if (isChecked) System.out.println("isChecked");
891             if (wouldHaveBeenAKaboom) System.out.println("wouldHaveBeenAKaboom");
892             if (createdDeadCode) System.out.println("createdDeadCode");
893         }
894         
895
896         BugAnnotation variableAnnotation = null;
897         try {
898 // Get the value number
899
ValueNumberFrame vnaFrame = classContext.getValueNumberDataflow(method).getFactAtLocation(location);
900             if (vnaFrame.isValid()) {
901         Instruction ins = location.getHandle().getInstruction();
902
903         ValueNumber valueNumber = vnaFrame.getInstance(ins, classContext.getConstantPoolGen());
904         if (valueNumber.hasFlag(ValueNumber.CONSTANT_CLASS_OBJECT)) return;
905         variableAnnotation = findAnnotationFromValueNumber(method, location, valueNumber, vnaFrame);
906
907             }
908         } catch (DataflowAnalysisException e) {
909             // ignore
910
} catch (CFGBuilderException e) {
911             // ignore
912
}
913     
914         BugInstance bugInstance =
915             new BugInstance(this, warning, priority)
916                 .addClassAndMethod(methodGen, sourceFile);
917         if (variableAnnotation != null) bugInstance.add(variableAnnotation);
918         else bugInstance.add(new LocalVariableAnnotation("?", -1, -1));
919         if (wouldHaveBeenAKaboom)
920             bugInstance.addSourceLine(classContext, methodGen, sourceFile, locationOfKaBoom.getHandle());
921         bugInstance.addSourceLine(classContext, methodGen, sourceFile, location.getHandle()).describe("SOURCE_REDUNDANT_NULL_CHECK");
922         
923         if (FindBugsAnalysisFeatures.isRelaxedMode()) {
924             WarningPropertySet propertySet = new WarningPropertySet();
925             WarningPropertyUtil.addPropertiesForLocation(propertySet, classContext, method, location);
926             if (isChecked)
927                 propertySet.addProperty(NullDerefProperty.CHECKED_VALUE);
928             if (wouldHaveBeenAKaboom)
929                 propertySet.addProperty(NullDerefProperty.WOULD_HAVE_BEEN_A_KABOOM);
930             if (createdDeadCode)
931                 propertySet.addProperty(NullDerefProperty.CREATED_DEAD_CODE);
932             
933             propertySet.decorateBugInstance(bugInstance);
934             
935             priority = propertySet.computePriority(NORMAL_PRIORITY);
936             bugInstance.setPriority(priority);
937         }
938
939         bugReporter.reportBug(bugInstance);
940     }
941
942     /**
943      * Determine whether or not given instruction is a goto.
944      *
945      * @param instruction the instruction
946      * @return true if the instruction is a goto, false otherwise
947      */

948     private boolean isGoto(Instruction instruction) {
949         return instruction.getOpcode() == Constants.GOTO
950             || instruction.getOpcode() == Constants.GOTO_W;
951     }
952
953     /* (non-Javadoc)
954      * @see edu.umd.cs.findbugs.ba.npe.NullDerefAndRedundantComparisonCollector#foundGuaranteedNullDeref(java.util.Set, java.util.Set, edu.umd.cs.findbugs.ba.vna.ValueNumber, boolean)
955      */

956     public void foundGuaranteedNullDeref(
957             @NonNull Set JavaDoc<Location> assignedNullLocationSet,
958             @NonNull Set JavaDoc<Location> derefLocationSet,
959             SortedSet JavaDoc<Location> doomedLocations,
960             ValueNumberDataflow vna, ValueNumber refValue, boolean alwaysOnExceptionPath, boolean npeIfStatementCovered) {
961         if (refValue.hasFlag(ValueNumber.CONSTANT_CLASS_OBJECT)) return;
962         
963         if (DEBUG) {
964             System.out.println("Found guaranteed null deref in " + method.getName());
965             for(Location loc : doomedLocations)
966                 System.out.println("Doomed at " + loc);
967         }
968         
969         String JavaDoc bugType = alwaysOnExceptionPath
970             ? "NP_GUARANTEED_DEREF_ON_EXCEPTION_PATH"
971             : "NP_GUARANTEED_DEREF";
972         int priority = alwaysOnExceptionPath ? NORMAL_PRIORITY : HIGH_PRIORITY;
973         if (!npeIfStatementCovered) priority++;
974         
975         
976         // Add Locations in the set of locations at least one of which
977
// is guaranteed to be dereferenced
978

979         TreeSet JavaDoc<Location> sortedDerefLocationSet = new TreeSet JavaDoc<Location>(derefLocationSet);
980         SortedSet JavaDoc<Location> sourceLocations;
981         if (doomedLocations.isEmpty()) sourceLocations= new TreeSet JavaDoc<Location>(assignedNullLocationSet);
982         else sourceLocations = doomedLocations;
983         
984         if (doomedLocations.isEmpty() || sortedDerefLocationSet.isEmpty()) return;
985         boolean derefOutsideCatchBlock = false;
986         for (Location loc : sortedDerefLocationSet)
987             if (!inCatchNullBlock(loc)) {
988                 derefOutsideCatchBlock = true;
989                 break;
990             }
991     
992         if (!derefOutsideCatchBlock) {
993             if (skipIfInsideCatchNull()) return;
994             priority++;
995         }
996         BugAnnotation variableAnnotation = null;
997         try {
998             for (Location loc : sourceLocations) {
999                 variableAnnotation = findAnnotationFromValueNumber(method, loc, refValue, vna.getFactAtLocation(loc));
1000                if (variableAnnotation != null) break;
1001            }
1002            if (variableAnnotation == null) for (Location loc : sortedDerefLocationSet) {
1003                variableAnnotation = findAnnotationFromValueNumber(method, loc, refValue, vna.getFactAtLocation(loc));
1004                if (variableAnnotation != null) break;
1005            }
1006            
1007            
1008        } catch (DataflowAnalysisException e) {
1009        }
1010        if (variableAnnotation == null) variableAnnotation = new LocalVariableAnnotation("?",-1,-1);
1011// Create BugInstance
1012
BugInstance bugInstance = new BugInstance(this, bugType, priority)
1013            .addClassAndMethod(classContext.getJavaClass(), method);
1014        
1015        bugInstance.add(variableAnnotation);
1016        BitSet JavaDoc knownNull = new BitSet JavaDoc();
1017        
1018        SortedSet JavaDoc<SourceLineAnnotation> knownNullLocations = new TreeSet JavaDoc<SourceLineAnnotation>();
1019        for (Location loc : sourceLocations) {
1020            SourceLineAnnotation sourceLineAnnotation =
1021                SourceLineAnnotation.fromVisitedInstruction(classContext, classContext.getMethodGen(method),
1022                        classContext.getJavaClass().getSourceFileName(),
1023                        loc.getHandle());
1024            if (sourceLineAnnotation == null) continue;
1025            int startLine = sourceLineAnnotation.getStartLine();
1026            if (startLine == -1)
1027                knownNullLocations.add(sourceLineAnnotation);
1028            else if (!knownNull.get(startLine)) {
1029                knownNull.set(startLine);
1030                knownNullLocations.add(sourceLineAnnotation);
1031            }
1032        }
1033    
1034        for(SourceLineAnnotation sourceLineAnnotation : knownNullLocations)
1035            bugInstance.add(sourceLineAnnotation);
1036        
1037
1038        for (Location loc : sortedDerefLocationSet) {
1039            bugInstance.addSourceLine(classContext, method, loc).describe("SOURCE_LINE_DEREF");
1040        }
1041        
1042                // Report it
1043
bugReporter.reportBug(bugInstance);
1044    }
1045    boolean inCatchNullBlock(Location loc) {
1046        int pc = loc.getHandle().getPosition();
1047        int catchSize = Util.getSizeOfSurroundingTryBlock(classContext.getConstantPoolGen().getConstantPool(), method.getCode(), "java/lang/NullPointerException", pc);
1048        if ( catchSize < Integer.MAX_VALUE) return true;
1049        catchSize = Util.getSizeOfSurroundingTryBlock(classContext.getConstantPoolGen().getConstantPool(), method.getCode(), "java/lang/Exception", pc);
1050        if ( catchSize < 5) return true;
1051        catchSize = Util.getSizeOfSurroundingTryBlock(classContext.getConstantPoolGen().getConstantPool(), method.getCode(), "java/lang/RuntimeException", pc);
1052        if ( catchSize < 5) return true;
1053        catchSize = Util.getSizeOfSurroundingTryBlock(classContext.getConstantPoolGen().getConstantPool(), method.getCode(), "java/lang/Throwable", pc);
1054        if ( catchSize < 5) return true;
1055        return false;
1056        
1057    }
1058}
1059
1060// vim:ts=4
Popular Tags