0
# Symbol Resolution and Type System
1
2
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.
3
4
## Capabilities
5
6
### Symbol Resolution
7
8
The symbol system provides resolution of names to their declarations with full support for scoping rules, overloading, and generic type parameters.
9
10
#### Base Symbol Interface
11
12
```java { .api }
13
/**
14
* Represents a named program element that can be referred to by simple name
15
*/
16
public interface JElementSymbol {
17
/**
18
* Gets the name with which this declaration may be referred to
19
*/
20
String getSimpleName();
21
22
/**
23
* Returns true if the simple name of this symbol matches the given name
24
*/
25
default boolean nameEquals(@NonNull String name);
26
27
/**
28
* Returns the type system that created this symbol
29
*/
30
TypeSystem getTypeSystem();
31
32
/**
33
* Returns true if this symbol is a placeholder for an unresolved reference
34
*/
35
default boolean isUnresolved();
36
37
/**
38
* Returns the node that declares this symbol if in the current file
39
*/
40
default @Nullable JavaNode tryGetNode();
41
42
/**
43
* Dispatch to the appropriate visit method of the visitor
44
*/
45
<R, P> R acceptVisitor(SymbolVisitor<R, P> visitor, P param);
46
}
47
```
48
49
#### Class Symbols
50
51
```java { .api }
52
/**
53
* Represents class/interface/enum/annotation/record symbols
54
*/
55
public interface JClassSymbol extends JTypeDeclSymbol, JTypeParameterOwnerSymbol,
56
BoundToNode<ASTTypeDeclaration> {
57
58
/**
59
* Returns the binary name of this type (JLS §13.1)
60
*/
61
@NonNull String getBinaryName();
62
63
/**
64
* Returns the canonical name of this class
65
*/
66
@Nullable String getCanonicalName();
67
68
/**
69
* Returns the method or constructor this symbol is declared in for local classes
70
*/
71
@Nullable JExecutableSymbol getEnclosingMethod();
72
73
/**
74
* Returns the member classes declared directly in this class
75
*/
76
List<JClassSymbol> getDeclaredClasses();
77
78
/**
79
* Returns a class with the given name defined in this class
80
*/
81
@Nullable JClassSymbol getDeclaredClass(String name);
82
83
/**
84
* Returns the methods declared directly in this class (excludes bridges/synthetic)
85
*/
86
List<JMethodSymbol> getDeclaredMethods();
87
88
/**
89
* Returns the constructors declared by this class (excludes synthetic)
90
*/
91
List<JConstructorSymbol> getConstructors();
92
93
/**
94
* Returns the fields declared directly in this class (excludes synthetic)
95
*/
96
List<JFieldSymbol> getDeclaredFields();
97
98
/**
99
* Returns a field with the given name defined in this class
100
*/
101
@Nullable JFieldSymbol getDeclaredField(String name);
102
103
/**
104
* Returns all enum constants (subset of getDeclaredFields())
105
*/
106
default @NonNull List<JFieldSymbol> getEnumConstants();
107
108
/**
109
* Returns all record components (for record types)
110
*/
111
default @NonNull List<JRecordComponentSymbol> getRecordComponents();
112
113
/**
114
* Returns the superclass symbol if it exists
115
*/
116
@Nullable JClassSymbol getSuperclass();
117
118
/**
119
* Returns the direct super-interfaces of this class or interface
120
*/
121
List<JClassSymbol> getSuperInterfaces();
122
123
/**
124
* Returns super interface types under the given substitution
125
*/
126
List<JClassType> getSuperInterfaceTypes(Substitution substitution);
127
128
/**
129
* Returns the superclass type under the given substitution
130
*/
131
@Nullable JClassType getSuperclassType(Substitution substitution);
132
133
/**
134
* Returns the component symbol for array types
135
*/
136
@Nullable JTypeDeclSymbol getArrayComponent();
137
138
// Type checking methods
139
boolean isArray();
140
boolean isPrimitive();
141
boolean isEnum();
142
boolean isRecord();
143
boolean isAnnotation();
144
boolean isLocalClass();
145
boolean isAnonymousClass();
146
boolean isAbstract();
147
boolean isFinal();
148
149
/**
150
* Returns the list of permitted subclasses for sealed types
151
*/
152
default List<JClassSymbol> getPermittedSubtypes();
153
154
/**
155
* Returns true if this type is sealed
156
*/
157
default boolean isSealed();
158
159
/**
160
* Returns simple names of all annotation attributes (for annotation types)
161
*/
162
default PSet<String> getAnnotationAttributeNames();
163
}
164
```
165
166
#### Method Symbols
167
168
```java { .api }
169
/**
170
* Represents method symbols
171
*/
172
public interface JMethodSymbol extends JExecutableSymbol {
173
/**
174
* Returns the return type of this method
175
*/
176
JTypeMirror getReturnType();
177
178
/**
179
* Returns the return type with the given substitution applied
180
*/
181
JTypeMirror getReturnType(Substitution subst);
182
183
/**
184
* Returns true if this is an abstract method
185
*/
186
boolean isAbstract();
187
188
/**
189
* Returns true if this is a default method (interface default)
190
*/
191
boolean isDefault();
192
193
/**
194
* Returns true if this is a native method
195
*/
196
boolean isNative();
197
198
/**
199
* Returns true if this is a synchronized method
200
*/
201
boolean isSynchronized();
202
203
/**
204
* Returns the method signature including parameter types
205
*/
206
JMethodSig getSignature();
207
208
/**
209
* Returns the method signature with substitution applied
210
*/
211
JMethodSig getSignature(Substitution subst);
212
}
213
```
214
215
#### Field Symbols
216
217
```java { .api }
218
/**
219
* Represents field symbols
220
*/
221
public interface JFieldSymbol extends JVariableSymbol {
222
/**
223
* Returns the type of this field
224
*/
225
JTypeMirror getTypeMirror();
226
227
/**
228
* Returns the type with the given substitution applied
229
*/
230
JTypeMirror getTypeMirror(Substitution subst);
231
232
/**
233
* Returns true if this is a static field
234
*/
235
boolean isStatic();
236
237
/**
238
* Returns true if this is a final field
239
*/
240
boolean isFinal();
241
242
/**
243
* Returns true if this is a volatile field
244
*/
245
boolean isVolatile();
246
247
/**
248
* Returns true if this is a transient field
249
*/
250
boolean isTransient();
251
252
/**
253
* Returns true if this field is an enum constant
254
*/
255
boolean isEnumConstant();
256
257
/**
258
* Returns the constant value if this is a compile-time constant
259
*/
260
@Nullable Object getConstantValue();
261
}
262
```
263
264
#### Variable Symbols
265
266
```java { .api }
267
/**
268
* Base interface for variables (local variables, parameters, fields)
269
*/
270
public interface JVariableSymbol extends JElementSymbol {
271
/**
272
* Returns the type of this variable
273
*/
274
JTypeMirror getTypeMirror();
275
276
/**
277
* Returns true if this variable is effectively final
278
*/
279
boolean isEffectivelyFinal();
280
281
/**
282
* Returns the modifiers of this variable
283
*/
284
int getModifiers();
285
}
286
287
/**
288
* Represents local variable symbols
289
*/
290
public interface JLocalVariableSymbol extends JVariableSymbol {
291
/**
292
* Returns true if this is a final local variable
293
*/
294
boolean isFinal();
295
}
296
297
/**
298
* Represents formal parameter symbols
299
*/
300
public interface JFormalParamSymbol extends JVariableSymbol {
301
/**
302
* Returns true if this is a varargs parameter
303
*/
304
boolean isVarargs();
305
306
/**
307
* Returns true if this is a final parameter
308
*/
309
boolean isFinal();
310
}
311
```
312
313
### Symbol Table
314
315
```java { .api }
316
/**
317
* Provides symbol resolution within scopes
318
*/
319
public interface JSymbolTable {
320
/**
321
* Resolve variables by simple name in the current scope
322
*/
323
List<JVariableSymbol> variables();
324
325
/**
326
* Resolve types by simple name in the current scope
327
*/
328
List<JClassSymbol> types();
329
330
/**
331
* Resolve methods by simple name in the current scope
332
*/
333
List<JMethodSymbol> methods();
334
}
335
```
336
337
## Type System
338
339
The type system provides complete representation of Java types including generics, wildcards, and type inference.
340
341
### Base Type Interface
342
343
```java { .api }
344
/**
345
* Type mirrors represent Java types
346
*/
347
public interface JTypeMirror extends JTypeVisitable {
348
/**
349
* Returns the type system that built this type
350
*/
351
TypeSystem getTypeSystem();
352
353
/**
354
* Return annotations on this type
355
*/
356
PSet<SymAnnot> getTypeAnnotations();
357
358
/**
359
* Returns true if this type is the same type or a subtype of the given type
360
*/
361
default boolean isSubtypeOf(@NonNull JTypeMirror other);
362
363
/**
364
* Tests this type's convertibility to the other type
365
*/
366
default Convertibility isConvertibleTo(@NonNull JTypeMirror other);
367
368
/**
369
* Returns the set of (nominal) supertypes of this type
370
*/
371
default Set<JTypeMirror> getSuperTypeSet();
372
373
/**
374
* Returns the erased version of this type (removes generics)
375
*/
376
JTypeMirror getErasure();
377
378
/**
379
* Returns the symbol that declared this type (class/interface/primitive)
380
*/
381
@Nullable JTypeDeclSymbol getSymbol();
382
383
/**
384
* Returns the boxed version of this type (primitive -> wrapper)
385
*/
386
default JTypeMirror box();
387
388
/**
389
* Returns the unboxed version of this type (wrapper -> primitive)
390
*/
391
default JTypeMirror unbox();
392
393
// Type checking methods
394
boolean isPrimitive();
395
boolean isArray();
396
boolean isClassOrInterface();
397
boolean isInterface();
398
boolean isClass();
399
boolean isGeneric();
400
boolean isParameterizedType();
401
boolean isRaw();
402
boolean isTypeVariable();
403
boolean isWildcard();
404
boolean isIntersectionType();
405
boolean isBottom();
406
boolean isTop();
407
}
408
```
409
410
### Primitive Types
411
412
```java { .api }
413
/**
414
* Represents primitive types (int, boolean, etc.)
415
*/
416
public interface JPrimitiveType extends JTypeMirror {
417
/**
418
* Returns the kind of primitive type
419
*/
420
PrimitiveTypeKind getKind();
421
422
/**
423
* Returns the wrapper class type for this primitive
424
*/
425
JClassType box();
426
427
/**
428
* Enumeration of primitive type kinds
429
*/
430
enum PrimitiveTypeKind {
431
BOOLEAN("boolean", boolean.class),
432
BYTE("byte", byte.class),
433
SHORT("short", short.class),
434
INT("int", int.class),
435
LONG("long", long.class),
436
CHAR("char", char.class),
437
FLOAT("float", float.class),
438
DOUBLE("double", double.class);
439
440
String getSimpleName();
441
Class<?> toReflect();
442
}
443
}
444
```
445
446
### Class Types
447
448
```java { .api }
449
/**
450
* Represents class and interface types (possibly generic)
451
*/
452
public interface JClassType extends JTypeMirror {
453
/**
454
* Returns the type arguments of this parameterized type
455
*/
456
List<JTypeMirror> getTypeArgs();
457
458
/**
459
* Returns the generic type declaration
460
*/
461
JGenericDeclaration getGenericTypeDeclaration();
462
463
/**
464
* Returns the raw type (without type arguments)
465
*/
466
JClassType getRawType();
467
468
/**
469
* Returns true if this is a raw type
470
*/
471
boolean isRaw();
472
473
/**
474
* Returns true if this is a parameterized type
475
*/
476
boolean isParameterized();
477
478
/**
479
* Returns the class symbol
480
*/
481
@Override
482
JClassSymbol getSymbol();
483
484
/**
485
* Returns the enclosing type for nested classes
486
*/
487
@Nullable JClassType getEnclosingType();
488
489
/**
490
* Returns a field with the given name accessible from this type
491
*/
492
@Nullable JFieldSymbol getDeclaredField(String fieldName);
493
494
/**
495
* Returns a nested class with the given name
496
*/
497
@Nullable JClassSymbol getDeclaredClass(String name);
498
499
/**
500
* Applies a substitution to this type
501
*/
502
JClassType subst(Substitution substitution);
503
504
/**
505
* Returns the superclass type
506
*/
507
@Nullable JClassType getSuperClass();
508
509
/**
510
* Returns the super interface types
511
*/
512
List<JClassType> getSuperInterfaces();
513
514
/**
515
* Returns all supertypes (direct and indirect)
516
*/
517
Stream<JClassType> streamSuperTypes();
518
}
519
```
520
521
### Array Types
522
523
```java { .api }
524
/**
525
* Represents array types
526
*/
527
public interface JArrayType extends JTypeMirror {
528
/**
529
* Returns the component type of this array
530
*/
531
JTypeMirror getComponentType();
532
533
/**
534
* Returns the element type (ultimate component for multi-dimensional arrays)
535
*/
536
JTypeMirror getElementType();
537
538
/**
539
* Returns the number of array dimensions
540
*/
541
int getNumDimensions();
542
543
/**
544
* Returns the array symbol
545
*/
546
@Override
547
JClassSymbol getSymbol();
548
549
/**
550
* Creates a new array type with additional dimensions
551
*/
552
JArrayType addDimensions(int n);
553
}
554
```
555
556
### Type Variables
557
558
```java { .api }
559
/**
560
* Represents type variables (generic type parameters)
561
*/
562
public interface JTypeVar extends JTypeMirror {
563
/**
564
* Returns the name of this type variable
565
*/
566
String getName();
567
568
/**
569
* Returns the type parameter symbol
570
*/
571
JTypeParameterSymbol getSymbol();
572
573
/**
574
* Returns the upper bounds of this type variable
575
*/
576
List<JTypeMirror> getUpperBounds();
577
578
/**
579
* Returns the lower bounds of this type variable
580
*/
581
List<JTypeMirror> getLowerBounds();
582
}
583
```
584
585
### Wildcard Types
586
587
```java { .api }
588
/**
589
* Represents wildcard types (? extends, ? super)
590
*/
591
public interface JWildcardType extends JTypeMirror {
592
/**
593
* Returns the upper bound (extends clause)
594
*/
595
@Nullable JTypeMirror getUpperBound();
596
597
/**
598
* Returns the lower bound (super clause)
599
*/
600
@Nullable JTypeMirror getLowerBound();
601
602
/**
603
* Returns true if this is an unbounded wildcard (?)
604
*/
605
boolean isUnbounded();
606
607
/**
608
* Returns true if this is an upper bounded wildcard (? extends T)
609
*/
610
boolean isUpperBounded();
611
612
/**
613
* Returns true if this is a lower bounded wildcard (? super T)
614
*/
615
boolean isLowerBounded();
616
}
617
```
618
619
### Type System Factory
620
621
```java { .api }
622
/**
623
* Factory and registry for all types
624
*/
625
public class TypeSystem {
626
// Special type constants
627
public static final JTypeMirror OBJECT = /* ... */;
628
public static final JTypeMirror NULL_TYPE = /* ... */;
629
public static final JTypeMirror ERROR = /* ... */;
630
public static final JTypeMirror UNKNOWN = /* ... */;
631
public static final JTypeMirror NO_TYPE = /* ... */;
632
633
// Primitive type constants
634
public static final JPrimitiveType BOOLEAN = /* ... */;
635
public static final JPrimitiveType BYTE = /* ... */;
636
public static final JPrimitiveType SHORT = /* ... */;
637
public static final JPrimitiveType INT = /* ... */;
638
public static final JPrimitiveType LONG = /* ... */;
639
public static final JPrimitiveType CHAR = /* ... */;
640
public static final JPrimitiveType FLOAT = /* ... */;
641
public static final JPrimitiveType DOUBLE = /* ... */;
642
643
/**
644
* Returns the type mirror for the given class
645
*/
646
public JTypeMirror typeOf(Class<?> clazz);
647
648
/**
649
* Returns the type mirror for the given class symbol
650
*/
651
public JClassType typeOf(JClassSymbol symbol);
652
653
/**
654
* Creates an array type with the given component type
655
*/
656
public JArrayType arrayOf(JTypeMirror componentType);
657
658
/**
659
* Creates a parameterized type
660
*/
661
public JClassType parameterize(JClassSymbol symbol, List<JTypeMirror> typeArgs);
662
663
/**
664
* Creates a wildcard type
665
*/
666
public JWildcardType wildcard(boolean isExtends, @Nullable JTypeMirror bound);
667
668
/**
669
* Returns the least upper bound of the given types
670
*/
671
public JTypeMirror lub(List<JTypeMirror> types);
672
673
/**
674
* Returns the greatest lower bound of the given types
675
*/
676
public JTypeMirror glb(List<JTypeMirror> types);
677
678
/**
679
* Performs type inference for method calls
680
*/
681
public Substitution inferMethodTypeArgs(JMethodSymbol method,
682
List<JTypeMirror> actualArgs);
683
}
684
```
685
686
## Usage Examples
687
688
### Basic Symbol Resolution
689
690
```java
691
// Example: Find all method calls to a specific method
692
public class MethodCallAnalyzer extends JavaVisitorBase<Void, Void> {
693
private String targetMethod;
694
private JClassSymbol targetClass;
695
private List<ASTMethodCall> matchingCalls = new ArrayList<>();
696
697
@Override
698
public Void visit(ASTMethodCall node, Void data) {
699
// Get the method symbol
700
JMethodSymbol methodSymbol = node.getMethodType();
701
702
if (methodSymbol != null && !methodSymbol.isUnresolved()) {
703
// Check if this is the target method
704
if (methodSymbol.nameEquals(targetMethod) &&
705
methodSymbol.getEnclosingClass().equals(targetClass)) {
706
matchingCalls.add(node);
707
}
708
}
709
710
return super.visit(node, data);
711
}
712
}
713
```
714
715
### Type Analysis
716
717
```java
718
// Example: Analyze generic type usage
719
public class GenericTypeAnalyzer extends JavaVisitorBase<Void, Void> {
720
@Override
721
public Void visit(ASTVariableDeclaration node, Void data) {
722
JTypeMirror varType = node.getTypeMirror();
723
724
if (varType.isClassOrInterface()) {
725
JClassType classType = (JClassType) varType;
726
727
if (classType.isParameterized()) {
728
System.out.println("Found parameterized type: " + classType);
729
730
// Analyze type arguments
731
for (JTypeMirror typeArg : classType.getTypeArgs()) {
732
if (typeArg.isWildcard()) {
733
JWildcardType wildcard = (JWildcardType) typeArg;
734
System.out.println(" Wildcard: " + wildcard);
735
736
if (wildcard.isUpperBounded()) {
737
System.out.println(" Upper bound: " + wildcard.getUpperBound());
738
}
739
if (wildcard.isLowerBounded()) {
740
System.out.println(" Lower bound: " + wildcard.getLowerBound());
741
}
742
}
743
}
744
} else if (classType.isRaw()) {
745
System.out.println("Found raw type usage: " + classType);
746
}
747
}
748
749
return super.visit(node, data);
750
}
751
}
752
```
753
754
### Inheritance Analysis
755
756
```java
757
// Example: Find all classes that implement a specific interface
758
public class InterfaceImplementationFinder extends JavaVisitorBase<Void, Void> {
759
private JClassSymbol targetInterface;
760
private List<JClassSymbol> implementations = new ArrayList<>();
761
762
@Override
763
public Void visit(ASTClassDeclaration node, Void data) {
764
JClassSymbol classSymbol = node.getSymbol();
765
766
if (classSymbol != null) {
767
// Check if this class implements the target interface
768
if (implementsInterface(classSymbol, targetInterface)) {
769
implementations.add(classSymbol);
770
}
771
}
772
773
return super.visit(node, data);
774
}
775
776
private boolean implementsInterface(JClassSymbol clazz, JClassSymbol targetIntf) {
777
// Direct implementation check
778
for (JClassSymbol superIntf : clazz.getSuperInterfaces()) {
779
if (superIntf.equals(targetIntf)) {
780
return true;
781
}
782
}
783
784
// Recursive check through superclass
785
JClassSymbol superclass = clazz.getSuperclass();
786
if (superclass != null) {
787
return implementsInterface(superclass, targetIntf);
788
}
789
790
// Recursive check through super-interfaces
791
for (JClassSymbol superIntf : clazz.getSuperInterfaces()) {
792
if (implementsInterface(superIntf, targetIntf)) {
793
return true;
794
}
795
}
796
797
return false;
798
}
799
}
800
```