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

pmd / pmd / 4509

21 Mar 2025 11:23AM UTC coverage: 77.757% (-0.004%) from 77.761%
4509

push

github

adangel
[doc] Use full class name for deprecation in release notes

17505 of 23464 branches covered (74.6%)

Branch coverage included in aggregate %.

38318 of 48328 relevant lines covered (79.29%)

0.8 hits per line

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

78.95
/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

21
import net.sourceforge.pmd.lang.java.symbols.JClassSymbol;
22
import net.sourceforge.pmd.lang.java.symbols.JTypeParameterOwnerSymbol;
23
import net.sourceforge.pmd.lang.java.symbols.SymbolicValue.SymAnnot;
24
import net.sourceforge.pmd.lang.java.symbols.internal.asm.TypeAnnotationHelper.TypeAnnotationSet;
25
import net.sourceforge.pmd.lang.java.symbols.internal.asm.TypeAnnotationHelper.TypeAnnotationSetWithReferences;
26
import net.sourceforge.pmd.lang.java.types.JClassType;
27
import net.sourceforge.pmd.lang.java.types.JTypeMirror;
28
import net.sourceforge.pmd.lang.java.types.JTypeVar;
29
import net.sourceforge.pmd.lang.java.types.LexicalScope;
30
import net.sourceforge.pmd.lang.java.types.Substitution;
31
import net.sourceforge.pmd.lang.java.types.TypeOps;
32
import net.sourceforge.pmd.util.AssertionUtil;
33
import net.sourceforge.pmd.util.CollectionUtil;
34

