001: /*
002: * FindBugs - Find Bugs in Java programs
003: * Copyright (C) 2003-2007 University of Maryland
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation; either
008: * version 2.1 of the License, or (at your option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public
016: * License along with this library; if not, write to the Free Software
017: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018: */
019:
020: package edu.umd.cs.findbugs.ba.npe;
021:
022: import javax.annotation.meta.When;
023:
024: import org.objectweb.asm.AnnotationVisitor;
025: import org.objectweb.asm.Type;
026:
027: import edu.umd.cs.findbugs.SystemProperties;
028: import edu.umd.cs.findbugs.annotations.CheckForNull;
029: import edu.umd.cs.findbugs.ba.AnalysisContext;
030: import edu.umd.cs.findbugs.ba.AnnotationDatabase;
031: import edu.umd.cs.findbugs.ba.DefaultNullnessAnnotations;
032: import edu.umd.cs.findbugs.ba.INullnessAnnotationDatabase;
033: import edu.umd.cs.findbugs.ba.NullnessAnnotation;
034: import edu.umd.cs.findbugs.ba.XClass;
035: import edu.umd.cs.findbugs.ba.XFactory;
036: import edu.umd.cs.findbugs.ba.XField;
037: import edu.umd.cs.findbugs.ba.XMethod;
038: import edu.umd.cs.findbugs.ba.XMethodParameter;
039: import edu.umd.cs.findbugs.ba.AnnotationDatabase.Target;
040: import edu.umd.cs.findbugs.ba.jsr305.FindBugsDefaultAnnotations;
041: import edu.umd.cs.findbugs.ba.jsr305.JSR305NullnessAnnotations;
042: import edu.umd.cs.findbugs.ba.jsr305.TypeQualifierAnnotation;
043: import edu.umd.cs.findbugs.ba.jsr305.TypeQualifierApplications;
044: import edu.umd.cs.findbugs.ba.jsr305.TypeQualifierValue;
045: import edu.umd.cs.findbugs.classfile.CheckedAnalysisException;
046: import edu.umd.cs.findbugs.classfile.ClassDescriptor;
047: import edu.umd.cs.findbugs.classfile.DescriptorFactory;
048: import edu.umd.cs.findbugs.classfile.Global;
049: import edu.umd.cs.findbugs.classfile.MissingClassException;
050: import edu.umd.cs.findbugs.classfile.analysis.AnnotatedObject;
051: import edu.umd.cs.findbugs.classfile.analysis.AnnotationValue;
052: import edu.umd.cs.findbugs.classfile.analysis.ClassInfo;
053: import edu.umd.cs.findbugs.classfile.analysis.FieldInfo;
054: import edu.umd.cs.findbugs.classfile.analysis.MethodInfo;
055: import edu.umd.cs.findbugs.internalAnnotations.DottedClassName;
056: import edu.umd.cs.findbugs.log.Profiler;
057:
058: /**
059: * Implementation of INullnessAnnotationDatabase that
060: * is based on JSR-305 type qualifiers.
061: *
062: * @author David Hovemeyer
063: */
064: public class TypeQualifierNullnessAnnotationDatabase implements
065: INullnessAnnotationDatabase {
066: private static final boolean DEBUG = SystemProperties
067: .getBoolean("findbugs.npe.tq.debug");
068:
069: private final TypeQualifierValue nonnullTypeQualifierValue;
070:
071: public TypeQualifierNullnessAnnotationDatabase() {
072: ClassDescriptor nonnullClassDesc = DescriptorFactory.instance()
073: .getClassDescriptor("javax/annotation/Nonnull");
074: this .nonnullTypeQualifierValue = TypeQualifierValue.getValue(
075: nonnullClassDesc, null);
076: }
077:
078: /* (non-Javadoc)
079: * @see edu.umd.cs.findbugs.ba.INullnessAnnotationDatabase#getResolvedAnnotation(java.lang.Object, boolean)
080: */
081: public NullnessAnnotation getResolvedAnnotation(Object o,
082: boolean getMinimal) {
083: Profiler profiler = Profiler.getInstance();
084: profiler.start(this .getClass());
085: try {
086:
087: if (DEBUG) {
088: System.out.println("getResolvedAnnotation: o=" + o
089: + "...");
090: }
091:
092: TypeQualifierAnnotation tqa = null;
093:
094: if (o instanceof XMethodParameter) {
095: XMethodParameter param = (XMethodParameter) o;
096:
097: tqa = TypeQualifierApplications
098: .getEffectiveTypeQualifierAnnotation(param
099: .getMethod(), param
100: .getParameterNumber(),
101: nonnullTypeQualifierValue);
102: } else if (o instanceof XMethod || o instanceof XField) {
103: tqa = TypeQualifierApplications
104: .getEffectiveTypeQualifierAnnotation(
105: (AnnotatedObject) o,
106: nonnullTypeQualifierValue);
107: }
108:
109: NullnessAnnotation result = toNullnessAnnotation(tqa);
110: if (DEBUG) {
111: System.out.println(" ==> "
112: + (result != null ? result.toString()
113: : "not found"));
114: }
115: return result;
116: } finally {
117: profiler.end(this .getClass());
118: }
119: }
120:
121: /* (non-Javadoc)
122: * @see edu.umd.cs.findbugs.ba.INullnessAnnotationDatabase#parameterMustBeNonNull(edu.umd.cs.findbugs.ba.XMethod, int)
123: */
124: public boolean parameterMustBeNonNull(XMethod m, int param) {
125: if (DEBUG) {
126: System.out.print("Checking " + m + " param " + param
127: + " for @Nonnull...");
128: }
129:
130: TypeQualifierAnnotation tqa = TypeQualifierApplications
131: .getEffectiveTypeQualifierAnnotation(m, param,
132: nonnullTypeQualifierValue);
133: boolean answer = (tqa != null) && tqa.when == When.ALWAYS;
134:
135: if (DEBUG) {
136: System.out.println(answer ? "yes" : "no");
137: }
138:
139: return answer;
140: }
141:
142: // NOTE:
143: // The way we handle adding default annotations is to actually add AnnotationValues
144: // to the corresponding XFoo objects, giving the illusion that the annotations
145: // were actually read from the underlying class files.
146:
147: /**
148: * Convert a NullnessAnnotation into the ClassDescriptor
149: * of the equivalent JSR-305 nullness type qualifier.
150: *
151: * @param n a NullnessAnnotation
152: * @return ClassDescriptor of the equivalent JSR-305 nullness type qualifier
153: */
154: private ClassDescriptor getNullnessAnnotationClassDescriptor(
155: NullnessAnnotation n) {
156: if (n == NullnessAnnotation.CHECK_FOR_NULL) {
157: return JSR305NullnessAnnotations.CHECK_FOR_NULL;
158: } else if (n == NullnessAnnotation.NONNULL) {
159: return JSR305NullnessAnnotations.NONNULL;
160: } else if (n == NullnessAnnotation.NULLABLE) {
161: return JSR305NullnessAnnotations.NULLABLE;
162: } else if (n == NullnessAnnotation.UNKNOWN_NULLNESS) {
163: return JSR305NullnessAnnotations.NULLABLE;
164: } else {
165: throw new IllegalArgumentException(
166: "Unknown NullnessAnnotation: " + n);
167: }
168: }
169:
170: private static final ClassDescriptor PARAMETERS_ARE_NONNULL_BY_DEFAULT = DescriptorFactory
171: .instance().getClassDescriptor(
172: "javax/annotation/ParametersAreNonnullByDefault");
173: private static final ClassDescriptor RETURN_VALUES_ARE_NONNULL_BY_DEFAULT = DescriptorFactory
174: .instance()
175: .getClassDescriptor(
176: "edu/umd/cs/findbugs/annotations/ReturnValuesAreNonnullByDefault");
177:
178: /* (non-Javadoc)
179: * @see edu.umd.cs.findbugs.ba.INullnessAnnotationDatabase#addDefaultAnnotation(java.lang.String, java.lang.String, edu.umd.cs.findbugs.ba.NullnessAnnotation)
180: */
181: public void addDefaultAnnotation(Target target, String c,
182: NullnessAnnotation n) {
183: if (DEBUG) {
184: System.out.println("addDefaultAnnotation: target=" + target
185: + ", c=" + c + ", n=" + n);
186: }
187:
188: ClassDescriptor classDesc = DescriptorFactory.instance()
189: .getClassDescriptorForDottedClassName(c);
190: ClassInfo xclass;
191:
192: // Get the XClass (really a ClassInfo object)
193: try {
194: xclass = (ClassInfo) Global.getAnalysisCache()
195: .getClassAnalysis(XClass.class, classDesc);
196: } catch (MissingClassException e) {
197: // AnalysisContext.currentAnalysisContext().getLookupFailureCallback().reportMissingClass(e.getClassDescriptor());
198: return;
199: } catch (CheckedAnalysisException e) {
200: // AnalysisContext.logError("Error adding built-in nullness annotation", e);
201: return;
202: }
203: if (n == NullnessAnnotation.NONNULL
204: && target == AnnotationDatabase.Target.PARAMETER) {
205: xclass.addAnnotation(new AnnotationValue(
206: PARAMETERS_ARE_NONNULL_BY_DEFAULT));
207: return;
208: } else if (n == NullnessAnnotation.NONNULL
209: && target == AnnotationDatabase.Target.METHOD) {
210: xclass.addAnnotation(new AnnotationValue(
211: RETURN_VALUES_ARE_NONNULL_BY_DEFAULT));
212: return;
213: }
214: // Get the default annotation type
215: ClassDescriptor defaultAnnotationType;
216: if (target == AnnotationDatabase.Target.ANY) {
217: defaultAnnotationType = FindBugsDefaultAnnotations.DEFAULT_ANNOTATION;
218: } else if (target == AnnotationDatabase.Target.FIELD) {
219: defaultAnnotationType = FindBugsDefaultAnnotations.DEFAULT_ANNOTATION_FOR_FIELDS;
220: } else if (target == AnnotationDatabase.Target.METHOD) {
221: defaultAnnotationType = FindBugsDefaultAnnotations.DEFAULT_ANNOTATION_FOR_METHODS;
222: } else if (target == AnnotationDatabase.Target.PARAMETER) {
223: defaultAnnotationType = FindBugsDefaultAnnotations.DEFAULT_ANNOTATION_FOR_PARAMETERS;
224: } else {
225: throw new IllegalArgumentException(
226: "Unknown target for default annotation: " + target);
227: }
228:
229: // Get the JSR-305 nullness annotation type
230: ClassDescriptor nullnessAnnotationType = getNullnessAnnotationClassDescriptor(n);
231:
232: // Construct an AnnotationValue containing the default annotation
233: AnnotationValue annotationValue = new AnnotationValue(
234: defaultAnnotationType);
235: AnnotationVisitor v = annotationValue.getAnnotationVisitor();
236: v.visit("value", Type.getObjectType(nullnessAnnotationType
237: .getClassName()));
238: v.visitEnd();
239:
240: if (DEBUG) {
241: System.out.println("Adding AnnotationValue "
242: + annotationValue + " to class " + xclass);
243: }
244:
245: // Destructively add the annotation to the ClassInfo object
246: xclass.addAnnotation(annotationValue);
247: }
248:
249: // /* (non-Javadoc)
250: // * @see edu.umd.cs.findbugs.ba.INullnessAnnotationDatabase#addDefaultMethodAnnotation(java.lang.String, edu.umd.cs.findbugs.ba.NullnessAnnotation)
251: // */
252: // public void addDefaultMethodAnnotation(String name, NullnessAnnotation annotation) {
253: // }
254:
255: /* (non-Javadoc)
256: * @see edu.umd.cs.findbugs.ba.INullnessAnnotationDatabase#addFieldAnnotation(java.lang.String, java.lang.String, java.lang.String, boolean, edu.umd.cs.findbugs.ba.NullnessAnnotation)
257: */
258: public void addFieldAnnotation(String cName, String mName,
259: String mSig, boolean isStatic, NullnessAnnotation annotation) {
260: if (DEBUG) {
261: System.out.println("addFieldAnnotation: annotate " + cName
262: + "." + mName + " with " + annotation);
263: }
264:
265: XField xfield = XFactory.createXField(cName, mName, mSig,
266: isStatic);
267: if (!(xfield instanceof FieldInfo)) {
268: if (DEBUG) {
269: System.out.println(" Field not found! " + cName + "."
270: + mName + ":" + mSig + " " + isStatic + " "
271: + annotation);
272: }
273: return;
274: }
275:
276: // Get JSR-305 nullness annotation type
277: ClassDescriptor nullnessAnnotationType = getNullnessAnnotationClassDescriptor(annotation);
278:
279: // Create an AnnotationValue
280: AnnotationValue annotationValue = new AnnotationValue(
281: nullnessAnnotationType);
282:
283: // Destructively add the annotation to the FieldInfo object
284: ((FieldInfo) xfield).addAnnotation(annotationValue);
285: }
286:
287: /* (non-Javadoc)
288: * @see edu.umd.cs.findbugs.ba.INullnessAnnotationDatabase#addMethodAnnotation(java.lang.String, java.lang.String, java.lang.String, boolean, edu.umd.cs.findbugs.ba.NullnessAnnotation)
289: */
290: public void addMethodAnnotation(String cName, String mName,
291: String sig, boolean isStatic, NullnessAnnotation annotation) {
292: if (DEBUG) {
293: System.out.println("addMethodAnnotation: annotate " + cName
294: + "." + mName + " with " + annotation);
295: }
296:
297: XMethod xmethod = XFactory.createXMethod(cName, mName, sig,
298: isStatic);
299: if (xmethod == null || !xmethod.isResolved()) {
300: if (DEBUG) {
301: System.out.println(" Method not found!");
302: }
303: return;
304: }
305:
306: // Get JSR-305 nullness annotation type
307: ClassDescriptor nullnessAnnotationType = getNullnessAnnotationClassDescriptor(annotation);
308:
309: // Create an AnnotationValue
310: AnnotationValue annotationValue = new AnnotationValue(
311: nullnessAnnotationType);
312:
313: // Destructively add the annotation to the MethodInfo object
314: ((MethodInfo) xmethod).addAnnotation(annotationValue);
315: }
316:
317: /* (non-Javadoc)
318: * @see edu.umd.cs.findbugs.ba.INullnessAnnotationDatabase#addMethodParameterAnnotation(java.lang.String, java.lang.String, java.lang.String, boolean, int, edu.umd.cs.findbugs.ba.NullnessAnnotation)
319: */
320: public void addMethodParameterAnnotation(@DottedClassName
321: String cName, String mName, String sig, boolean isStatic,
322: int param, NullnessAnnotation annotation) {
323: if (DEBUG) {
324: System.out
325: .println("addMethodParameterAnnotation: annotate "
326: + cName + "." + mName + " param " + param
327: + " with " + annotation);
328: }
329:
330: XMethod xmethod = XFactory.createXMethod(cName, mName, sig,
331: isStatic);
332: if (xmethod == null || !xmethod.isResolved()) {
333: if (DEBUG) {
334: System.out.println(" Method not found!");
335: }
336: return;
337: }
338: if (!(xmethod instanceof MethodInfo)) {
339: if (false)
340: AnalysisContext
341: .logError("Could not fully resolve method "
342: + cName + "." + mName + sig
343: + " to apply annotation " + annotation);
344: return;
345: }
346: if (!xmethod.getClassName().equals(cName)) {
347: if (false)
348: AnalysisContext
349: .logError("Could not fully resolve method "
350: + cName + "." + mName + sig
351: + " to apply annotation " + annotation);
352: return;
353: }
354:
355: // Get JSR-305 nullness annotation type
356: ClassDescriptor nullnessAnnotationType = getNullnessAnnotationClassDescriptor(annotation);
357:
358: // Create an AnnotationValue
359: AnnotationValue annotationValue = new AnnotationValue(
360: nullnessAnnotationType);
361:
362: // Destructively add the annotation to the MethodInfo object
363: ((MethodInfo) xmethod).addParameterAnnotation(param,
364: annotationValue);
365: }
366:
367: /* (non-Javadoc)
368: * @see edu.umd.cs.findbugs.ba.INullnessAnnotationDatabase#loadAuxiliaryAnnotations()
369: */
370: public void loadAuxiliaryAnnotations() {
371: DefaultNullnessAnnotations.addDefaultNullnessAnnotations(this );
372: }
373:
374: /**
375: * Convert a Nonnull-based TypeQualifierAnnotation
376: * into a NullnessAnnotation.
377: *
378: * @param tqa Nonnull-based TypeQualifierAnnotation
379: * @return corresponding NullnessAnnotation
380: */
381: private NullnessAnnotation toNullnessAnnotation(@CheckForNull
382: TypeQualifierAnnotation tqa) {
383: if (tqa == null) {
384: return null;
385: }
386:
387: switch (tqa.when) {
388: case ALWAYS:
389: return NullnessAnnotation.NONNULL;
390: case MAYBE:
391: return NullnessAnnotation.CHECK_FOR_NULL;
392: case NEVER:
393: return NullnessAnnotation.CHECK_FOR_NULL; // FIXME: is this right?
394: case UNKNOWN:
395: return NullnessAnnotation.UNKNOWN_NULLNESS;
396: }
397:
398: throw new IllegalStateException();
399: }
400: }
|