// old version of compare types private boolean compareTypesOld(Type parmType, Type argType) { // XXX equality not implemented for GenericObjectType // if (parmType.equals(argType)) return true; // Compare type signatures instead if (GenericUtilities.getString(parmType).equals(GenericUtilities.getString(argType))) return true; if (parmType instanceof GenericObjectType) { GenericObjectType o = (GenericObjectType) parmType; if (o.getTypeCategory() == GenericUtilities.TypeCategory.WILDCARD_EXTENDS) { return compareTypesOld(o.getExtension(), argType); } } // ignore type variables for now if (parmType instanceof GenericObjectType && !((GenericObjectType) parmType).hasParameters()) return true; if (argType instanceof GenericObjectType && !((GenericObjectType) argType).hasParameters()) return true; // Case: Both are generic containers if (parmType instanceof GenericObjectType && argType instanceof GenericObjectType) { return true; } else { // Don't consider non reference types (should not be possible) if (!(parmType instanceof ReferenceType && argType instanceof ReferenceType)) return true; // Don't consider non object types (for now) if (!(parmType instanceof ObjectType && argType instanceof ObjectType)) return true; // Otherwise, compare base types ignoring generic information try { return Repository.instanceOf( ((ObjectType) argType).getClassName(), ((ObjectType) parmType).getClassName()); } catch (ClassNotFoundException e) { } } return true; }
private boolean isGenericCollection(ClassDescriptor operandClass) { String dottedClassName = operandClass.getDottedClassName(); if (baseGenericTypes.contains(dottedClassName)) return true; String found = null; for (String c : baseGenericTypes) { if (Subtypes2.instanceOf(operandClass, c)) { found = c; break; } } if (found == null) return false; if (dottedClassName.startsWith("java.util.") || dottedClassName.startsWith("com.google.common.collect.")) return true; try { XClass xclass = Global.getAnalysisCache().getClassAnalysis(XClass.class, operandClass); String sig = xclass.getSourceSignature(); if (sig == null) return false; String typeParameter = null; List<String> split = GenericUtilities.split(sig, true); if (sig.charAt(0) == '<') { int end = sig.indexOf(':'); if (end > 0) typeParameter = sig.substring(1, end); } if (DEBUG) System.out.println(dottedClassName + " " + typeParameter + " " + split); for (String s : split) { int i = s.indexOf('<'); if (i < 0) continue; if (s.charAt(0) != 'L') throw new IllegalStateException("unexpected non signature: " + s); ClassDescriptor c = DescriptorFactory.createClassDescriptor(s.substring(1, i)); String superTypeParameter = s.substring(i + 1); if (isGenericCollection(c) && (typeParameter == null || superTypeParameter.startsWith("T" + typeParameter))) { if (DEBUG) System.out.println(operandClass + " is a subtype of " + s); return true; } } if (DEBUG) System.out.println("Not a subtype"); } catch (CheckedAnalysisException e1) { AnalysisContext.logError( "Error checking for weird generic parameterization of " + operandClass, e1); } return false; }
/** * Compare to see if the argument <code>argType</code> passed to the method matches the type of * the corresponding parameter. The simplest case is when both are equal. * * <p>This is a conservative comparison - returns true if it cannot decide. If the parameter type * is a type variable (e.g. <code>T</code>) then we don't know enough (yet) to decide if they do * not match so return true. * * @param ignoreBaseType TODO */ private IncompatibleTypes compareTypes( Type expectedType, Type actualType, boolean ignoreBaseType) { // XXX equality not implemented for GenericObjectType // if (parmType.equals(argType)) return true; if (expectedType == actualType) return IncompatibleTypes.SEEMS_OK; // Compare type signatures instead String expectedString = GenericUtilities.getString(expectedType); String actualString = GenericUtilities.getString(actualType); if (expectedString.equals(actualString)) return IncompatibleTypes.SEEMS_OK; if (expectedType.equals(Type.OBJECT)) return IncompatibleTypes.SEEMS_OK; // if either type is java.lang.Object, then automatically true! // again compare strings... String objString = GenericUtilities.getString(Type.OBJECT); if (expectedString.equals(objString)) { return IncompatibleTypes.SEEMS_OK; } // get a category for each type TypeCategory expectedCat = GenericUtilities.getTypeCategory(expectedType); TypeCategory argCat = GenericUtilities.getTypeCategory(actualType); if (actualString.equals(objString) && expectedCat == TypeCategory.TYPE_VARIABLE) { return IncompatibleTypes.SEEMS_OK; } if (ignoreBaseType) { if (expectedCat == TypeCategory.PARAMETERIZED && argCat == TypeCategory.PARAMETERIZED) { GenericObjectType parmGeneric = (GenericObjectType) expectedType; GenericObjectType argGeneric = (GenericObjectType) actualType; return compareTypeParameters(parmGeneric, argGeneric); } return IncompatibleTypes.SEEMS_OK; } if (actualType.equals(Type.OBJECT) && expectedCat == TypeCategory.ARRAY_TYPE) return IncompatibleTypes.ARRAY_AND_OBJECT; // -~- plain objects are easy if (expectedCat == TypeCategory.PLAIN_OBJECT_TYPE && argCat == TypeCategory.PLAIN_OBJECT_TYPE) return IncompatibleTypes.getPriorityForAssumingCompatible(expectedType, actualType, false); if (expectedCat == TypeCategory.PARAMETERIZED && argCat == TypeCategory.PLAIN_OBJECT_TYPE) return IncompatibleTypes.getPriorityForAssumingCompatible( (GenericObjectType) expectedType, actualType); if (expectedCat == TypeCategory.PLAIN_OBJECT_TYPE && argCat == TypeCategory.PARAMETERIZED) return IncompatibleTypes.getPriorityForAssumingCompatible( (GenericObjectType) actualType, expectedType); // -~- parmType is: "? extends Another Type" OR "? super Another Type" if (expectedCat == TypeCategory.WILDCARD_EXTENDS || expectedCat == TypeCategory.WILDCARD_SUPER) return compareTypes( ((GenericObjectType) expectedType).getExtension(), actualType, ignoreBaseType); // -~- Not handling type variables if (expectedCat == TypeCategory.TYPE_VARIABLE || argCat == TypeCategory.TYPE_VARIABLE) return IncompatibleTypes.SEEMS_OK; // -~- Array Types: compare dimensions, then base type if (expectedCat == TypeCategory.ARRAY_TYPE && argCat == TypeCategory.ARRAY_TYPE) { ArrayType parmArray = (ArrayType) expectedType; ArrayType argArray = (ArrayType) actualType; if (parmArray.getDimensions() != argArray.getDimensions()) return IncompatibleTypes.ARRAY_AND_NON_ARRAY; return compareTypes(parmArray.getBasicType(), argArray.getBasicType(), ignoreBaseType); } // If one is an Array Type and the other is not, then they // are incompatible. (We already know neither is java.lang.Object) if (expectedCat == TypeCategory.ARRAY_TYPE ^ argCat == TypeCategory.ARRAY_TYPE) { return IncompatibleTypes.ARRAY_AND_NON_ARRAY; } // -~- Parameter Types: compare base type then parameters if (expectedCat == TypeCategory.PARAMETERIZED && argCat == TypeCategory.PARAMETERIZED) { GenericObjectType parmGeneric = (GenericObjectType) expectedType; GenericObjectType argGeneric = (GenericObjectType) actualType; // base types should be related { IncompatibleTypes result = compareTypes(parmGeneric.getObjectType(), argGeneric.getObjectType(), ignoreBaseType); if (!result.equals(IncompatibleTypes.SEEMS_OK)) return result; } return compareTypeParameters(parmGeneric, argGeneric); // XXX More to come } // If one is a Parameter Type and the other is not, then they // are incompatible. (We already know neither is java.lang.Object) if (false) { // not true. Consider class Foo extends ArrayList<String> if (expectedCat == TypeCategory.PARAMETERIZED ^ argCat == TypeCategory.PARAMETERIZED) { return IncompatibleTypes.SEEMS_OK; // fix this when we know what // we are doing here } } // -~- Wildcard e.g. List<*>.contains(...) if (expectedCat == TypeCategory.WILDCARD) // No Way to know return IncompatibleTypes.SEEMS_OK; // -~- Non Reference types // if ( parmCat == TypeCategory.NON_REFERENCE_TYPE || // argCat == TypeCategory.NON_REFERENCE_TYPE ) if (expectedType instanceof BasicType || actualType instanceof BasicType) { // this should not be possible, compiler will complain (pre 1.5) // or autobox primitive types (1.5 +) throw new IllegalArgumentException( "checking for compatibility of " + expectedType + " with " + actualType); } return IncompatibleTypes.SEEMS_OK; }