Class TypeSystem

java.lang.Object
net.sourceforge.pmd.lang.java.types.TypeSystem

public final class TypeSystem extends Object
Root context object for type analysis. Type systems own a global SymbolResolver, which creates and caches external symbols. Methods of this class promote symbols to types, and compose types together. TypeOps and TypeConversion have some more operations on types.

Some special types are presented as constant fields, eg OBJECT or NULL_TYPE. These are always comparable by reference. Note that the primitive wrapper types are not exposed as constants here, but can be accessed by using the box method on some primitive constant.

The lifetime of a type system is the analysis: it is shared by all compilation units. TODO this is hacked together by comparing the ClassLoader, but this should be in the language instance

Nodes have a reference to the type system they were created for: JavaNode.getTypeSystem().

  • Field Details

    • OBJECT

      public final JClassType OBJECT
      Top type of the reference type system. This is the type for the Object class. Note that even interfaces have this type as a supertype (JTypeMirror.getSuperTypeSet()).
    • NULL_TYPE

      public final JTypeMirror NULL_TYPE
      The bottom type of the reference type system. This is named the null type in the JLS and is not denotable in Java programs.

      This implementation uses this as the type of the 'null' literal.

      The null type has no symbol.

    • BOOLEAN

      public final JPrimitiveType BOOLEAN
      Primitive type boolean.
    • CHAR

      public final JPrimitiveType CHAR
      Primitive type char.
    • BYTE

      public final JPrimitiveType BYTE
      Primitive type byte.
    • SHORT

      public final JPrimitiveType SHORT
      Primitive type short.
    • INT

      public final JPrimitiveType INT
      Primitive type int.
    • LONG

      public final JPrimitiveType LONG
      Primitive type long.
    • FLOAT

      public final JPrimitiveType FLOAT
      Primitive type float.
    • DOUBLE

      public final JPrimitiveType DOUBLE
      Primitive type double.
    • allPrimitives

      public final Set<JPrimitiveType> allPrimitives
      The set of all primitive types. See getPrimitive(PrimitiveTypeKind).
    • NO_TYPE

      public final JTypeMirror NO_TYPE
      A constant to represent the normal absence of a type. The primitive void.class represents that type, and this is the return type of a void method.

      Note that the type of the class literal void.class is Class<java.lang.Void>, not NO_TYPE.

      java.lang.Void is represented by BOXED_VOID. Note that BOXED_VOID.unbox() != NO_TYPE, NO_TYPE.box() != BOXED_VOID.

      NO_TYPE.isPrimitive() returns false, even though void.class.isPrimitive() returns true.

    • UNKNOWN

      public final JTypeMirror UNKNOWN
      A constant to represent an unresolved type. This means, that resolution was attempted but failed and shouldn't be tried again. The symbol is a JClassSymbol.

      Note that TypeOps.isConvertible(JTypeMirror, JTypeMirror) considers this type a subtype of anything, even primitive types.

    • ERROR

      public final JTypeMirror ERROR
      A constant to represent a typing error. This would have been reported by a compiler, and is used to propagate errors.

      Note that TypeOps.isConvertible(JTypeMirror, JTypeMirror) considers this type a subtype of anything, even primitive types.

    • UNRESOLVED_METHOD

      public final JMethodSig UNRESOLVED_METHOD
      Sentinel value for an unresolved method. This type corresponds to a method declaration in the type UNKNOWN, returning UNKNOWN.
    • UNBOUNDED_WILD

      public final JWildcardType UNBOUNDED_WILD
      The unbounded wildcard, "?".
    • CLONEABLE

      public final JClassType CLONEABLE
      The interface Cloneable. This is included because it is a supertype of array types.
    • SERIALIZABLE

      public final JClassType SERIALIZABLE
      The interface Serializable. This is included because it is a supertype of array types.
    • BOXED_VOID

      public final JClassType BOXED_VOID
      This is the boxed type of Void.class, not to be confused with void.class, which in this framework is represented by NO_TYPE.

      Note that BOXED_VOID.unbox() != NO_TYPE, NO_TYPE.box() != BOXED_VOID.

  • Constructor Details

    • TypeSystem

      public TypeSystem(Function<TypeSystem,? extends SymbolResolver> symResolverMaker)
      Builds a new type system. Its public fields will be initialized with fresh types, unrelated to other types.
      Parameters:
      symResolverMaker - A function that creates a new symbol resolver, which will be owned by the new type system. Because of cyclic dependencies, the new type system is leaked before its initialization completes, so fields of the type system are unusable at that time. The resolver is used to create some shared types: OBJECT, CLONEABLE, SERIALIZABLE, BOXED_VOID.
  • Method Details

    • usingClassLoaderClasspath

      public static TypeSystem usingClassLoaderClasspath(ClassLoader bootstrapResourceLoader)
      Builds a new type system. Its public fields will be initialized with fresh types, unrelated to other types.
      Parameters:
      bootstrapResourceLoader - Classloader used to resolve class files to populate the fields of the new type system
    • usingClasspath

      public static TypeSystem usingClasspath(net.sourceforge.pmd.lang.java.symbols.internal.asm.Classpath bootstrapResourceLoader)
      Builds a new type system. Its public fields will be initialized with fresh types, unrelated to other types.
      Parameters:
      bootstrapResourceLoader - Classpath used to resolve class files to populate the fields of the new type system
    • bootstrapResolver

      public SymbolResolver bootstrapResolver()
      Returns the bootstrap symbol resolver. Concrete analysis passes may decorate this with different resolvers.
    • getClassSymbol

      public @Nullable JClassSymbol getClassSymbol(@Nullable Class<?> clazz)
      Returns the class symbol for the given reflected class. This asks the classloader of this type system. Returns null if the parameter is null, or the class is not available in the analysis classpath.
      Parameters:
      clazz - Class
    • getClassSymbol

      public @Nullable JClassSymbol getClassSymbol(String binaryName)
      Returns a symbol for the binary name. Returns null if the name is null or the symbol is not found on the classpath. The class must not be an array.
      Parameters:
      binaryName - Binary name
      Returns:
      A symbol, or null
      Throws:
      IllegalArgumentException - if the argument is not a binary name
    • getClassSymbolFromCanonicalName

      public @Nullable JClassSymbol getClassSymbolFromCanonicalName(String canonicalName)
      Returns a symbol for the canonical name. Returns null if the name is null or the symbol is not found on the classpath. The class must not be an array.

      Canonical names separate nested classes with . (periods) instead of $ (dollars) as the JVM does. Users usually use canonical names, but lookup is much more costly.

      Parameters:
      canonicalName - Canonical name
      Returns:
      A symbol, or null
      Throws:
      IllegalArgumentException - if the argument is not a binary name
    • getModuleSymbol

      public @Nullable JModuleSymbol getModuleSymbol(String moduleName)
      Since:
      7.5.0
    • typeOf

      public JTypeMirror typeOf(@Nullable JTypeDeclSymbol symbol, boolean isErased)
      Returns a type mirror for the given symbol. If the symbol declares type parameters, then the resulting type is raw (differs from the behaviour of declaration(JClassSymbol)), meaning all its supertypes are erased.

      If the symbol is a type parameter, returns a JTypeVar.

      If the symbol is a JClassSymbol, then:

      • If it represents a primitive type, the corresponding JPrimitiveType is returned (one of INT, CHAR, etc.).
      • If it represents an array type, a new JArrayType is returned. The component type will be built with a recursive call.
      • If it represents a class or interface type, a JClassType is returned.
        • If the parameter isErased is true, and if the symbol declares type parameters, then it will be a raw type. This means, which means all its generic supertypes are erased.
        • Otherwise, the generic supertypes are preserved. In particular, if the symbol declares type parameters itself, then it will be a generic type declaration.
        If the symbol is a non-static member of another class, then the given type's enclosing type is created, applying the above rules about erasure recursively. A type is either completely erased, or completely parameterized.
      Parameters:
      symbol - Symbol for the type declaration
      isErased - Whether the type should be consider erased, if it represents a class or interface type. This does not erase type variables, or array types for that matter.
      Returns:
      A type, or null if the symbol is null
    • rawType

      public JTypeMirror rawType(@Nullable JTypeDeclSymbol klass)
      Like typeOf(JTypeDeclSymbol, boolean), defaulting the erased parameter to true. If the symbol is not generic, the returned symbol is not actually raw.
      Parameters:
      klass - Symbol
      Returns:
      An erased class type
    • declaration

      public JTypeMirror declaration(@Nullable JClassSymbol klass)
      Like typeOf(JTypeDeclSymbol, boolean), defaulting the erased parameter to false. If the symbol is not generic, the returned symbol is not actually a generic type declaration.
      Parameters:
      klass - Symbol
      Returns:
      An erased class type
    • parameterise

      public @NonNull JTypeMirror parameterise(@NonNull JClassSymbol klass, @NonNull List<? extends JTypeMirror> typeArgs)
      Produce a parameterized type with the given symbol and type arguments. The type argument list must match the declared formal type parameters in length. Non-generic symbols are accepted by this method, provided the argument list is empty. If the symbol is unresolved, any type argument list is accepted.

      This method is equivalent to rawType(klass).withTypeArguments(typeArgs), but that code would require a cast.

      Parameters:
      klass - A symbol
      typeArgs - List of type arguments
      Throws:
      IllegalArgumentException - see JClassType.withTypeArguments(List)
    • arrayType

      public JTypeMirror arrayType(@NonNull JTypeMirror element, int numDimensions)
      Creates a new array type from an arbitrary element type.
      
       arrayType(T, 0)          = T
       arrayType(T, 1)          = T[]
       arrayType(T, 3)          = T[][][]
       arrayType(T[], 2)        = T[][][]
       
      Parameters:
      element - Element type
      numDimensions - Number of dimensions
      Returns:
      A new array type
      Throws:
      IllegalArgumentException - If numDimensions is negative
      IllegalArgumentException - If the element type is a JWildcardType, the null type, or void.
      NullPointerException - If the element type is null
    • arrayType

      public JArrayType arrayType(@NonNull JTypeMirror component)
      Like arrayType(JTypeMirror, int), with one dimension.
      Parameters:
      component - Component type
      Returns:
      An array type
      Throws:
      IllegalArgumentException - If the element type is a JWildcardType, the null type, or void.
      NullPointerException - If the element type is null
    • sigOf

      public JMethodSig sigOf(JExecutableSymbol methodSym)
    • sigOf

      public JMethodSig sigOf(JExecutableSymbol methodSym, Substitution subst)
    • sigOf

      public JVariableSig.FieldSig sigOf(JTypeMirror decl, JFieldSymbol fieldSym)
    • sigOf

      public JVariableSig sigOf(JClassType decl, JLocalVariableSymbol fieldSym)
    • sigOf

      public JVariableSig sigOf(JClassType decl, JFormalParamSymbol fieldSym)
    • wildcard

      public JWildcardType wildcard(boolean isUpperBound, @NonNull JTypeMirror bound)
      Builds a wildcard type with a single bound.
      
      
       wildcard(true, T)      = ? extends T
       wildcard(false, T)     = ? super T
       wildcard(true, OBJECT) = ?
      
       
      Parameters:
      isUpperBound - If true, this is an "extends" wildcard, otherwise a "super"
      bound - Bound of the wildcard
      Returns:
      A wildcard
      Throws:
      NullPointerException - If the bound is null
      IllegalArgumentException - If the bound is a primitive type, or a wildcard type
      IllegalArgumentException - If the bound is OBJECT and this is a lower-bounded wildcard (? super Object)
    • getPrimitive

      public @NonNull JPrimitiveType getPrimitive(@NonNull JPrimitiveType.PrimitiveTypeKind kind)
      Gets the primitive type identified by the given kind.
      Parameters:
      kind - Kind of primitive type
      Returns:
      A primitive type
      Throws:
      NullPointerException - if kind is null
    • lub

      public JTypeMirror lub(Collection<? extends JTypeMirror> types)
      The least upper bound, or "lub", of a set of reference types is a shared supertype that is more specific than any other shared supertype (that is, no other shared supertype is a subtype of the least upper bound).
      Throws:
      IllegalArgumentException - If types is empty
      NullPointerException - If types is null
    • glb

      public JTypeMirror glb(Collection<? extends JTypeMirror> types)
      Returns the greatest lower bound of the given set of types. This is defined in JLS§5.1.10 (Capture Conversion):
       glb(V1,...,Vm) = V1 & ... & Vm
       glb(V) = V
       

      This may alter the components, so that:

      • No intersection type is a component: ((A & B) & C) = (A & (B & C)) = (A & B & C)
      • No two types in the intersection are subtypes of one another (the intersection is minimal): A <: B => (A & B) = A, in particular, (A & A) = A
      • The intersection has a single component that is a class, array, or type variable. If all components are interfaces, then that component is OBJECT.
      • If several components are arrays, then their components are intersected: A[] & B[] = (A & B)[]

      If after these transformations, only a single component remains, then that is the returned type. Otherwise a JIntersectionType is created. Note that the intersection may be unsatisfiable (eg A[] & Runnable), but we don't attempt to minimize this to NULL_TYPE. Similarly, we do not attempt to minimize valid intersections. For instance List<?> & Collection<Number> can technically be minimized to List<Number>, but doing this requires inference of a fitting parameterization in general, which is complex, and not necessary in the internal tasks where intersection types are useful. In fact intersection types are precisely useful because they are simple to build.

      See also JLS§4.9 (Intersection types).

      Throws:
      IllegalArgumentException - If some component is not a class, interface, array, or type variable
      IllegalArgumentException - If there is more than one minimal class or array type
      IllegalArgumentException - If types is empty
      NullPointerException - If types is null
    • newTypeVar

      public JTypeVar newTypeVar(JTypeParameterSymbol symbol)
      Returns a new type variable for the given symbol. This is only intended to be used by the implementor of JTypeParameterSymbol.
    • logStats

      public void logStats()
      Called at the end of the analysis to log statistics about the loaded types.