35
abstract class GenericSigBase<T extends JTypeParameterOwnerSymbol & AsmStub> {
1✔
36
    /*
37
       Signatures must be parsed lazily, because at the point we see them
38
       in the file, the enclosing class might not yet have been encountered
39
       (and since its type parameters are in scope in the signature we must
40
        wait for it).
41
     */
42

43

44
    protected final T ctx;
45
    protected List<JTypeVar> typeParameters;
46
    private final ParseLock lock;
47

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

67

68
            @Override
69
            protected boolean postCondition() {
70
                return GenericSigBase.this.postCondition();
1✔
71
            }
72

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

81
    LexicalScope getEnclosingTypeParams() {
82
        JTypeParameterOwnerSymbol enclosing = ctx.getEnclosingTypeParameterOwner();
1✔
83
        return enclosing == null ? LexicalScope.EMPTY : enclosing.getLexicalScope();
1✔
84
    }
85

86
    protected final void ensureParsed() {
87
        lock.ensureParsed();
1✔
88
    }
1✔
89

90
    protected abstract void doParse();
91

92

93
    protected abstract boolean postCondition();
94

95
    protected abstract int getTypeParameterCount();
96

97
    protected boolean isGeneric() {
98
        return getTypeParameterCount() > 0;
×
99
    }
100

101
    public void setTypeParams(List<JTypeVar> tvars) {
102
        assert this.typeParameters == null : "Type params were already parsed for " + this;
1!
103
        this.typeParameters = tvars;
1✔
104
    }
1✔
105

106
    public List<JTypeVar> getTypeParams() {
107
        ensureParsed();
1✔
108
        return typeParameters;
1✔
109
    }
110

111
    public SignatureParser typeLoader() {
112
        return ctx.sigParser();
1✔
113
    }
114

115

116
    static class LazyClassSignature extends GenericSigBase<ClassStub> {
1✔
117

118
        private static final String OBJECT_INTERNAL_NAME = "java/lang/Object";
119
        private static final String OBJECT_SIG = "L" + OBJECT_INTERNAL_NAME + ";";
120
        private static final String OBJECT_BOUND = ":" + OBJECT_SIG;
121

122
        private final @Nullable String signature;
123
        private final int typeParameterCount;
124

125
        private @Nullable JClassType superType;
126
        private List<JClassType> superItfs;
127

128
        private final List<JClassSymbol> rawItfs;
129
        private final @Nullable JClassSymbol rawSuper;
130

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

139
            this.rawItfs = CollectionUtil.map(interfaces, ctx.getResolver()::resolveFromInternalNameCannotFail);
1✔
140
            this.rawSuper = ctx.getResolver().resolveFromInternalNameCannotFail(superInternalName);
1✔
141
        }
1✔
142

143
        static LazyClassSignature defaultWhenUnresolved(ClassStub ctx, int observedArity) {
144
            String sig = sigWithNTypeParams(observedArity);
1✔
145

146
            return new LazyClassSignature(ctx, sig, OBJECT_INTERNAL_NAME, null);
1✔
147
        }
148

149
        private static @NonNull String sigWithNTypeParams(int observedArity) {
150
            assert observedArity >= 0;
1!
151

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

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

176
        @Override
177
        protected int getTypeParameterCount() {
178
            // note: no ensureParsed() needed, the type parameters are counted eagerly
179
            return typeParameterCount;
1✔
180
        }
181

182
        @Override
183
        protected boolean postCondition() {
184
            return superItfs != null && (superType != null || signature == null) && typeParameters != null;
1!
185
        }
186

187
        void setSuperInterfaces(List<JClassType> supers) {
188
            Validate.validState(superItfs == null);
1!
189
            superItfs = supers;
1✔
190
        }
1✔
191

192
        void setSuperClass(JClassType sup) {
193
            Validate.validState(this.superType == null);
1!
194
            this.superType = sup;
1✔
195
        }
1✔
196

197
        public JClassType getSuperType(Substitution subst) {
198
            ensureParsed();
1✔
199
            return superType == null ? null : superType.subst(subst);
1!
200
        }
201

202
        public List<JClassType> getSuperItfs(Substitution subst) {
203
            ensureParsed();
1✔
204
            return TypeOps.substClasses(superItfs, subst);
1✔
205
        }
206

207
        public @Nullable JClassSymbol getRawSuper() {
208
            return rawSuper;
1✔
209
        }
210

211
        public List<JClassSymbol> getRawItfs() {
212
            return rawItfs;
1✔
213
        }
214

215
        @Override
216
        public String toString() {
217
            return signature;
×
218
        }
219
    }
220

221
    /**
222
     * Method or constructor type.
223
     */
224
    static class LazyMethodType extends GenericSigBase<ExecutableStub> implements TypeAnnotationReceiver {
1✔
225

226
        private final @NonNull String signature;
227
        private final int typeParameterCount;
228

229
        private @Nullable TypeAnnotationSet receiverAnnotations;
230
        private List<JTypeMirror> parameterTypes;
231
        private List<JTypeMirror> exceptionTypes;
232
        private JTypeMirror returnType;
233
        private @Nullable TypeAnnotationSetWithReferences typeAnnots;
234
        private @Nullable String[] rawExceptions;
235

236
        /** Used for constructors of inner non-static classes. */
237
        private final boolean skipFirstParam;
238

239

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

263
        @Override
264
        protected void doParse() {
265
            ctx.sigParser().parseMethodType(this, signature);
1✔
266
            if (rawExceptions != null && this.exceptionTypes.isEmpty()) {
1!
267
                // the descriptor did not contain exceptions. They're in this string array.
268
                this.exceptionTypes = Arrays.stream(rawExceptions)
1✔
269
                                            .map(ctx.getResolver()::resolveFromInternalNameCannotFail)
1✔
270
                                            .map(ctx.getTypeSystem()::rawType)
1✔
271
                                            .collect(CollectionUtil.toUnmodifiableList());
1✔
272
            }
273
            if (typeAnnots != null) {
1✔
274
                // apply type annotations here
275
                // this may change type parameters
276
                typeAnnots.forEach(this::acceptAnnotationAfterParse);
1✔
277
            }
278
            // null this transient data out
279
            this.rawExceptions = null;
1✔
280
            this.typeAnnots = null;
1✔
281

282
        }
1✔
283

284
        public JTypeMirror applyReceiverAnnotations(JTypeMirror typeMirror) {
285
            if (receiverAnnotations == null) {
1!
286
                return typeMirror;
×
287
            }
288
            return receiverAnnotations.decorate(typeMirror);
1✔
289
        }
290

291
        @Override
292
        protected boolean postCondition() {
293
            return parameterTypes != null && exceptionTypes != null && returnType != null;
1!
294
        }
295

296

297
        @Override
298
        protected int getTypeParameterCount() {
299
            // note: no ensureParsed() needed, the type parameters are counted eagerly
300
            return typeParameterCount;
×
301
        }
302

303
        void setParameterTypes(List<JTypeMirror> params) {
304
            Validate.validState(parameterTypes == null);
1!
305
            parameterTypes = skipFirstParam ? params.subList(1, params.size())
1✔
306
                                            : params;
1✔
307
        }
1✔
308

309
        void setExceptionTypes(List<JTypeMirror> exs) {
310
            Validate.validState(exceptionTypes == null);
1!
311
            exceptionTypes = exs;
1✔
312
        }
1✔
313

314
        void setReturnType(JTypeMirror returnType) {
315
            Validate.validState(this.returnType == null);
1!
316
            this.returnType = returnType;
1✔
317
        }
1✔
318

319
        public List<JTypeMirror> getParameterTypes() {
320
            ensureParsed();
1✔
321
            return parameterTypes;
1✔
322
        }
323

324
        public List<JTypeMirror> getExceptionTypes() {
325
            ensureParsed();
1✔
326
            return exceptionTypes;
1✔
327
        }
328

329
        public JTypeMirror getReturnType() {
330
            ensureParsed();
1✔
331
            return returnType;
1✔
332
        }
333

334
        @Override
335
        public String toString() {
336
            return signature;
×
337
        }
338

339

340
        @Override
341
        public void acceptTypeAnnotation(int typeRefInt, @Nullable TypePath path, SymAnnot annot) {
342
            // Accumulate type annotations for later
343
            // They shouldn't be applied right now because the descriptor maybe has not been parsed yet.
344
            if (typeAnnots == null) {
1✔
345
                typeAnnots = new TypeAnnotationSetWithReferences();
1✔
346
            }
347
            typeAnnots.add(new TypeReference(typeRefInt), path, annot);
1✔
348
        }
1✔
349

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

390
                JTypeVar tparam = typeParameters.get(tparamIdx);
1✔
391
                TParamStub sym = (TParamStub) tparam.getSymbol();
1✔
392
                assert sym != null;
1!
393
                sym.addAnnotationOnBound(tyRef, path, annot);
1✔
394
                return false;
1✔
395
            }
396
            case TypeReference.METHOD_RECEIVER: {
397
                if (receiverAnnotations == null) {
1✔
398
                    receiverAnnotations = new TypeAnnotationSet();
1✔
399
                }
400
                receiverAnnotations.add(path, annot);
1✔
401
                return false;
1✔
402
            }
403
            default:
404
                throw new IllegalArgumentException(
×
405
                    "Invalid type reference for method or ctor type annotation: " + tyRef.getSort());
×
406
            }
407
        }
408

409
    }
410
}
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