Java language support module for the PMD static code analyzer with AST processing, symbol resolution, type system, metrics, and 400+ built-in rules
—
PMD Java provides a sophisticated symbol resolution and type system that enables semantic analysis of Java code. This includes complete support for generics, wildcards, type inference, and modern Java features like sealed classes and records.
The symbol system provides resolution of names to their declarations with full support for scoping rules, overloading, and generic type parameters.
/**
* Represents a named program element that can be referred to by simple name
*/
public interface JElementSymbol {
/**
* Gets the name with which this declaration may be referred to
*/
String getSimpleName();
/**
* Returns true if the simple name of this symbol matches the given name
*/
default boolean nameEquals(@NonNull String name);
/**
* Returns the type system that created this symbol
*/
TypeSystem getTypeSystem();
/**
* Returns true if this symbol is a placeholder for an unresolved reference
*/
default boolean isUnresolved();
/**
* Returns the node that declares this symbol if in the current file
*/
default @Nullable JavaNode tryGetNode();
/**
* Dispatch to the appropriate visit method of the visitor
*/
<R, P> R acceptVisitor(SymbolVisitor<R, P> visitor, P param);
}/**
* Represents class/interface/enum/annotation/record symbols
*/
public interface JClassSymbol extends JTypeDeclSymbol, JTypeParameterOwnerSymbol,
BoundToNode<ASTTypeDeclaration> {
/**
* Returns the binary name of this type (JLS §13.1)
*/
@NonNull String getBinaryName();
/**
* Returns the canonical name of this class
*/
@Nullable String getCanonicalName();
/**
* Returns the method or constructor this symbol is declared in for local classes
*/
@Nullable JExecutableSymbol getEnclosingMethod();
/**
* Returns the member classes declared directly in this class
*/
List<JClassSymbol> getDeclaredClasses();
/**
* Returns a class with the given name defined in this class
*/
@Nullable JClassSymbol getDeclaredClass(String name);
/**
* Returns the methods declared directly in this class (excludes bridges/synthetic)
*/
List<JMethodSymbol> getDeclaredMethods();
/**
* Returns the constructors declared by this class (excludes synthetic)
*/
List<JConstructorSymbol> getConstructors();
/**
* Returns the fields declared directly in this class (excludes synthetic)
*/
List<JFieldSymbol> getDeclaredFields();
/**
* Returns a field with the given name defined in this class
*/
@Nullable JFieldSymbol getDeclaredField(String name);
/**
* Returns all enum constants (subset of getDeclaredFields())
*/
default @NonNull List<JFieldSymbol> getEnumConstants();
/**
* Returns all record components (for record types)
*/
default @NonNull List<JRecordComponentSymbol> getRecordComponents();
/**
* Returns the superclass symbol if it exists
*/
@Nullable JClassSymbol getSuperclass();
/**
* Returns the direct super-interfaces of this class or interface
*/
List<JClassSymbol> getSuperInterfaces();
/**
* Returns super interface types under the given substitution
*/
List<JClassType> getSuperInterfaceTypes(Substitution substitution);
/**
* Returns the superclass type under the given substitution
*/
@Nullable JClassType getSuperclassType(Substitution substitution);
/**
* Returns the component symbol for array types
*/
@Nullable JTypeDeclSymbol getArrayComponent();
// Type checking methods
boolean isArray();
boolean isPrimitive();
boolean isEnum();
boolean isRecord();
boolean isAnnotation();
boolean isLocalClass();
boolean isAnonymousClass();
boolean isAbstract();
boolean isFinal();
/**
* Returns the list of permitted subclasses for sealed types
*/
default List<JClassSymbol> getPermittedSubtypes();
/**
* Returns true if this type is sealed
*/
default boolean isSealed();
/**
* Returns simple names of all annotation attributes (for annotation types)
*/
default PSet<String> getAnnotationAttributeNames();
}/**
* Represents method symbols
*/
public interface JMethodSymbol extends JExecutableSymbol {
/**
* Returns the return type of this method
*/
JTypeMirror getReturnType();
/**
* Returns the return type with the given substitution applied
*/
JTypeMirror getReturnType(Substitution subst);
/**
* Returns true if this is an abstract method
*/
boolean isAbstract();
/**
* Returns true if this is a default method (interface default)
*/
boolean isDefault();
/**
* Returns true if this is a native method
*/
boolean isNative();
/**
* Returns true if this is a synchronized method
*/
boolean isSynchronized();
/**
* Returns the method signature including parameter types
*/
JMethodSig getSignature();
/**
* Returns the method signature with substitution applied
*/
JMethodSig getSignature(Substitution subst);
}/**
* Represents field symbols
*/
public interface JFieldSymbol extends JVariableSymbol {
/**
* Returns the type of this field
*/
JTypeMirror getTypeMirror();
/**
* Returns the type with the given substitution applied
*/
JTypeMirror getTypeMirror(Substitution subst);
/**
* Returns true if this is a static field
*/
boolean isStatic();
/**
* Returns true if this is a final field
*/
boolean isFinal();
/**
* Returns true if this is a volatile field
*/
boolean isVolatile();
/**
* Returns true if this is a transient field
*/
boolean isTransient();
/**
* Returns true if this field is an enum constant
*/
boolean isEnumConstant();
/**
* Returns the constant value if this is a compile-time constant
*/
@Nullable Object getConstantValue();
}/**
* Base interface for variables (local variables, parameters, fields)
*/
public interface JVariableSymbol extends JElementSymbol {
/**
* Returns the type of this variable
*/
JTypeMirror getTypeMirror();
/**
* Returns true if this variable is effectively final
*/
boolean isEffectivelyFinal();
/**
* Returns the modifiers of this variable
*/
int getModifiers();
}
/**
* Represents local variable symbols
*/
public interface JLocalVariableSymbol extends JVariableSymbol {
/**
* Returns true if this is a final local variable
*/
boolean isFinal();
}
/**
* Represents formal parameter symbols
*/
public interface JFormalParamSymbol extends JVariableSymbol {
/**
* Returns true if this is a varargs parameter
*/
boolean isVarargs();
/**
* Returns true if this is a final parameter
*/
boolean isFinal();
}/**
* Provides symbol resolution within scopes
*/
public interface JSymbolTable {
/**
* Resolve variables by simple name in the current scope
*/
List<JVariableSymbol> variables();
/**
* Resolve types by simple name in the current scope
*/
List<JClassSymbol> types();
/**
* Resolve methods by simple name in the current scope
*/
List<JMethodSymbol> methods();
}The type system provides complete representation of Java types including generics, wildcards, and type inference.
/**
* Type mirrors represent Java types
*/
public interface JTypeMirror extends JTypeVisitable {
/**
* Returns the type system that built this type
*/
TypeSystem getTypeSystem();
/**
* Return annotations on this type
*/
PSet<SymAnnot> getTypeAnnotations();
/**
* Returns true if this type is the same type or a subtype of the given type
*/
default boolean isSubtypeOf(@NonNull JTypeMirror other);
/**
* Tests this type's convertibility to the other type
*/
default Convertibility isConvertibleTo(@NonNull JTypeMirror other);
/**
* Returns the set of (nominal) supertypes of this type
*/
default Set<JTypeMirror> getSuperTypeSet();
/**
* Returns the erased version of this type (removes generics)
*/
JTypeMirror getErasure();
/**
* Returns the symbol that declared this type (class/interface/primitive)
*/
@Nullable JTypeDeclSymbol getSymbol();
/**
* Returns the boxed version of this type (primitive -> wrapper)
*/
default JTypeMirror box();
/**
* Returns the unboxed version of this type (wrapper -> primitive)
*/
default JTypeMirror unbox();
// Type checking methods
boolean isPrimitive();
boolean isArray();
boolean isClassOrInterface();
boolean isInterface();
boolean isClass();
boolean isGeneric();
boolean isParameterizedType();
boolean isRaw();
boolean isTypeVariable();
boolean isWildcard();
boolean isIntersectionType();
boolean isBottom();
boolean isTop();
}/**
* Represents primitive types (int, boolean, etc.)
*/
public interface JPrimitiveType extends JTypeMirror {
/**
* Returns the kind of primitive type
*/
PrimitiveTypeKind getKind();
/**
* Returns the wrapper class type for this primitive
*/
JClassType box();
/**
* Enumeration of primitive type kinds
*/
enum PrimitiveTypeKind {
BOOLEAN("boolean", boolean.class),
BYTE("byte", byte.class),
SHORT("short", short.class),
INT("int", int.class),
LONG("long", long.class),
CHAR("char", char.class),
FLOAT("float", float.class),
DOUBLE("double", double.class);
String getSimpleName();
Class<?> toReflect();
}
}/**
* Represents class and interface types (possibly generic)
*/
public interface JClassType extends JTypeMirror {
/**
* Returns the type arguments of this parameterized type
*/
List<JTypeMirror> getTypeArgs();
/**
* Returns the generic type declaration
*/
JGenericDeclaration getGenericTypeDeclaration();
/**
* Returns the raw type (without type arguments)
*/
JClassType getRawType();
/**
* Returns true if this is a raw type
*/
boolean isRaw();
/**
* Returns true if this is a parameterized type
*/
boolean isParameterized();
/**
* Returns the class symbol
*/
@Override
JClassSymbol getSymbol();
/**
* Returns the enclosing type for nested classes
*/
@Nullable JClassType getEnclosingType();
/**
* Returns a field with the given name accessible from this type
*/
@Nullable JFieldSymbol getDeclaredField(String fieldName);
/**
* Returns a nested class with the given name
*/
@Nullable JClassSymbol getDeclaredClass(String name);
/**
* Applies a substitution to this type
*/
JClassType subst(Substitution substitution);
/**
* Returns the superclass type
*/
@Nullable JClassType getSuperClass();
/**
* Returns the super interface types
*/
List<JClassType> getSuperInterfaces();
/**
* Returns all supertypes (direct and indirect)
*/
Stream<JClassType> streamSuperTypes();
}/**
* Represents array types
*/
public interface JArrayType extends JTypeMirror {
/**
* Returns the component type of this array
*/
JTypeMirror getComponentType();
/**
* Returns the element type (ultimate component for multi-dimensional arrays)
*/
JTypeMirror getElementType();
/**
* Returns the number of array dimensions
*/
int getNumDimensions();
/**
* Returns the array symbol
*/
@Override
JClassSymbol getSymbol();
/**
* Creates a new array type with additional dimensions
*/
JArrayType addDimensions(int n);
}/**
* Represents type variables (generic type parameters)
*/
public interface JTypeVar extends JTypeMirror {
/**
* Returns the name of this type variable
*/
String getName();
/**
* Returns the type parameter symbol
*/
JTypeParameterSymbol getSymbol();
/**
* Returns the upper bounds of this type variable
*/
List<JTypeMirror> getUpperBounds();
/**
* Returns the lower bounds of this type variable
*/
List<JTypeMirror> getLowerBounds();
}/**
* Represents wildcard types (? extends, ? super)
*/
public interface JWildcardType extends JTypeMirror {
/**
* Returns the upper bound (extends clause)
*/
@Nullable JTypeMirror getUpperBound();
/**
* Returns the lower bound (super clause)
*/
@Nullable JTypeMirror getLowerBound();
/**
* Returns true if this is an unbounded wildcard (?)
*/
boolean isUnbounded();
/**
* Returns true if this is an upper bounded wildcard (? extends T)
*/
boolean isUpperBounded();
/**
* Returns true if this is a lower bounded wildcard (? super T)
*/
boolean isLowerBounded();
}/**
* Factory and registry for all types
*/
public class TypeSystem {
// Special type constants
public static final JTypeMirror OBJECT = /* ... */;
public static final JTypeMirror NULL_TYPE = /* ... */;
public static final JTypeMirror ERROR = /* ... */;
public static final JTypeMirror UNKNOWN = /* ... */;
public static final JTypeMirror NO_TYPE = /* ... */;
// Primitive type constants
public static final JPrimitiveType BOOLEAN = /* ... */;
public static final JPrimitiveType BYTE = /* ... */;
public static final JPrimitiveType SHORT = /* ... */;
public static final JPrimitiveType INT = /* ... */;
public static final JPrimitiveType LONG = /* ... */;
public static final JPrimitiveType CHAR = /* ... */;
public static final JPrimitiveType FLOAT = /* ... */;
public static final JPrimitiveType DOUBLE = /* ... */;
/**
* Returns the type mirror for the given class
*/
public JTypeMirror typeOf(Class<?> clazz);
/**
* Returns the type mirror for the given class symbol
*/
public JClassType typeOf(JClassSymbol symbol);
/**
* Creates an array type with the given component type
*/
public JArrayType arrayOf(JTypeMirror componentType);
/**
* Creates a parameterized type
*/
public JClassType parameterize(JClassSymbol symbol, List<JTypeMirror> typeArgs);
/**
* Creates a wildcard type
*/
public JWildcardType wildcard(boolean isExtends, @Nullable JTypeMirror bound);
/**
* Returns the least upper bound of the given types
*/
public JTypeMirror lub(List<JTypeMirror> types);
/**
* Returns the greatest lower bound of the given types
*/
public JTypeMirror glb(List<JTypeMirror> types);
/**
* Performs type inference for method calls
*/
public Substitution inferMethodTypeArgs(JMethodSymbol method,
List<JTypeMirror> actualArgs);
}// Example: Find all method calls to a specific method
public class MethodCallAnalyzer extends JavaVisitorBase<Void, Void> {
private String targetMethod;
private JClassSymbol targetClass;
private List<ASTMethodCall> matchingCalls = new ArrayList<>();
@Override
public Void visit(ASTMethodCall node, Void data) {
// Get the method symbol
JMethodSymbol methodSymbol = node.getMethodType();
if (methodSymbol != null && !methodSymbol.isUnresolved()) {
// Check if this is the target method
if (methodSymbol.nameEquals(targetMethod) &&
methodSymbol.getEnclosingClass().equals(targetClass)) {
matchingCalls.add(node);
}
}
return super.visit(node, data);
}
}// Example: Analyze generic type usage
public class GenericTypeAnalyzer extends JavaVisitorBase<Void, Void> {
@Override
public Void visit(ASTVariableDeclaration node, Void data) {
JTypeMirror varType = node.getTypeMirror();
if (varType.isClassOrInterface()) {
JClassType classType = (JClassType) varType;
if (classType.isParameterized()) {
System.out.println("Found parameterized type: " + classType);
// Analyze type arguments
for (JTypeMirror typeArg : classType.getTypeArgs()) {
if (typeArg.isWildcard()) {
JWildcardType wildcard = (JWildcardType) typeArg;
System.out.println(" Wildcard: " + wildcard);
if (wildcard.isUpperBounded()) {
System.out.println(" Upper bound: " + wildcard.getUpperBound());
}
if (wildcard.isLowerBounded()) {
System.out.println(" Lower bound: " + wildcard.getLowerBound());
}
}
}
} else if (classType.isRaw()) {
System.out.println("Found raw type usage: " + classType);
}
}
return super.visit(node, data);
}
}// Example: Find all classes that implement a specific interface
public class InterfaceImplementationFinder extends JavaVisitorBase<Void, Void> {
private JClassSymbol targetInterface;
private List<JClassSymbol> implementations = new ArrayList<>();
@Override
public Void visit(ASTClassDeclaration node, Void data) {
JClassSymbol classSymbol = node.getSymbol();
if (classSymbol != null) {
// Check if this class implements the target interface
if (implementsInterface(classSymbol, targetInterface)) {
implementations.add(classSymbol);
}
}
return super.visit(node, data);
}
private boolean implementsInterface(JClassSymbol clazz, JClassSymbol targetIntf) {
// Direct implementation check
for (JClassSymbol superIntf : clazz.getSuperInterfaces()) {
if (superIntf.equals(targetIntf)) {
return true;
}
}
// Recursive check through superclass
JClassSymbol superclass = clazz.getSuperclass();
if (superclass != null) {
return implementsInterface(superclass, targetIntf);
}
// Recursive check through super-interfaces
for (JClassSymbol superIntf : clazz.getSuperInterfaces()) {
if (implementsInterface(superIntf, targetIntf)) {
return true;
}
}
return false;
}
}Install with Tessl CLI
npx tessl i tessl/maven-net-sourceforge-pmd--pmd-java