001: /*
002: * FindBugs - Find Bugs in Java programs
003: * Copyright (C) 2006, 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;
021:
022: import java.util.Set;
023:
024: import org.apache.bcel.generic.ArrayType;
025: import org.apache.bcel.generic.ObjectType;
026: import org.apache.bcel.generic.ReferenceType;
027: import org.apache.bcel.generic.Type;
028:
029: import edu.umd.cs.findbugs.Priorities;
030: import edu.umd.cs.findbugs.annotations.NonNull;
031: import edu.umd.cs.findbugs.classfile.CheckedAnalysisException;
032: import edu.umd.cs.findbugs.classfile.ClassDescriptor;
033: import edu.umd.cs.findbugs.classfile.DescriptorFactory;
034: import edu.umd.cs.findbugs.classfile.Global;
035: import edu.umd.cs.findbugs.classfile.IAnalysisCache;
036:
037: public class IncompatibleTypes {
038: final int priority;
039:
040: final String msg;
041:
042: private IncompatibleTypes(String msg, int priority) {
043: this .msg = msg;
044: this .priority = priority;
045: }
046:
047: public int getPriority() {
048: return priority;
049: }
050:
051: public String getMsg() {
052: return msg;
053: }
054:
055: @Override
056: public String toString() {
057: return msg;
058: }
059:
060: public static final IncompatibleTypes SEEMS_OK = new IncompatibleTypes(
061: "Seems OK", Priorities.IGNORE_PRIORITY);
062:
063: public static final IncompatibleTypes ARRAY_AND_NON_ARRAY = new IncompatibleTypes(
064: "Array and non array", Priorities.HIGH_PRIORITY);
065:
066: public static final IncompatibleTypes ARRAY_AND_OBJECT = new IncompatibleTypes(
067: "Array and Object", Priorities.LOW_PRIORITY);
068:
069: public static final IncompatibleTypes INCOMPATIBLE_CLASSES = new IncompatibleTypes(
070: "Incompatible classes", Priorities.HIGH_PRIORITY);
071:
072: public static final IncompatibleTypes UNRELATED_CLASS_AND_INTERFACE = new IncompatibleTypes(
073: "Unrelated class and interface", Priorities.NORMAL_PRIORITY);
074: public static final IncompatibleTypes UNRELATED_FINAL_CLASS_AND_INTERFACE = new IncompatibleTypes(
075: "Unrelated final class and interface",
076: Priorities.HIGH_PRIORITY);
077:
078: public static final IncompatibleTypes UNRELATED_INTERFACES = new IncompatibleTypes(
079: "Unrelated interfaces", Priorities.NORMAL_PRIORITY);
080:
081: static public @NonNull
082: IncompatibleTypes getPriorityForAssumingCompatible(Type lhsType,
083: Type rhsType) {
084: return getPriorityForAssumingCompatible(lhsType, rhsType, false);
085: }
086:
087: static public @NonNull
088: IncompatibleTypes getPriorityForAssumingCompatible(Type lhsType,
089: Type rhsType, boolean pointerEquality) {
090: if (!(lhsType instanceof ReferenceType))
091: return SEEMS_OK;
092: if (!(rhsType instanceof ReferenceType))
093: return SEEMS_OK;
094:
095: while (lhsType instanceof ArrayType
096: && rhsType instanceof ArrayType) {
097: lhsType = ((ArrayType) lhsType).getElementType();
098: rhsType = ((ArrayType) rhsType).getElementType();
099: }
100:
101: if (lhsType instanceof ArrayType) {
102:
103: if (rhsType.equals(ObjectType.OBJECT))
104: return ARRAY_AND_OBJECT;
105: else
106: return ARRAY_AND_NON_ARRAY;
107: }
108: if (rhsType instanceof ArrayType) {
109: if (lhsType.equals(ObjectType.OBJECT))
110: return ARRAY_AND_OBJECT;
111: else
112: return ARRAY_AND_NON_ARRAY;
113: }
114: if (lhsType.equals(rhsType))
115: return SEEMS_OK;
116:
117: // For now, ignore the case where either reference is not
118: // of an object type. (It could be either an array or null.)
119: if (!(lhsType instanceof ObjectType)
120: || !(rhsType instanceof ObjectType))
121: return SEEMS_OK;
122:
123: return getPriorityForAssumingCompatible((ObjectType) lhsType,
124: (ObjectType) rhsType, pointerEquality);
125:
126: }
127:
128: static @NonNull
129: XMethod getInvokedMethod(XClass xClass, String name, String sig,
130: boolean isStatic) throws CheckedAnalysisException {
131: IAnalysisCache cache = Global.getAnalysisCache();
132: while (true) {
133: XMethod result = xClass.findMethod(name, sig, isStatic);
134: if (result != null)
135: return result;
136: if (isStatic)
137: throw new CheckedAnalysisException();
138: ClassDescriptor super classDescriptor = xClass
139: .getSuperclassDescriptor();
140: if (super classDescriptor == null)
141: throw new CheckedAnalysisException();
142: xClass = cache.getClassAnalysis(XClass.class,
143: super classDescriptor);
144: }
145:
146: }
147:
148: static public @NonNull
149: IncompatibleTypes getPriorityForAssumingCompatible(
150: ObjectType lhsType, ObjectType rhsType,
151: boolean pointerEquality) {
152: if (lhsType.equals(rhsType))
153: return SEEMS_OK;
154: try {
155: // See if the types are related by inheritance.
156: ClassDescriptor lhsDescriptor = DescriptorFactory
157: .createClassDescriptorFromDottedClassName(lhsType
158: .getClassName());
159: ClassDescriptor rhsDescriptor = DescriptorFactory
160: .createClassDescriptorFromDottedClassName(rhsType
161: .getClassName());
162:
163: IAnalysisCache cache = Global.getAnalysisCache();
164: XClass lhs = cache.getClassAnalysis(XClass.class,
165: lhsDescriptor);
166: XClass rhs = cache.getClassAnalysis(XClass.class,
167: rhsDescriptor);
168: if (!Hierarchy.isSubtype(lhsType, rhsType)
169: && !Hierarchy.isSubtype(rhsType, lhsType)) {
170: AnalysisContext analysisContext = AnalysisContext
171: .currentAnalysisContext();
172: // Look up the classes
173: XMethod lhsEquals = getInvokedMethod(lhs, "equals",
174: "(Ljava/lang/Object;)Z", false);
175: XMethod rhsEquals = getInvokedMethod(rhs, "equals",
176: "(Ljava/lang/Object;)Z", false);
177: String lhsClassName = lhsEquals.getClassName();
178: if (lhsEquals.equals(rhsEquals)) {
179: if (lhsClassName.equals("java.lang.Enum"))
180: return INCOMPATIBLE_CLASSES;
181: if (!pointerEquality
182: && !lhsClassName.equals("java.lang.Object"))
183: return SEEMS_OK;
184: }
185:
186: if (!lhs.isInterface() && !rhs.isInterface()) {
187: // Both are class types, and therefore there is no possible
188: // way
189: // the compared objects can have the same runtime type.
190: return INCOMPATIBLE_CLASSES;
191: } else {
192:
193: // Look up the common subtypes of the two types. If the
194: // intersection does not contain at least one instantiable
195: // class,
196: // then issue a warning of the appropriate type.
197: Set<ClassDescriptor> commonSubtypes = analysisContext
198: .getSubtypes2()
199: .getTransitiveCommonSubtypes(lhsDescriptor,
200: rhsDescriptor);
201:
202: if (!containsAtLeastOneInstantiableClass(commonSubtypes)) {
203: if (lhs.isInterface() && rhs.isInterface())
204: return UNRELATED_INTERFACES;
205: else if (lhs.isFinal() || rhs.isFinal())
206: return UNRELATED_FINAL_CLASS_AND_INTERFACE;
207: else
208: return UNRELATED_CLASS_AND_INTERFACE;
209: }
210:
211: }
212: }
213:
214: } catch (ClassNotFoundException e) {
215: AnalysisContext.reportMissingClass(e);
216: } catch (MissingClassException e) {
217: AnalysisContext.reportMissingClass(e
218: .getClassNotFoundException());
219: } catch (CheckedAnalysisException e) {
220: AnalysisContext.logError(
221: "Error checking for incompatible types", e);
222: }
223: return SEEMS_OK;
224: }
225:
226: private static boolean containsAtLeastOneInstantiableClass(
227: Set<ClassDescriptor> commonSubtypes)
228: throws CheckedAnalysisException {
229: IAnalysisCache cache = Global.getAnalysisCache();
230: for (ClassDescriptor classDescriptor : commonSubtypes) {
231:
232: XClass xclass = cache.getClassAnalysis(XClass.class,
233: classDescriptor);
234:
235: if (!xclass.isInterface() && !xclass.isAbstract())
236: return true;
237: }
238: return false;
239: }
240:
241: }
|