• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pmd / pmd / 277

27 Nov 2025 01:37PM UTC coverage: 78.778% (+0.03%) from 78.749%
277

push

github

adangel
[java] UseArraysAsList: skip when if-statements (#6228)

18419 of 24233 branches covered (76.01%)

Branch coverage included in aggregate %.

40090 of 50038 relevant lines covered (80.12%)

0.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.52
/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JClassSymbol.java
1
/*
2
 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3
 */
4

5

6
package net.sourceforge.pmd.lang.java.symbols;
7

8
import java.lang.annotation.ElementType;
9
import java.lang.annotation.Retention;
10
import java.lang.annotation.RetentionPolicy;
11
import java.lang.annotation.Target;
12
import java.lang.reflect.Modifier;
13
import java.util.Collections;
14
import java.util.List;
15
import java.util.Optional;
16

17
import org.checkerframework.checker.nullness.qual.NonNull;
18
import org.checkerframework.checker.nullness.qual.Nullable;
19
import org.pcollections.HashTreePSet;
20
import org.pcollections.PSet;
21

22
import net.sourceforge.pmd.lang.LanguageRegistry;
23
import net.sourceforge.pmd.lang.LanguageVersion;
24
import net.sourceforge.pmd.lang.java.ast.ASTTypeDeclaration;
25
import net.sourceforge.pmd.lang.java.symbols.SymbolicValue.SymAnnot;
26
import net.sourceforge.pmd.lang.java.symbols.SymbolicValue.SymEnum;
27
import net.sourceforge.pmd.lang.java.types.JArrayType;
28
import net.sourceforge.pmd.lang.java.types.JClassType;
29
import net.sourceforge.pmd.lang.java.types.JPrimitiveType;
30
import net.sourceforge.pmd.lang.java.types.JTypeMirror;
31
import net.sourceforge.pmd.lang.java.types.Substitution;
32
import net.sourceforge.pmd.util.OptionalBool;
33

34

35
/**
36
 * Abstraction over a {@link Class} instance. This is not a type, it's
37
 * the *declaration* of a type. For example, a class symbol representing
38
 * a generic class can provide access to the formal type parameters, but
39
 * the symbol does not represent a specific parametrization of a type.
40
 *
41
 * <p>Class symbols represent the full range of types represented by {@link Class}:
42
 * classes, interfaces, arrays, and primitives. This excludes type variables,
43
 * intersection types, parameterized types, wildcard types, etc., which are only
44
 * compile-time constructs.
45
 *
46
 * <p>Class symbols are used to back {@link JClassType}, {@link JArrayType},
47
 * and {@link JPrimitiveType}. See {@link JTypeMirror#getSymbol()}.
48
 *
49
 * @since 7.0.0
50
 */
51
public interface JClassSymbol extends JTypeDeclSymbol,
52
                                      JTypeParameterOwnerSymbol,
53
                                      BoundToNode<ASTTypeDeclaration> {
54

55

56
    /**
57
     * Returns the binary name of this type, as specified by the JLS:
58
     * <a href="https://docs.oracle.com/javase/specs/jls/se7/html/jls-13.html#jls-13.1">the JLS</a>.
59
     * For array types this returns the binary name of the component followed by "[]".
60
     * This differs from {@link Class#getName()}, which for array types outputs an
61
     * <i>internal name</i>.
62
     *
63
     * <p>For example:
64
     * <pre>{@code
65
     * int.class.getName() == "int"
66
     * int[].class.getName() == "[I"
67
     * String.class.getName() == "java.lang.String"
68
     * String[].class.getName() == "[Ljava.lang.String;"
69
     * }</pre>
70
     * whereas
71
     * <pre>{@code
72
     * symbolOf(int.class).getBinaryName() == "int"
73
     * symbolOf(int[].class).getBinaryName() == "int[]"
74
     * symbolOf(String.class).getBinaryName() == "java.lang.String"
75
     * symbolOf(String[].class).getBinaryName() == "java.lang.String[]"
76
     * }</pre>
77
     */
78
    @NonNull
79
    String getBinaryName();
80

81

82
    /**
83
     * Returns the simple name of this class, as specified by
84
     * {@link Class#getCanonicalName()}.
85
     */
86
    @Nullable
87
    String getCanonicalName();
88

89

90
    /**
91
     * Returns the method or constructor this symbol is declared in, if
92
     * it represents a {@linkplain #isLocalClass() local class declaration}
93
     * or an anonymous class declaration.
94
     *
95
     * <p>Notice, that this returns null also if this class is local to
96
     * a class or instance initializer, a field initializer, and some other
97
     * special circumstances.
98
     *
99
     * @see Class#getEnclosingMethod()
100
     */
101
    @Nullable JExecutableSymbol getEnclosingMethod();
102

103
    @Override
104
    default JTypeParameterOwnerSymbol getEnclosingTypeParameterOwner() {
105
        JExecutableSymbol enclosingMethod = getEnclosingMethod();
×
106
        return enclosingMethod != null ? enclosingMethod : getEnclosingClass();
×
107
    }
108

109

110
    /**
111
     * Returns the member classes declared directly in this class.
112
     *
113
     * @see Class#getDeclaredClasses()
114
     */
115
    List<JClassSymbol> getDeclaredClasses();
116

117

118
    /** Returns a class with the given name defined in this class. */
119
    default @Nullable JClassSymbol getDeclaredClass(String name) {
120
        for (JClassSymbol klass : getDeclaredClasses()) {
1✔
121
            if (klass.nameEquals(name)) {
1✔
122
                return klass;
1✔
123
            }
124
        }
1✔
125
        return null;
1✔
126
    }
127

128

129
    /**
130
     * Returns the methods declared directly in this class.
131
     * <i>This excludes bridges and other synthetic methods.</i>
132
     *
133
     * <p>For an array type T[], to the difference of {@link Class},
134
     * this method returns a one-element list with the {@link Object#clone()}
135
     * method, as if declared like so: {@code public final T[] clone() {...}}.
136
     *
137
     * @see Class#getDeclaredMethods()
138
     */
139
    List<JMethodSymbol> getDeclaredMethods();
140

141

142
    /**
143
     * Returns the constructors declared by this class.
144
     * <i>This excludes synthetic constructors.</i>
145
     *
146
     * <p>For an array type T[], and to the difference of {@link Class},
147
     * this should return a one-element list with a constructor
148
     * having the same modifiers as the array type, and a single
149
     * {@code int} parameter.
150
     *
151
     * @see Class#getDeclaredConstructors()
152
     */
153
    List<JConstructorSymbol> getConstructors();
154

155

156
    /**
157
     * Returns the fields declared directly in this class.
158
     * <i>This excludes synthetic fields.</i>
159
     *
160
     * <p>For arrays, and to the difference of {@link Class},
161
     * this should return a one-element list with the
162
     * {@code public final int length} field.
163
     *
164
     * @see Class#getDeclaredFields()
165
     */
166
    List<JFieldSymbol> getDeclaredFields();
167

168

169
    /** Returns a field with the given name defined in this class. */
170
    default @Nullable JFieldSymbol getDeclaredField(String name) {
171
        for (JFieldSymbol field : getDeclaredFields()) {
1✔
172
            if (field.nameEquals(name)) {
1✔
173
                return field;
1✔
174
            }
175
        }
1✔
176
        return null;
1✔
177
    }
178

179
    /**
180
     * Returns a list with all enum constants. If this symbol does
181
     * not represent an enum, returns an empty list. The returned list
182
     * is a subset of {@link #getDeclaredFields()}. The order of fields
183
     * denotes the normal order of enum constants.
184
     */
185
    default @NonNull List<JFieldSymbol> getEnumConstants() {
186
        return Collections.emptyList();
×
187
    }
188

189

190
    /**
191
     * Returns a list with all record components. If this symbol does
192
     * not represent a record, returns an empty list. The order of values
193
     * denotes the normal order of components.
194
     */
195
    default @NonNull List<JRecordComponentSymbol> getRecordComponents() {
196
        return Collections.emptyList();
×
197
    }
198

199

200
    /** Returns the list of super interface types, under the given substitution. */
201
    List<JClassType> getSuperInterfaceTypes(Substitution substitution);
202

203

204
    /** Returns the superclass type, under the given substitution. */
205
    @Nullable JClassType getSuperclassType(Substitution substitution);
206

207

208
    /**
209
     * Returns the superclass symbol if it exists. Returns null if this
210
     * class represents the class {@link Object}, or a primitive type.
211
     * If this symbol is an interface, returns the symbol for {@link Object}.
212
     */
213
    @Nullable
214
    JClassSymbol getSuperclass();
215

216

217
    /** Returns the direct super-interfaces of this class or interface symbol. */
218
    List<JClassSymbol> getSuperInterfaces();
219

220

221
    default boolean isAbstract() {
222
        return Modifier.isAbstract(getModifiers());
1✔
223
    }
224

225

226
    /** Returns the component symbol, returns null if this is not an array. */
227
    @Nullable
228
    JTypeDeclSymbol getArrayComponent();
229

230

231
    boolean isArray();
232

233
    boolean isPrimitive();
234

235
    boolean isEnum();
236

237
    boolean isRecord();
238

239
    boolean isAnnotation();
240

241
    boolean isLocalClass();
242

243
    boolean isAnonymousClass();
244

245
    /**
246
     * Return the list of permitted subclasses or subinterfaces, as defined in the
247
     * {@code permits} clause of a sealed class or interface. If this class is sealed
248
     * but has no permits clause, the permitted subtypes are inferred from the types
249
     * in the compilation unit. If the class is not sealed, returns an empty list.
250
     *
251
     * <p>Note that an enum class for which some constants declare a body is technically
252
     * implicitly sealed, and implicitly permits only the anonymous classes for those enum
253
     * constants. For consistency, this method will return only symbols that have a canonical
254
     * name, and therefore always return an empty list for enums.
255
     *
256
     * @see #isSealed()
257
     */
258
    default List<JClassSymbol> getPermittedSubtypes() {
259
        return Collections.emptyList();
1✔
260
    }
261

262
    /**
263
     * Return true if this type is sealed. Then it has a non-empty list of permitted
264
     * subclasses (or it is a compile-time error). Note that there is no trace of the
265
     * non-sealed modifier in class files. A class must have the {@code non-sealed}
266
     * modifier if it is not sealed, not final, and has a sealed supertype.
267
     *
268
     * <p>Note that an enum class for which some constants declare a body is technically
269
     * implicitly sealed, and implicitly permits only the anonymous classes for those enum
270
     * constants. For consistency with {@link #getPermittedSubtypes()}, we treat such enums
271
     * as not sealed.
272
     *
273
     * @see #getPermittedSubtypes()
274
     */
275
    default boolean isSealed() {
276
        return !getPermittedSubtypes().isEmpty();
1✔
277
    }
278

279
    /**
280
     * Return true if this type is final, that is, does not admit subtypes. Note that
281
     * array types have both modifiers final and abstract. Note also that enum classes
282
     * may be non-final if they have constants that declare an anonymous body.
283
     */
284
    default boolean isFinal() {
285
        return Modifier.isFinal(getModifiers());
1✔
286
    }
287

288
    /**
289
     * Return the simple names of all annotation attributes. If this
290
     * is not an annotation type, return an empty set.
291
     */
292
    default PSet<String> getAnnotationAttributeNames() {
293
        return HashTreePSet.empty();
×
294
    }
295

296
    /**
297
     * Return the default value of the attribute if this is an annotation type
298
     * with a default. Return null if this is not an annotation type, if there
299
     * is no such attribute, or the attribute has no default value. If the name
300
     * is in the {@linkplain  #getAnnotationAttributeNames() attribute name set},
301
     * then the null return value can only mean that the attribute exists but has
302
     * no default value.
303
     *
304
     * @param attrName Attribute name
305
     */
306
    default @Nullable SymbolicValue getDefaultAnnotationAttributeValue(String attrName) {
307
        if (!isAnnotation()) {
1!
308
            return null;
×
309
        }
310
        for (JMethodSymbol m : getDeclaredMethods()) {
1!
311
            if (m.nameEquals(attrName) && m.isAnnotationAttribute()) {
1!
312
                return m.getDefaultAnnotationValue(); // nullable
1✔
313
            }
314
        }
1✔
315
        return null;
×
316
    }
317

318
    /**
319
     * Returns the retention policy of this annotation, if this is an
320
     * annotation symbol. Otherwise returns null.
321
     */
322
    default @Nullable RetentionPolicy getAnnotationRetention() {
323
        if (!isAnnotation()) {
1!
324
            return null;
×
325
        }
326
        return Optional.ofNullable(getDeclaredAnnotation(Retention.class))
1✔
327
                       .map(annot -> annot.getAttribute("value"))
1✔
328
                       .filter(value -> value instanceof SymEnum)
1✔
329
                       .map(value -> ((SymEnum) value).toEnum(RetentionPolicy.class))
1✔
330
                       .orElse(RetentionPolicy.CLASS);
1✔
331
    }
332

333
    /**
334
     * Return whether annotations of this annotation type apply to the
335
     * given construct, as per the {@link Target} annotation. Return
336
     * false if this is not an annotation.
337
     * @deprecated Since 7.17.0. Use {@link #annotationAppliesToContext(ElementType, LanguageVersion)}
338
     */
339
    @Deprecated
340
    default boolean annotationAppliesTo(ElementType elementType) {
341
        LanguageVersion javaVer = LanguageRegistry.PMD.getLanguageById("java").getDefaultVersion();
1✔
342
        return annotationAppliesToContext(elementType, javaVer) != OptionalBool.NO;
1✔
343
    }
344

345
    /**
346
     * Return whether annotations of this annotation type apply to the
347
     * given construct, as per the {@link Target} annotation. Return
348
     * false if this is not an annotation. May return unknown if we are
349
     * not sure.
350
     * @since 7.17.0
351
     */
352
    default OptionalBool annotationAppliesToContext(ElementType elementType, LanguageVersion javaVersion) {
353
        if (isUnresolved()) {
1✔
354
            return OptionalBool.UNKNOWN;
1✔
355
        }
356
        if (!isAnnotation()) {
1✔
357
            return OptionalBool.NO;
1✔
358
        }
359
        SymAnnot target = getDeclaredAnnotation(Target.class);
1✔
360
        if (target == null) {
1✔
361
            /*
362
             If a @Target meta-annotation is not present on an annotation type T, then
363
             behavior depends on java version. It changed a couple of times between different
364
             java versions:
365
             - java 5-7: all decl except TP
366
             - java 8-12: all decl except TP, no type
367
             - java 13-16: all decl, all type
368
             - java 17+:  all decl, no type
369
             */
370
            if (elementType == ElementType.TYPE_PARAMETER) {
1✔
371
                return OptionalBool.definitely(javaVersion.compareToVersion("13") >= 0);
1!
372
            } else if (elementType == ElementType.TYPE_USE) {
1✔
373
                return OptionalBool.definitely(javaVersion.compareToVersion("13") >= 0
1✔
374
                                                   && javaVersion.compareToVersion("17") < 0);
1!
375
            } else {
376
                return OptionalBool.YES;
1✔
377
            }
378
        }
379
        return target.attributeContains("value", elementType);
1✔
380
    }
381

382
    /**
383
     * Return whether this is an annotation type, and can apply to type use.
384
     * Whether a specific annotation of this type is a type annotation also depends
385
     * on the context where it appears, as this annotation type may be applicable
386
     * to other contexts than {@link ElementType#TYPE_USE}.
387
     * @since 7.17.0
388
     */
389
    default OptionalBool mayBeTypeAnnotation(LanguageVersion lv) {
390
        return annotationAppliesToContext(ElementType.TYPE_USE, lv);
1✔
391
    }
392

393
    /**
394
     * This returns true if this is not an interface, primitive or array.
395
     * Note that this includes in particular records and enums.
396
     */
397
    default boolean isClass() {
398
        return !isInterface() && !isArray() && !isPrimitive();
1!
399
    }
400

401

402
    /**
403
     * Returns the toplevel class containing this class. If this is a
404
     * toplevel class, returns this.
405
     */
406
    default @NonNull JClassSymbol getNestRoot() {
407
        JClassSymbol e = this;
1✔
408
        while (e.getEnclosingClass() != null) {
1✔
409
            e = e.getEnclosingClass();
1✔
410
        }
411
        return e;
1✔
412
    }
413

414

415
    @Override
416
    default <R, P> R acceptVisitor(SymbolVisitor<R, P> visitor, P param) {
417
        return visitor.visitClass(this, param);
1✔
418
    }
419
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc