• 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

92.0
/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.ArrayList;
9
import java.util.Arrays;
10
import java.util.Collections;
11
import java.util.List;
12
import java.util.stream.Collectors;
13
import java.util.stream.Stream;
14

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

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

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

44

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

49
    protected GenericSigBase(T ctx) {
1✔
50
        this.ctx = ctx;
1✔
51
        this.lock = new ParseLock() {
1✔
52
            @Override
53
            protected boolean doParse() {
54
                GenericSigBase.this.doParse();
1✔
55
                return true;
1✔
56
            }
57

58
            @Override
59
            protected boolean postCondition() {
60
                return GenericSigBase.this.postCondition();
1✔
61
            }
62

63
            @Override
64
            protected boolean canReenter() {
65
                return typeParameters != null;
1✔
66
            }
67
        };
68
    }
1✔
69

70
    LexicalScope getEnclosingTypeParams() {
71
        JTypeParameterOwnerSymbol enclosing = ctx.getEnclosingTypeParameterOwner();
1✔
72
        return enclosing == null ? LexicalScope.EMPTY : enclosing.getLexicalScope();
1✔
73
    }
74

75
    protected final void ensureParsed() {
76
        lock.ensureParsed();
1✔
77
    }
1✔
78

79
    protected abstract void doParse();
80

81

82
    protected abstract boolean postCondition();
83

84
    protected abstract boolean isGeneric();
85

86
    public void setTypeParams(List<JTypeVar> tvars) {
87
        assert this.typeParameters == null : "Type params were already parsed for " + this;
1✔
88
        this.typeParameters = tvars;
1✔
89
    }
1✔
90

91
    public List<JTypeVar> getTypeParams() {
92
        ensureParsed();
1✔
93
        return typeParameters;
1✔
94
    }
95

96
    public SignatureParser typeLoader() {
97
        return ctx.sigParser();
1✔
98
    }
99

100

101
    static class LazyClassSignature extends GenericSigBase<ClassStub> {
1✔
102

103
        private static final String OBJECT_INTERNAL_NAME = "java/lang/Object";
104
        private static final String OBJECT_SIG = "L" + OBJECT_INTERNAL_NAME + ";";
105
        private static final String OBJECT_BOUND = ":" + OBJECT_SIG;
106

107
        private final @Nullable String signature;
108

109
        private @Nullable JClassType superType;
110
        private List<JClassType> superItfs;
111

112
        private final List<JClassSymbol> rawItfs;
113
        private final @Nullable JClassSymbol rawSuper;
114

115
        LazyClassSignature(ClassStub ctx,
116
                           @Nullable String signature, // null if doesn't use generics in header
117
                           @Nullable String superInternalName, // null if this is the Object class
118
                           String[] interfaces) {
119
            super(ctx);
1✔
120
            this.signature = signature;
1✔
121

122
            this.rawItfs = CollectionUtil.map(interfaces, ctx.getResolver()::resolveFromInternalNameCannotFail);
1✔
123
            this.rawSuper = ctx.getResolver().resolveFromInternalNameCannotFail(superInternalName);
1✔
124
        }
1✔
125

126
        static LazyClassSignature defaultWhenUnresolved(ClassStub ctx, int observedArity) {
127
            String sig = sigWithNTypeParams(observedArity);
1✔
128

129
            return new LazyClassSignature(ctx, sig, OBJECT_INTERNAL_NAME, null);
1✔
130
        }
131

132
        private static @NonNull String sigWithNTypeParams(int observedArity) {
133
            assert observedArity >= 0;
1✔
134

135
            // use constants for common values
136
            switch (observedArity) {
1✔
137
            case 0: return OBJECT_SIG;
1✔
138
            case 1: return "<T0" + OBJECT_BOUND + ">" + OBJECT_SIG;
×
139
            case 2: return "<T0" + OBJECT_BOUND + "T1" + OBJECT_BOUND + ">" + OBJECT_SIG;
×
140
            default: return Stream.iterate(0, i -> i + 1)
×
141
                                  .limit(observedArity)
×
142
                                  .map(i -> "T" + i + OBJECT_BOUND)
×
143
                                  .collect(Collectors.joining("", "<", ">" + OBJECT_SIG));
×
144
            }
145
        }
146

147
        @Override
148
        protected void doParse() {
149
            if (signature == null) {
1✔
150
                this.superType = rawSuper == null ? null // the Object class
1✔
151
                                                  : (JClassType) ctx.getTypeSystem().rawType(rawSuper);
1✔
152
                this.superItfs = CollectionUtil.map(rawItfs, klass -> (JClassType) ctx.getTypeSystem().rawType(klass));
1✔
153
                setTypeParams(Collections.emptyList());
1✔
154
            } else {
155
                ctx.sigParser().parseClassSignature(this, signature);
1✔
156
            }
157
        }
1✔
158

159
        @Override
160
        protected boolean isGeneric() {
161
            return signature != null && TypeParamsParser.hasTypeParams(signature);
1✔
162
        }
163

164
        @Override
165
        protected boolean postCondition() {
166
            return (superItfs != null && superType != null || signature == null) && typeParameters != null;
1✔
167
        }
168

169
        void setSuperInterfaces(List<JClassType> supers) {
170
            Validate.validState(superItfs == null);
1✔
171
            superItfs = supers;
1✔
172
        }
1✔
173

174
        void setSuperClass(JClassType sup) {
175
            Validate.validState(this.superType == null);
1✔
176
            this.superType = sup;
1✔
177
        }
1✔
178

179
        public JClassType getSuperType(Substitution subst) {
180
            ensureParsed();
1✔
181
            return superType == null ? null : superType.subst(subst);
1✔
182
        }
183

184
        public List<JClassType> getSuperItfs(Substitution subst) {
185
            ensureParsed();
1✔
186
            return TypeOps.substClasses(superItfs, subst);
1✔
187
        }
188

189
        public @Nullable JClassSymbol getRawSuper() {
190
            return rawSuper;
1✔
191
        }
192

193
        public List<JClassSymbol> getRawItfs() {
194
            return rawItfs;
1✔
195
        }
196

197
        @Override
198
        public String toString() {
199
            return signature;
×
200
        }
201
    }
