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

pmd / pmd / 415

27 Feb 2026 12:39PM UTC coverage: 79.038% (+0.03%) from 79.004%
415

push

github

adangel
[release] Prepare next development version

18604 of 24437 branches covered (76.13%)

Branch coverage included in aggregate %.

40598 of 50466 relevant lines covered (80.45%)

0.81 hits per line

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

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

5
package net.sourceforge.pmd.lang.java.symbols.internal;
6

7
import static java.util.Collections.emptyList;
8
import static java.util.Collections.singletonList;
9

10
import java.lang.reflect.Modifier;
11
import java.util.List;
12
import java.util.function.BiFunction;
13
import java.util.function.Function;
14

15
import org.checkerframework.checker.nullness.qual.NonNull;
16
import org.checkerframework.checker.nullness.qual.Nullable;
17

18
import net.sourceforge.pmd.lang.java.ast.ASTPrimitiveType;
19
import net.sourceforge.pmd.lang.java.ast.ASTType;
20
import net.sourceforge.pmd.lang.java.ast.ASTVariableId;
21
import net.sourceforge.pmd.lang.java.internal.JavaAstProcessor;
22
import net.sourceforge.pmd.lang.java.symbols.JClassSymbol;
23
import net.sourceforge.pmd.lang.java.symbols.JConstructorSymbol;
24
import net.sourceforge.pmd.lang.java.symbols.JExecutableSymbol;
25
import net.sourceforge.pmd.lang.java.symbols.JFieldSymbol;
26
import net.sourceforge.pmd.lang.java.symbols.JFormalParamSymbol;
27
import net.sourceforge.pmd.lang.java.symbols.JMethodSymbol;
28
import net.sourceforge.pmd.lang.java.symbols.JRecordComponentSymbol;
29
import net.sourceforge.pmd.lang.java.types.JPrimitiveType.PrimitiveTypeKind;
30
import net.sourceforge.pmd.lang.java.types.JTypeMirror;
31
import net.sourceforge.pmd.lang.java.types.JTypeVar;
32
import net.sourceforge.pmd.lang.java.types.Substitution;
33
import net.sourceforge.pmd.lang.java.types.TypeSystem;
34
import net.sourceforge.pmd.util.CollectionUtil;
35

36
/**
37
 * Members inserted by the compiler, eg default constructor, etc. They
38
 * would be absent from the source, but are reflected by the {@link Class}.
39
 */
