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

pmd / pmd / #3722

pending completion
#3722

push

github actions

adangel
Suppress MissingOverride for Chars::isEmpty (#4291)

67270 of 127658 relevant lines covered (52.7%)

0.53 hits per line

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

82.93
/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.java.ast.ASTAnyTypeDeclaration;
23
import net.sourceforge.pmd.lang.java.symbols.SymbolicValue.SymAnnot;
24
import net.sourceforge.pmd.lang.java.symbols.SymbolicValue.SymEnum;
25
import net.sourceforge.pmd.lang.java.types.JArrayType;
26
import net.sourceforge.pmd.lang.java.types.JClassType;
27
import net.sourceforge.pmd.lang.java.types.JPrimitiveType;
28
import net.sourceforge.pmd.lang.java.types.JTypeMirror;
29
import net.sourceforge.pmd.lang.java.types.Substitution;
30

31

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

52

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

78

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

86

87
    /**
88
     * Returns the method or constructor this symbol is declared in, if
89
     * it represents a {@linkplain #isLocalClass() local class declaration}.
90
     *
91
     * <p>Notice, that this returns null also if this class is local to
92
     * a class or instance initializer.
93
     */
94
    @Nullable JExecutableSymbol getEnclosingMethod();
95

96
    @Override
97
    default JTypeParameterOwnerSymbol getEnclosingTypeParameterOwner() {
98
        JExecutableSymbol enclosingMethod = getEnclosingMethod();
×
99
        return enclosingMethod != null ? enclosingMethod : getEnclosingClass();
×
100
    }
101

102

103
    /**
104
     * Returns the member classes declared directly in this class.
105
     *
106
     * @see Class#getDeclaredClasses()
107
     */
108
    List<JClassSymbol> getDeclaredClasses();
109

110

111
    /** Returns a class with the given name defined in this class. */
112
    @Nullable
113
    default JClassSymbol getDeclaredClass(String name) {
114
        for (JClassSymbol klass : getDeclaredClasses()) {
1✔
115
            if (klass.nameEquals(name)) {
1✔
116
                return klass;
1✔
117
            }
118
        }
1✔
119
        return null;
1✔
120
    }
121

122

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

135

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

149

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

162

163
    /** Returns a field with the given name defined in this class. */
164
    @Nullable
165
    default JFieldSymbol getDeclaredField(String name) {
166
        for (JFieldSymbol field : getDeclaredFields()) {
1✔
167
            if (field.nameEquals(name)) {
1✔
168
                return field;
1✔
169
            }
170
        }
1✔
171
        return null;
1✔
172
    }
173

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

184

185
    /** Returns the list of super interface types, under the given substitution. */
186
    List<JClassType> getSuperInterfaceTypes(Substitution substitution);
187

188

189
    /** Returns the superclass type, under the given substitution. */
190
    @Nullable JClassType getSuperclassType(Substitution substitution);
191

192

193
    /**
194
     * Returns the superclass symbol if it exists. Returns null if this
195
     * class represents the class {@link Object}, or a primitive type.
196
     * If this symbol is an interface, returns the symbol for {@link Object}.
197
     */
198
    @Nullable
199
    JClassSymbol getSuperclass();
200

201

202
    /** Returns the direct super-interfaces of this class or interface symbol. */
203
    List<JClassSymbol> getSuperInterfaces();
204

205

206
    default boolean isAbstract() {
207
        return Modifier.isAbstract(getModifiers());
1✔
208
    }
209

210

211
    /** Returns the component symbol, returns null if this is not an array. */
212
    @Nullable
213
    JTypeDeclSymbol getArrayComponent();
214

215

216
    boolean isArray();
217

218
    boolean isPrimitive();
219

220
    boolean isEnum();
221

222
    boolean isRecord();
223

224
    boolean isAnnotation();
225

226
    boolean isLocalClass();
227

228
    boolean isAnonymousClass();
229

230
    /**
231
     * Return the simple names of all annotation attributes. If this
232
     * is not an annotation type, return an empty set.
233
     */
234
    default PSet<String> getAnnotationAttributeNames() {
235
        return HashTreePSet.empty();
×
236
    }
237

238
    /**
239
     * Return the default value of the attribute if this is an annotation type
240
     * with a default. Return null if this is not an annotation type, if there
241
     * is no such attribute, or the attribute has no default value. If the name
242
     * is in the {@linkplain  #getAnnotationAttributeNames() attribute name set},
243
     * then the null return value can only mean that the attribute exists but has
244
     * no default value.
245
     *
246
     * @param attrName Attribute name
247
     */
248
    default @Nullable SymbolicValue getDefaultAnnotationAttributeValue(String attrName) {
249
        if (!isAnnotation()) {
1✔
250
            return null;
×
251
        }
252
        for (JMethodSymbol m : getDeclaredMethods()) {
1✔
253
            if (m.nameEquals(attrName) && m.isAnnotationAttribute()) {
1✔
254
                return m.getDefaultAnnotationValue(); // nullable
1✔
255
            }
256
        }
1✔
257
        return null;
×
258
    }
259

260
    /**
261
     * Returns the retention policy of this annotation, if this is an
262
     * annotation symbol. Otherwise returns null.
263
     */
264
    default @Nullable RetentionPolicy getAnnotationRetention() {
265
        if (!isAnnotation()) {
1✔
266
            return null;
×
267
        }
268
        return Optional.ofNullable(getDeclaredAnnotation(Retention.class))
1✔
269
                       .map(annot -> annot.getAttribute("value"))
1✔
270
                       .filter(value -> value instanceof SymEnum)
1✔
271
                       .map(value -> ((SymEnum) value).toEnum(RetentionPolicy.class))
1✔
272
                       .orElse(RetentionPolicy.CLASS);
1✔
273
    }
274

275
    /**
276
     * Return whether annotations of this annotation type apply to the
277
     * given construct, as per the {@link Target} annotation. Return
278
     * false if this is not an annotation.
279
     */
280
    default boolean annotationAppliesTo(ElementType elementType) {
281
        if (!isAnnotation()) {
1✔
282
            return false;
1✔
283
        }
284
        SymAnnot target = getDeclaredAnnotation(Target.class);
1✔
285
        if (target == null) {
1✔
286
            // If an @Target meta-annotation is not present on an annotation type T,
287
            // then an annotation of type T may be written as a modifier
288
            // for any declaration except a type parameter declaration.
289
            return elementType != ElementType.TYPE_PARAMETER;
1✔
290
        }
291
        return target.attributeContains("value", elementType).isTrue();
1✔
292
    }
293

294
    // todo isSealed + getPermittedSubclasses
295
    //  (isNonSealed is not so useful I think)
296

297
    /**
298
     * This returns true if this is not an interface, primitive or array.
299
     */
300
    default boolean isClass() {
301
        return !isInterface() && !isArray() && !isPrimitive();
1✔
302
    }
303

304

305
    /**
306
     * Returns the toplevel class containing this class. If this is a
307
     * toplevel class, returns this.
308
     */
309
    default @NonNull JClassSymbol getNestRoot() {
310
        JClassSymbol e = this;
1✔
311
        while (e.getEnclosingClass() != null) {
1✔
312
            e = e.getEnclosingClass();
1✔
313
        }
314
        return e;
1✔
315
    }
316

317

318
    @Override
319
    default <R, P> R acceptVisitor(SymbolVisitor<R, P> visitor, P param) {
320
        return visitor.visitClass(this, param);
1✔
321
    }
322
}
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