202

203
    /**
204
     * Method or constructor type.
205
     */
206
    static class LazyMethodType extends GenericSigBase<ExecutableStub> implements TypeAnnotationReceiver {
1✔
207

208
        private final @NonNull String signature;
209

210
        private @Nullable TypeAnnotationSet receiverAnnotations;
211
        private List<JTypeMirror> parameterTypes;
212
        private List<JTypeMirror> exceptionTypes;
213
        private JTypeMirror returnType;
214
        private @Nullable TypeAnnotationSetWithReferences typeAnnots;
215
        private @Nullable String[] rawExceptions;
216

217
        /** Used for constructors of inner non-static classes. */
218
        private final boolean skipFirstParam;
219

220

221
        // TODO exceptions. Couple of notes:
222
        //  - the descriptor never contains thrown exceptions
223
        //  - the signature might not contain the thrown exception types (if they do not depend on type variables)
224
        //  - the exceptions array also contains unchecked exceptions
225
        //
226
        //  See https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.9.1
227
        // TODO test cases
228
        //  <E extends Exception> void foo()    throws E;          // descriptor "()V"                     signature "<TE;>()V^TE;"   exceptions: ???
229
        //  <E>                   void foo(E e) throws Exception;  // descriptor "(Ljava.lang.Object;)V"   signature "<TE;>(TE;)V"    exceptions: [ "java/lang/Exception" ]
230
        //                        void foo()    throws Exception;  // descriptor "()V"                     signature null             exceptions: [ "java/lang/Exception" ]
231
        LazyMethodType(ExecutableStub ctx,
232
                       @NonNull String descriptor,
233
                       @Nullable String genericSig,
234
                       @Nullable String[] exceptions,
235
                       boolean skipFirstParam) {
236
            super(ctx);
1✔
237
            this.signature = genericSig != null ? genericSig : descriptor;
1✔
238
            // generic signatures already omit the synthetic param
239
            this.skipFirstParam = skipFirstParam && genericSig == null;
1✔
240
            this.rawExceptions = exceptions;
1✔
241
        }
1✔
242

243
        @Override
244
        protected void doParse() {
245
            ctx.sigParser().parseMethodType(this, signature);
1✔
246
            if (rawExceptions != null && this.exceptionTypes.isEmpty()) {
1✔
247
                // the descriptor did not contain exceptions. They're in this string array.
248
                this.exceptionTypes = Arrays.stream(rawExceptions)
1✔
249
                                            .map(ctx.getResolver()::resolveFromInternalNameCannotFail)
1✔
250
                                            .map(ctx.getTypeSystem()::rawType)
1✔
251
                                            .collect(CollectionUtil.toUnmodifiableList());
1✔
252
            }
253
            if (typeAnnots != null) {
1✔
254
                // apply type annotations here
255
                // this may change type parameters
256
                boolean typeParamsWereMutated = typeAnnots.forEach(this::acceptAnnotationAfterParse);
1✔
257
                if (typeParamsWereMutated) {
1✔
258
                    // Some type parameters were mutated. We need to replace
259
                    // the old tparams with the annotated ones in all other
260
                    // types of this signature.
261

262
                    // This substitution looks like the identity mapping.
263
                    // It actually does work, because JTypeVar#equals considers only the symbol
264
                    // and not the type annotations. So unannotated tvars in the type will be
265
                    // matched with the annotated tvar that has the same symbol.
266
                    Substitution subst = Substitution.mapping(typeParameters, typeParameters);
1✔
267
                    this.returnType = this.returnType.subst(subst);
1✔
268
                    this.parameterTypes = TypeOps.subst(parameterTypes, subst);
1✔
269
                    this.exceptionTypes = TypeOps.subst(exceptionTypes, subst);
1✔
270
                }
271
            }
272
            // null this transient data out
273
            this.rawExceptions = null;
1✔
274
            this.typeAnnots = null;
1✔
275
        }
1✔
276

277
        public JTypeMirror applyReceiverAnnotations(JTypeMirror typeMirror) {
278
            if (receiverAnnotations == null) {
1✔
279
                return typeMirror;
×
280
            }
281
            return receiverAnnotations.decorate(typeMirror);
1✔
282
        }
283

284
        @Override
285
        protected boolean postCondition() {
286
            return parameterTypes != null && exceptionTypes != null && returnType != null;
1✔
287
        }
288

289

290
        @Override
291
        protected boolean isGeneric() {
292
            return TypeParamsParser.hasTypeParams(signature);
×
293
        }
294

295
        void setParameterTypes(List<JTypeMirror> params) {
296
            Validate.validState(parameterTypes == null);
1✔
297
            parameterTypes = skipFirstParam ? params.subList(1, params.size())
1✔
298
                                            : params;
1✔
299
        }
1✔
300

301
        void setExceptionTypes(List<JTypeMirror> exs) {
302
            Validate.validState(exceptionTypes == null);
1✔
303
            exceptionTypes = exs;
1✔
304
        }
1✔
305

306
        void setReturnType(JTypeMirror returnType) {
307
            Validate.validState(this.returnType == null);
1✔
308
            this.returnType = returnType;
1✔
309
        }
1✔
310

311
        public List<JTypeMirror> getParameterTypes() {
312
            ensureParsed();
1✔
313
            return parameterTypes;
1✔
314
        }
315

316
        public List<JTypeMirror> getExceptionTypes() {
317
            ensureParsed();
1✔
318
            return exceptionTypes;
1✔
319
        }
320

321
        public JTypeMirror getReturnType() {
322
            ensureParsed();
1✔
323
            return returnType;
1✔
324
        }
325

326
        @Override
327
        public String toString() {
328
            return signature;
×
329
        }
330

331

332
        @Override
333
        public void acceptTypeAnnotation(int typeRefInt, @Nullable TypePath path, SymAnnot annot) {
334
            // Accumulate type annotations for later
335
            // They shouldn't be applied right now because the descriptor maybe has not been parsed yet.
336
            if (typeAnnots == null) {
1✔
337
                typeAnnots = new TypeAnnotationSetWithReferences();
1✔
338
            }
339
            typeAnnots.add(new TypeReference(typeRefInt), path, annot);
1✔
340
        }
1✔
341

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

383
                JTypeVar tparam = typeParameters.get(tparamIdx);
1✔
384
                final JTypeMirror newUb = computeNewUpperBound(path, annot, boundIdx, tparam.getUpperBound());
1✔
385
                typeParameters.set(tparamIdx, tparam.withUpperBound(newUb));
1✔
386
                return true;
1✔
387
            }
388
            case TypeReference.METHOD_RECEIVER: {
389
                if (receiverAnnotations == null) {
1✔
390
                    receiverAnnotations = new TypeAnnotationSet();
1✔
391
                }
392
                receiverAnnotations.add(path, annot);
1✔
393
                return false;
1✔
394
            }
395
            default:
396
                throw new IllegalArgumentException(
×
397
                    "Invalid type reference for method or ctor type annotation: " + tyRef.getSort());
×
398
            }
399
        }
400

401
        private static JTypeMirror computeNewUpperBound(@Nullable TypePath path, SymAnnot annot, int boundIdx, JTypeMirror ub) {
402
            final JTypeMirror newUb;
403
            if (ub instanceof JIntersectionType) {
1✔
404
                JIntersectionType intersection = (JIntersectionType) ub;
1✔
405

406
                // Object is pruned from the component list
407
                boundIdx = intersection.getPrimaryBound().isTop() ? boundIdx - 1 : boundIdx;
1✔
408

409
                List<JTypeMirror> components = new ArrayList<>(intersection.getComponents());
1✔
410
                JTypeMirror bound = components.get(boundIdx);
1✔
411
                JTypeMirror newBound = TypeAnnotationHelper.applySinglePath(bound, path, annot);
1✔
412
                components.set(boundIdx, newBound);
1✔
413
                JTypeMirror newIntersection = intersection.getTypeSystem().glb(components);
1✔
414
                newUb = newIntersection;
1✔
415
            } else {
1✔
416
                newUb = TypeAnnotationHelper.applySinglePath(ub, path, annot);
1✔
417
            }
418
            return newUb;
1✔
419
        }
420
    }
421
}
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