40
public final class ImplicitMemberSymbols {
1✔
41

42
    private static final int VISIBILITY_MASK = Modifier.PRIVATE | Modifier.PUBLIC | Modifier.PROTECTED;
43
    /** This is the private access flag for varargs modifiers. */
44
    private static final int VARARGS_MOD = 0x00000080;
45

46
    private ImplicitMemberSymbols() {
47

48
    }
49

50
    public static JMethodSymbol enumValueOf(JClassSymbol enumSym) {
51
        assert enumSym.isEnum() : "Not an enum symbol " + enumSym;
1!
52

53
        return new FakeMethodSym(
1✔
54
            enumSym,
55
            "valueOf",
56
            Modifier.PUBLIC | Modifier.STATIC,
57
            TypeSystem::declaration,
58
            singletonList(t -> new FakeFormalParamSym(t, "name", (ts, s) -> ts.declaration(ts.getClassSymbol(String.class))))
1✔
59
        );
60
    }
61

62
    public static JMethodSymbol enumValues(JClassSymbol enumSym) {
63
        assert enumSym.isEnum() : "Not an enum symbol " + enumSym;
1!
64

65
        return new FakeMethodSym(
1✔
66
            enumSym,
67
            "values",
68
            Modifier.PUBLIC | Modifier.STATIC,
69
            (ts, c) -> ts.arrayType(ts.declaration(c)),
1✔
70
            emptyList()
1✔
71
        );
72
    }
73

74

75
    public static JConstructorSymbol defaultCtor(JClassSymbol sym) {
76
        assert sym != null;
1!
77

78
        // Enum constructors have 2 additional implicit parameters, for the name and ordinal
79
        // Inner classes have 1 additional implicit param, for the outer instance
80
        // They are not reflected by the symbol
81

82
        int modifiers = sym.isEnum() ? Modifier.PRIVATE
1✔
83
                                     : sym.getModifiers() & VISIBILITY_MASK;
1✔
84

85
        return new FakeCtorSym(sym, modifiers, emptyList());
1✔
86
    }
87

88

89
    public static JMethodSymbol arrayClone(JClassSymbol arraySym) {
90
        assert arraySym.isArray() : "Not an array symbol " + arraySym;
1!
91

92
        return new FakeMethodSym(
1✔
93
            arraySym,
94
            "clone",
95
            Modifier.PUBLIC | Modifier.FINAL,
96
            TypeSystem::declaration,
97
            emptyList()
1✔
98
        );
99
    }
100

101
    public static JConstructorSymbol arrayConstructor(JClassSymbol arraySym) {
102
        assert arraySym.isArray() : "Not an array symbol " + arraySym;
1!
103

104
        return new FakeCtorSym(
1✔
105
            arraySym,
106
            Modifier.PUBLIC | Modifier.FINAL,
107
            singletonList(c -> new FakeFormalParamSym(c, "arg0", (ts, sym) -> ts.INT))
1✔
108
        );
109
    }
110

111
    /** Symbol for the canonical record constructor. */
112
    public static JConstructorSymbol recordConstructor(JClassSymbol recordSym,
113
                                                       List<JRecordComponentSymbol> recordComponents,
114
                                                       boolean isVarargs) {
115
        assert recordSym.isRecord() : "Not a record symbol " + recordSym;
1!
116

117
        int modifiers = isVarargs ? Modifier.PUBLIC | VARARGS_MOD
1✔
118
                                  : Modifier.PUBLIC;
1✔
119

120
        return new FakeCtorSym(
1✔
121
            recordSym,
122
            modifiers,
123
            CollectionUtil.map(
1✔
124
                recordComponents,
125
                f -> c -> new FakeFormalParamSym(c, f.getSimpleName(), f.tryGetNode().getVarId(), (ts, sym) -> f.getTypeMirror(Substitution.EMPTY))
1✔
126
            )
127
        );
128
    }
129

130
    /**
131
     * Symbol for a record component accessor.
132
     * Only synthesized if it is not explicitly declared.
133
     */
134
    public static JMethodSymbol recordAccessor(JClassSymbol recordSym, JRecordComponentSymbol recordComponent) {
135
        // See https://cr.openjdk.java.net/~gbierman/jep359/jep359-20200115/specs/records-jls.html#jls-8.10.3
136

137
        assert recordSym.isRecord() : "Not a record symbol " + recordSym;
1!
138

139
        return new FakeMethodSym(
1✔
140
            recordSym,
141
            recordComponent.getSimpleName(),
1✔
142
            Modifier.PUBLIC,
143
            (ts, encl) -> recordComponent.getTypeMirror(Substitution.EMPTY),
1✔
144
            emptyList()
1✔
145
        );
146
    }
147

148
    public static JFieldSymbol arrayLengthField(JClassSymbol arraySym) {
149
        assert arraySym.isArray() : "Not an array symbol " + arraySym;
1!
150

151
        return new FakeFieldSym(
1✔
152
            arraySym,
153
            "length",
154
            Modifier.PUBLIC | Modifier.FINAL,
155
            (ts, s) -> ts.INT
1✔
156
        );
157
    }
158

159
    public static JFieldSymbol lombokSlf4jLoggerField(JClassSymbol classSym, JavaAstProcessor processor) {
160
        // https://javadoc.io/doc/org.projectlombok/lombok/1.16.18/lombok/extern/slf4j/Slf4j.html
161

162
        return new FakeFieldSym(
1✔
163
                classSym,
164
                "log",
165
                Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL,
166
                (ts, s) ->
167
                    ts.declaration(processor.findSymbolCannotFail("org.slf4j.Logger"))
1✔
168
        );
169
    }
170

171
    public static JMethodSymbol lombokGetter(JClassSymbol classSym, JFieldSymbol field, int accessModifier) {
172
        String fieldName = field.getSimpleName();
1✔
173
        String prefix = "get";
1✔
174
        ASTVariableId varId = field.tryGetNode();
1✔
175
        if (varId != null) {
1!
176
            ASTType typeNode = varId.getTypeNode();
1✔
177
            if (typeNode instanceof ASTPrimitiveType
1✔
178
                    && ((ASTPrimitiveType) typeNode).getKind() == PrimitiveTypeKind.BOOLEAN) {
1✔
179
                prefix = "is";
1✔
180
            }
181
        }
182
        String name = prefix + Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1);
1✔
183

184
        return new FakeMethodSym(
1✔
185
                classSym,
186
                name,
187
                accessModifier,
188
                (ts, s) -> field.getTypeMirror(Substitution.EMPTY),
1✔
189
                emptyList()
1✔
190
        );
191
    }
