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

pmd / pmd / 282

27 Nov 2025 07:43PM UTC coverage: 78.781% (+0.001%) from 78.78%
282

push

github

web-flow
Bump PMD Designer from 7.10.0 to 7.19.1 (#6283)

18428 of 24242 branches covered (76.02%)

Branch coverage included in aggregate %.

40096 of 50045 relevant lines covered (80.12%)

0.81 hits per line

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

76.54
/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/GenericSigBase.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.asm;
6

7

8
import java.util.Arrays;
9
import java.util.Collections;
10
import java.util.List;
11
import java.util.stream.Collectors;
12
import java.util.stream.Stream;
13

14
import org.apache.commons.lang3.Validate;
15
import org.checkerframework.checker.nullness.qual.NonNull;
16
import org.checkerframework.checker.nullness.qual.Nullable;
17
import org.objectweb.asm.MethodVisitor;
18
import org.objectweb.asm.TypePath;
19
import org.objectweb.asm.TypeReference;
20
import org.slf4j.Logger;
21
import org.slf4j.LoggerFactory;
22

23
import net.sourceforge.pmd.lang.java.symbols.JClassSymbol;
24
import net.sourceforge.pmd.lang.java.symbols.JTypeParameterOwnerSymbol;
25
import net.sourceforge.pmd.lang.java.symbols.SymbolicValue.SymAnnot;
26
import net.sourceforge.pmd.lang.java.symbols.internal.asm.ExecutableStub.CtorStub;
27
import net.sourceforge.pmd.lang.java.symbols.internal.asm.TypeAnnotationHelper.TypeAnnotationSet;
28
import net.sourceforge.pmd.lang.java.symbols.internal.asm.TypeAnnotationHelper.TypeAnnotationSetWithReferences;
29
import net.sourceforge.pmd.lang.java.types.JClassType;
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.LexicalScope;
33
import net.sourceforge.pmd.lang.java.types.Substitution;
34
import net.sourceforge.pmd.lang.java.types.TypeOps;
35
import net.sourceforge.pmd.util.AssertionUtil;
36
import net.sourceforge.pmd.util.CollectionUtil;
37

38
abstract class GenericSigBase<T extends JTypeParameterOwnerSymbol & AsmStub> {
39
    private static final Logger LOGGER = LoggerFactory.getLogger(GenericSigBase.class);
1✔
40

41
    /*
42
       Signatures must be parsed lazily, because at the point we see them
43
       in the file, the enclosing class might not yet have been encountered
44
       (and since its type parameters are in scope in the signature we must
45
        wait for it).
46
     */
47

48

49
    protected final T ctx;
50
    protected List<JTypeVar> typeParameters;
51
    private final ParseLock lock;
52

53
    protected GenericSigBase(T ctx, String parseLockName) {
1✔
54
        this.ctx = ctx;
1✔
55
        this.lock = new ParseLock(parseLockName) {
1✔
56
            @Override
57
            protected boolean doParse() {
58
                try {
59
                    GenericSigBase.this.doParse();
1✔
60
                    return true;
1✔
61
                } catch (RuntimeException e) {
×
62
                    throw AssertionUtil.contexted(e)
×
63
                                       .addContextValue("signature", GenericSigBase.this)
×
64
                                       // Here we don't use the toString of the ctx directly because
65
                                       // it could be using the signature indirectly, which would fail
66
                                       .addContextValue("owner class", ctx.getEnclosingClass())
×
67
                                       .addContextValue("owner name", ctx.getSimpleName())
×
68
                                       .addContextValue("owner package", ctx.getPackageName());
×
69
                }
70
            }
71

72

73
            @Override
74
            protected boolean postCondition() {
75
                return GenericSigBase.this.postCondition();
1✔
76
            }
77

78
            @Override
79
            protected void finishParse(boolean failed) {
80
                // Mark the bound as ready to parse, after any type annotations have been collected
81
                typeParameters.forEach(tp -> ((TParamStub) tp.getSymbol()).setCanComputeBound());
1✔
82
            }
1✔
83
        };
84
    }
1✔
85

86
    LexicalScope getEnclosingTypeParams() {
87
        JTypeParameterOwnerSymbol enclosing = ctx.getEnclosingTypeParameterOwner();
1✔
88
        return enclosing == null ? LexicalScope.EMPTY : enclosing.getLexicalScope();
1✔
89
    }
90

91
    protected final void ensureParsed() {
92
        lock.ensureParsed();
1✔
93
    }
1✔
94

95
    protected abstract void doParse();
96

97

98
    protected abstract boolean postCondition();
99

100
    protected abstract int getTypeParameterCount();
101

102
    protected boolean isGeneric() {
103
        return getTypeParameterCount() > 0;
×
104
    }
105

106
    public void setTypeParams(List<JTypeVar> tvars) {
107
        assert this.typeParameters == null : "Type params were already parsed for " + this;
1!
108
        this.typeParameters = tvars;
1✔
109
    }
1✔
110

111
    public List<JTypeVar> getTypeParams() {
112
        ensureParsed();
1✔
113
        return typeParameters;
1✔
114
    }
115

116
    public SignatureParser typeLoader() {
117
        return ctx.sigParser();
1✔
118
    }
119

120

121
    static class LazyClassSignature extends GenericSigBase<ClassStub> {
1✔
122

123
        private static final String OBJECT_INTERNAL_NAME = "java/lang/Object";
124
        private static final String OBJECT_SIG = "L" + OBJECT_INTERNAL_NAME + ";";
125
        private static final String OBJECT_BOUND = ":" + OBJECT_SIG;
126

127
        private final @Nullable String signature;
128
        private final int typeParameterCount;
129

130
        private @Nullable JClassType superType;
131
        private List<JClassType> superItfs;
132

133
        private final List<JClassSymbol> rawItfs;
134
        private final @Nullable JClassSymbol rawSuper;
135

136
        LazyClassSignature(ClassStub ctx,
137
                           @Nullable String signature, // null if doesn't use generics in header
138
                           @Nullable String superInternalName, // null if this is the Object class
139
                           String[] interfaces) {
140
            super(ctx, "LazyClassSignature:" + ctx.getInternalName() + "[" + signature + "]");
1✔
141
            this.signature = signature;
1✔
142
            this.typeParameterCount = GenericTypeParameterCounter.determineTypeParameterCount(this.signature);
1✔
143

144
            this.rawItfs = CollectionUtil.map(interfaces, ctx.getResolver()::resolveFromInternalNameCannotFail);
1✔
145
            this.rawSuper = ctx.getResolver().resolveFromInternalNameCannotFail(superInternalName);
1✔
146
        }
1✔
147

148
        static LazyClassSignature defaultWhenUnresolved(ClassStub ctx, int observedArity) {
149
            String sig = sigWithNTypeParams(observedArity);
1✔
150

151
            return new LazyClassSignature(ctx, sig, OBJECT_INTERNAL_NAME, null);
1✔
152
        }
153

154
        private static @NonNull String sigWithNTypeParams(int observedArity) {
155
            assert observedArity >= 0;
1!
156

157
            // use constants for common values
158
            switch (observedArity) {
1!
159
            case 0: return OBJECT_SIG;
1✔
160
            case 1: return "<T0" + OBJECT_BOUND + ">" + OBJECT_SIG;
×
161
            case 2: return "<T0" + OBJECT_BOUND + "T1" + OBJECT_BOUND + ">" + OBJECT_SIG;
×
162
            default: return Stream.iterate(0, i -> i + 1)
×
163
                                  .limit(observedArity)
×
164
                                  .map(i -> "T" + i + OBJECT_BOUND)
×
165
                                  .collect(Collectors.joining("", "<", ">" + OBJECT_SIG));
×
166
            }
167
        }
168

169
        @Override
170
        protected void doParse() {
171
            if (signature == null) {
1✔
172
                this.superType = rawSuper == null ? null // the Object class
1✔
173
                                                  : (JClassType) ctx.getTypeSystem().rawType(rawSuper);
1✔
174
                this.superItfs = CollectionUtil.map(rawItfs, klass -> (JClassType) ctx.getTypeSystem().rawType(klass));
1✔
175
                setTypeParams(Collections.emptyList());
1✔
176
            } else {
177
                ctx.sigParser().parseClassSignature(this, signature);
1✔
178
            }
179
        }
1✔
180

181
        @Override
182
        protected int getTypeParameterCount() {
183
            // note: no ensureParsed() needed, the type parameters are counted eagerly
184
            return typeParameterCount;
1✔
185
        }
186

187
        @Override
188
        protected boolean postCondition() {
189
            return superItfs != null && (superType != null || signature == null) && typeParameters != null;
1!
190
        }
191

192
        void setSuperInterfaces(List<JClassType> supers) {
193
            Validate.validState(superItfs == null);
1!
194
            superItfs = supers;
1✔
195
        }
1✔
196

197
        void setSuperClass(JClassType sup) {
198
            Validate.validState(this.superType == null);
1!
199
            this.superType = sup;
1✔
200
        }
1✔
201

202
        public JClassType getSuperType(Substitution subst) {
203
            ensureParsed();
1✔
204
            return superType == null ? null : superType.subst(subst);
1!
205
        }
206

207
        public List<JClassType> getSuperItfs(Substitution subst) {
208
            ensureParsed();
1✔
209
            return TypeOps.substClasses(superItfs, subst);
1✔
210
        }
211

212
        public @Nullable JClassSymbol getRawSuper() {
213
            return rawSuper;
1✔
214
        }
215

216
        public List<JClassSymbol> getRawItfs() {
217
            return rawItfs;
1✔
218
        }
219

220
        @Override
221
        public String toString() {
222
            return signature;
×
223
        }
224
    }
225

226
    /**
227
     * Method or constructor type.
228
     */
229
    static class LazyMethodType extends GenericSigBase<ExecutableStub> implements TypeAnnotationReceiver {
1✔
230

231
        private final @NonNull String signature;
232
        private final int typeParameterCount;
233

234
        private @Nullable TypeAnnotationSet receiverAnnotations;
235
        private List<JTypeMirror> parameterTypes;
236
        private List<JTypeMirror> exceptionTypes;
237
        private JTypeMirror returnType;
238
        private @Nullable TypeAnnotationSetWithReferences typeAnnots;
239
        private @Nullable String[] rawExceptions;
240

241
        /** Used for constructors of inner non-static classes. */
242
        private final boolean skipFirstParam;
243

244

245
        // TODO exceptions. Couple of notes:
246
        //  - the descriptor never contains thrown exceptions
247
        //  - the signature might not contain the thrown exception types (if they do not depend on type variables)
248
        //  - the exceptions array also contains unchecked exceptions
249
        //
250
        //  See https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.9.1
251
        // TODO test cases
252
        //  <E extends Exception> void foo()    throws E;          // descriptor "()V"                     signature "<TE;>()V^TE;"   exceptions: ???
253
        //  <E>                   void foo(E e) throws Exception;  // descriptor "(Ljava.lang.Object;)V"   signature "<TE;>(TE;)V"    exceptions: [ "java/lang/Exception" ]
254
        //                        void foo()    throws Exception;  // descriptor "()V"                     signature null             exceptions: [ "java/lang/Exception" ]
255
        LazyMethodType(ExecutableStub ctx,
256
                       @NonNull String descriptor,
257
                       @Nullable String genericSig,
258
                       @Nullable String[] exceptions,
259
                       boolean skipFirstParam) {
260
            super(ctx, "LazyMethodType:" + (genericSig != null ? genericSig : descriptor));
1✔
261
            this.signature = genericSig != null ? genericSig : descriptor;
1✔
262
            this.typeParameterCount = GenericTypeParameterCounter.determineTypeParameterCount(genericSig);
1✔
263
            // generic signatures already omit the synthetic param
264
            this.skipFirstParam = skipFirstParam && genericSig == null;
1✔
265
            this.rawExceptions = exceptions;
1✔
266
        }
1✔
267

268
        @Override
269
        protected void doParse() {
270
            ctx.sigParser().parseMethodType(this, signature);
1✔
271
            if (rawExceptions != null && this.exceptionTypes.isEmpty()) {
1!
272
                // the descriptor did not contain exceptions. They're in this string array.
273
                this.exceptionTypes = Arrays.stream(rawExceptions)
1✔
274
                                            .map(ctx.getResolver()::resolveFromInternalNameCannotFail)
1✔
275
                                            .map(ctx.getTypeSystem()::rawType)
1✔
276
                                            .collect(CollectionUtil.toUnmodifiableList());
1✔
277
            }
278
            if (ctx instanceof CtorStub) {
1✔
279
                // Is a constructor, return type of the descriptor is void.
280
                // We replace the return type with the owner type. This must
281
                // be done before type annotations are applied.
282
                assert this.returnType.isVoid();
1!
283
                this.returnType = ctx.getTypeSystem().declaration(ctx.getEnclosingClass());
1✔
284
            }
285
            if (typeAnnots != null) {
1✔
286
                // apply type annotations here
287
                // this may change type parameters
288
                typeAnnots.forEach(this::acceptAnnotationAfterParse);
1✔
289
            }
290
            // null this transient data out
291
            this.rawExceptions = null;
1✔
292
            this.typeAnnots = null;
1✔
293

294
        }
1✔
295

296
        public JTypeMirror applyReceiverAnnotations(JTypeMirror typeMirror) {
297
            if (receiverAnnotations == null) {
1!
298
                return typeMirror;
×
299
            }
300
            return receiverAnnotations.decorate(typeMirror);
1✔
301
        }
302

303
        @Override
304
        protected boolean postCondition() {
305
            return parameterTypes != null && exceptionTypes != null && returnType != null;
1!
306
        }
307

308

309
        @Override
310
        protected int getTypeParameterCount() {
311
            // note: no ensureParsed() needed, the type parameters are counted eagerly
312
            return typeParameterCount;
×
313
        }
314

315
        void setParameterTypes(List<JTypeMirror> params) {
316
            Validate.validState(parameterTypes == null);
1!
317
            parameterTypes = skipFirstParam ? params.subList(1, params.size())
1✔
318
                                            : params;
1✔
319
        }
1✔
320

321
        void setExceptionTypes(List<JTypeMirror> exs) {
322
            Validate.validState(exceptionTypes == null);
1!
323
            exceptionTypes = exs;
1✔
324
        }
1✔
325

326
        void setReturnType(JTypeMirror returnType) {
327
            Validate.validState(this.returnType == null);
1!
328
            this.returnType = returnType;
1✔
329
        }
1✔
330

331
        public List<JTypeMirror> getParameterTypes() {
332
            ensureParsed();
1✔
333
            return parameterTypes;
1✔
334
        }
335

336
        public List<JTypeMirror> getExceptionTypes() {
337
            ensureParsed();
1✔
338
            return exceptionTypes;
1✔
339
        }
340

341
        public JTypeMirror getReturnType() {
342
            ensureParsed();
1✔
343
            return returnType;
1✔
344
        }
345

346
        @Override
347
        public String toString() {
348
            return signature;
×
349
        }
350

351

352
        @Override
353
        public void acceptTypeAnnotation(int typeRefInt, @Nullable TypePath path, SymAnnot annot) {
354
            // Accumulate type annotations for later
355
            // They shouldn't be applied right now because the descriptor maybe has not been parsed yet.
356
            if (typeAnnots == null) {
1✔
357
                typeAnnots = new TypeAnnotationSetWithReferences();
1✔
358
            }
359
            typeAnnots.add(new TypeReference(typeRefInt), path, annot);
1✔
360
        }
1✔
361

362
        /**
363
         * See {@link MethodVisitor#visitTypeAnnotation(int, TypePath, String, boolean)} for possible
364
         * values of typeRef sort (they're each case of the switch).
365
         * Returns true if type parameters have been mutated.
366
         */
367
        boolean acceptAnnotationAfterParse(TypeReference tyRef, @Nullable TypePath path, SymAnnot annot) {
368
            switch (tyRef.getSort()) {
1!
369
            case TypeReference.METHOD_RETURN: {
370
                assert returnType != null : "Return type is not set";
1!
371
                returnType = TypeAnnotationHelper.applySinglePath(returnType, path, annot);
1✔
372
                return false;
1✔
373
            }
374
            case TypeReference.METHOD_FORMAL_PARAMETER: {
375
                assert parameterTypes != null : "Parameter types are not set";
1!
376
                int idx = tyRef.getFormalParameterIndex();
1✔
377
                JTypeMirror annotatedFormal = TypeAnnotationHelper.applySinglePath(parameterTypes.get(idx), path, annot);
1✔
378
                parameterTypes = TypeAnnotationHelper.replaceAtIndex(parameterTypes, idx, annotatedFormal);
1✔
379
                return false;
1✔
380
            }
381
            case TypeReference.THROWS: {
382
                assert exceptionTypes != null : "Exception types are not set";
1!
383
                int idx = tyRef.getExceptionIndex();
1✔
384
                JTypeMirror annotatedFormal = TypeAnnotationHelper.applySinglePath(exceptionTypes.get(idx), path, annot);
1✔
385
                exceptionTypes = TypeAnnotationHelper.replaceAtIndex(exceptionTypes, idx, annotatedFormal);
1✔
386
                return false;
1✔
387
            }
388
            case TypeReference.METHOD_TYPE_PARAMETER: {
389
                assert typeParameters != null;
1!
390
                assert path == null : "unexpected path " + path;
1!
391
                int idx = tyRef.getTypeParameterIndex();
1✔
392
                // Here we add to the symbol, not the type var.
393
                // This ensures that all occurrences of the type var
394
                // share these annotations (symbol is unique, contrary to jtypevar)
395
                ((TParamStub) typeParameters.get(idx).getSymbol()).addAnnotation(annot);
1✔
396
                return false;
1✔
397
            }
398
            case TypeReference.METHOD_TYPE_PARAMETER_BOUND: {
399
                assert typeParameters != null;
1!
400
                int tparamIdx = tyRef.getTypeParameterIndex();
1✔
401

402
                JTypeVar tparam = typeParameters.get(tparamIdx);
1✔
403
                TParamStub sym = (TParamStub) tparam.getSymbol();
1✔
404
                assert sym != null;
1!
405
                sym.addAnnotationOnBound(tyRef, path, annot);
1✔
406
                return false;
1✔
407
            }
408
            case TypeReference.METHOD_RECEIVER: {
409
                if (receiverAnnotations == null) {
1✔
410
                    receiverAnnotations = new TypeAnnotationSet();
1✔
411
                }
412
                receiverAnnotations.add(path, annot);
1✔
413
                return false;
1✔
414
            }
415
            case TypeReference.CLASS_EXTENDS: {
416
                // avoid exception for Java 11 bug
417
                // see https://github.com/pmd/pmd/issues/5344 and https://bugs.openjdk.org/browse/JDK-8198945
418
                LOGGER.debug("Invalid target type CLASS_EXTENDS of type annotation {} for method or ctor detected. "
×
419
                        + "The annotation is ignored. Method: {}#{}. See https://github.com/pmd/pmd/issues/5344.",
420
                        annot, ctx.getEnclosingClass().getCanonicalName(), ctx.getSimpleName());
×
421
                return false;
×
422
            }
423
            case TypeReference.FIELD: {
424
                // avoid exception for Java 16+ bug
425
                // see https://github.com/pmd/pmd/issues/6256 and https://bugs.openjdk.org/browse/JDK-8372382
426
                LOGGER.debug("Invalid target type FIELD of type annotation {} for method or ctor detected. "
×
427
                        + "The annotation is ignored. Method: {}#{}. See https://github.com/pmd/pmd/issues/6256.",
428
                        annot, ctx.getEnclosingClass().getCanonicalName(), ctx.getSimpleName());
×
429
                return false;
×
430
            }
431
            default:
432
                throw new IllegalArgumentException(
×
433
                    "Invalid target type of type annotation " + annot + " for method or ctor type annotation: "
434
                            + tyRef.getSort());
×
435
            }
436
        }
437

438
    }
439
}
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