192

193
    private abstract static class FakeExecutableSymBase<T extends JExecutableSymbol> implements JExecutableSymbol {
194

195
        private final JClassSymbol owner;
196
        private final String name;
197
        private final int modifiers;
198
        private final List<JFormalParamSymbol> formals;
199

200

201
        FakeExecutableSymBase(JClassSymbol owner,
202
                              String name,
203
                              int modifiers,
204
                              List<Function<T, JFormalParamSymbol>> formals) {
1✔
205
            this.owner = owner;
1✔
206
            this.name = name;
1✔
207
            this.modifiers = modifiers;
1✔
208
            this.formals = CollectionUtil.map(formals, f -> f.apply((T) this));
1✔
209
        }
1✔
210

211
        @Override
212
        public TypeSystem getTypeSystem() {
213
            return owner.getTypeSystem();
1✔
214
        }
215

216
        @Override
217
        public List<JTypeMirror> getFormalParameterTypes(Substitution subst) {
218
            return CollectionUtil.map(formals, p -> p.getTypeMirror(subst));
1✔
219
        }
220

221
        @Override
222
        public List<JTypeMirror> getThrownExceptionTypes(Substitution subst) {
223
            return emptyList();
1✔
224
        }
225

226
        @Override
227
        public List<JTypeVar> getTypeParameters() {
228
            return emptyList();
1✔
229
        }
230

231
        @Override
232
        public String getSimpleName() {
233
            return name;
1✔
234
        }
235

236
        @Override
237
        public List<JFormalParamSymbol> getFormalParameters() {
238
            return formals;
1✔
239
        }
240

241
        @Override
242
        public boolean isVarargs() {
243
            return (modifiers & VARARGS_MOD) != 0;
1✔
244
        }
245

246
        @Override
247
        public int getArity() {
248
            return formals.size();
1✔
249
        }
250

251
        @Override
252
        public @Nullable JTypeMirror getAnnotatedReceiverType(Substitution subst) {
253
            if (!this.hasReceiver()) {
×
254
                return null;
×
255
            }
256
            return getTypeSystem().declaration(owner).subst(subst);
×
257
        }
258

259
        @Override
260
        public int getModifiers() {
261
            return modifiers;
1✔
262
        }
263

264
        @Override
265
        public @NonNull JClassSymbol getEnclosingClass() {
266
            return owner;
1✔
267
        }
268

269
        @Override
270
        public String toString() {
271
            return SymbolToStrings.FAKE.toString(this);
×
272
        }
273
    }
274

275
    private static final class FakeMethodSym extends FakeExecutableSymBase<JMethodSymbol> implements JMethodSymbol {
276

277
        private final BiFunction<? super TypeSystem, ? super JClassSymbol, ? extends JTypeMirror> returnType;
278

279
        FakeMethodSym(JClassSymbol owner,
280
                      String name,
281
                      int modifiers,
282
                      BiFunction<? super TypeSystem, ? super JClassSymbol, ? extends JTypeMirror> returnType,
283
                      List<Function<JMethodSymbol, JFormalParamSymbol>> formals) {
284
            super(owner, name, modifiers, formals);
1✔
285
            this.returnType = returnType;
1✔
286
        }
1✔
287

288
        @Override
289
        public boolean isBridge() {
290
            return false;
×
291
        }
292

293
        @Override
294
        public JTypeMirror getReturnType(Substitution subst) {
295
            return returnType.apply(getTypeSystem(), getEnclosingClass());
1✔
296
        }
297

298
        @Override
299
        public boolean equals(Object o) {
300
            return SymbolEquality.METHOD.equals(this, o);
1✔
301
        }
302

303
        @Override
304
        public int hashCode() {
305
            return SymbolEquality.METHOD.hash(this);
1✔
306
        }
307
    }
308

309
    private static final class FakeCtorSym extends FakeExecutableSymBase<JConstructorSymbol> implements JConstructorSymbol {
310

311
        FakeCtorSym(JClassSymbol owner,
312
                    int modifiers,
313
                    List<Function<JConstructorSymbol, JFormalParamSymbol>> formals) {
314
            super(owner, JConstructorSymbol.CTOR_NAME, modifiers, formals);
1✔
315
        }
1✔
316

317

318
        @Override
319
        public boolean equals(Object o) {
320
            return SymbolEquality.CONSTRUCTOR.equals(this, o);
1✔
321
        }
322

323
        @Override
324
        public int hashCode() {
325
            return SymbolEquality.CONSTRUCTOR.hash(this);
1✔
326
        }
327
    }
328

329
    private static final class FakeFormalParamSym implements JFormalParamSymbol {
330

331
        private final JExecutableSymbol owner;
332
        private final String name;
333
        private final ASTVariableId node;
334
        private final BiFunction<? super TypeSystem, ? super JFormalParamSymbol, ? extends JTypeMirror> type;
335

336
        private FakeFormalParamSym(JExecutableSymbol owner, String name, BiFunction<? super TypeSystem, ? super JFormalParamSymbol, ? extends JTypeMirror> type) {
337
            this(owner, name, null, type);
1✔
338
        }
1✔
339

340
        private FakeFormalParamSym(JExecutableSymbol owner, String name, @Nullable ASTVariableId node, BiFunction<? super TypeSystem, ? super JFormalParamSymbol, ? extends JTypeMirror> type) {
1✔
341
            this.owner = owner;
1✔
342
            this.name = name;
1✔
343
            this.node = node;
1✔
344
            this.type = type;
1✔
345
        }
1✔
346

347
        @Override
348
        public @Nullable ASTVariableId tryGetNode() {
349
            return node;
1✔
350
        }
351

352
        @Override
353
        public TypeSystem getTypeSystem() {
354
            return owner.getTypeSystem();
1✔
355
        }
356

357
        @Override
358
        public JTypeMirror getTypeMirror(Substitution subst) {
359
            return type.apply(getTypeSystem(), this).subst(subst);
1✔
360
        }
361

362
        @Override
363
        public JExecutableSymbol getDeclaringSymbol() {
364
            return owner;
1✔
365
        }
366

367
        @Override
368
        public boolean isFinal() {
369
            return false;
1✔
370
        }
371

372
        @Override
373
        public String getSimpleName() {
374
            return name;
1✔
375
        }
376

377
        @Override
378
        public String toString() {
379
            return SymbolToStrings.FAKE.toString(this);
×
380
        }
381

382
        @Override
383
        public boolean equals(Object o) {
384
            return SymbolEquality.FORMAL_PARAM.equals(this, o);
×
385
        }
386

387
        @Override
388
        public int hashCode() {
389
            return SymbolEquality.FORMAL_PARAM.hash(this);
1✔
390
        }
391
    }
392

393

394
    private static final class FakeFieldSym implements JFieldSymbol {
395

396
        private final JClassSymbol owner;
397
        private final String name;
398
        private final int modifiers;
399
        private final BiFunction<? super TypeSystem, ? super JClassSymbol, ? extends JTypeMirror> type;
400

401
        FakeFieldSym(JClassSymbol owner, String name, int modifiers, BiFunction<? super TypeSystem, ? super JClassSymbol, ? extends JTypeMirror> type) {
1✔
402
            this.owner = owner;
1✔
403
            this.name = name;
1✔
404
            this.modifiers = modifiers;
1✔
405
            this.type = type;
1✔
406
        }
1✔
407

408
        @Override
409
        public TypeSystem getTypeSystem() {
410
            return owner.getTypeSystem();
1✔
411
        }
412

413
        @Override
414
        public JTypeMirror getTypeMirror(Substitution subst) {
415
            return type.apply(getTypeSystem(), owner).subst(subst);
1✔
416
        }
417

418
        @Override
419
        public String getSimpleName() {
420
            return name;
1✔
421
        }
422

423
        @Override
424
        public int getModifiers() {
425
            return modifiers;
1✔
426
        }
427

428
        @Override
429
        public boolean isEnumConstant() {
430
            return false;
×
431
        }
432

433
        @Override
434
        public @NonNull JClassSymbol getEnclosingClass() {
435
            return owner;
1✔
436
        }
437

438
        @Override
439
        public String toString() {
440
            return SymbolToStrings.FAKE.toString(this);
1✔
441
        }
442

443
        @Override
444
        public boolean equals(Object o) {
445
            return SymbolEquality.FIELD.equals(this, o);
1✔
446
        }
447

448
        @Override
449
        public int hashCode() {
450
            return SymbolEquality.FIELD.hash(this);
1✔
451
        }
452
    }
453

454
}
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