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

pmd / pmd / 277

27 Nov 2025 01:37PM UTC coverage: 78.778% (+0.03%) from 78.749%
277

push

github

adangel
[java] UseArraysAsList: skip when if-statements (#6228)

18419 of 24233 branches covered (76.01%)

Branch coverage included in aggregate %.

40090 of 50038 relevant lines covered (80.12%)

0.81 hits per line

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

92.4
/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/TypeSigParser.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
import static java.util.Collections.emptyList;
8

9
import java.util.ArrayList;
10
import java.util.List;
11

12
import org.checkerframework.checker.nullness.qual.NonNull;
13
import org.checkerframework.checker.nullness.qual.Nullable;
14

15
import net.sourceforge.pmd.lang.java.symbols.JClassSymbol;
16
import net.sourceforge.pmd.lang.java.symbols.internal.asm.GenericSigBase.LazyMethodType;
17
import net.sourceforge.pmd.lang.java.types.JClassType;
18
import net.sourceforge.pmd.lang.java.types.JTypeMirror;
19
import net.sourceforge.pmd.lang.java.types.LexicalScope;
20
import net.sourceforge.pmd.lang.java.types.SubstVar;
21
import net.sourceforge.pmd.lang.java.types.TypeSystem;
22

23
/**
24
 * Implementation of the signature parser.
25
 */
26
final class TypeSigParser {
27

28
    private TypeSigParser() {
29
        // utility class
30
    }
31

32
    static int classHeader(final int start, TypeScanner b) {
33
        int cur = classType(start, b); // superclass
1✔
34
        JTypeMirror superClass = b.pop();
1✔
35

36
        if (b.charAt(cur) == 'L') {
1✔
37
            List<JTypeMirror> superItfs = new ArrayList<>(1);
1✔
38
            do {
39
                cur = classType(cur, b);
1✔
40
                superItfs.add(b.pop());
1✔
41
            } while (b.charAt(cur) == 'L');
1✔
42
            b.pushList(superItfs);
1✔
43
        } else {
1✔
44
            b.pushList(emptyList());
1✔
45
        }
46
        b.push(superClass);
1✔
47
        return cur;
1✔
48
    }
49

50
    static int methodType(LazyMethodType type, final int start, TypeScanner b) {
51
        int cur = parameterTypes(start, b);
1✔
52
        type.setParameterTypes(b.popList());
1✔
53
        cur = typeSignature(cur, b, true); // return type
1✔
54
        type.setReturnType(b.pop());
1✔
55
        cur = throwsSignaturesOpt(cur, b);
1✔
56
        type.setExceptionTypes(b.popList());
1✔
57
        return cur;
1✔
58
    }
59

60
    private static int parameterTypes(int start, TypeScanner b) {
61
        int cur = b.consumeChar(start, '(', "parameter types");
1✔
62
        if (b.charAt(cur) == ')') {
1✔
63
            b.pushList(emptyList()); // empty parameters
1✔
64
        } else {
65
            List<JTypeMirror> params = new ArrayList<>(1);
1✔
66
            do {
67
                cur = typeSignature(cur, b);
1✔
68
                params.add(b.pop());
1✔
69
            } while (b.charAt(cur) != ')');
1✔
70
            b.pushList(params);
1✔
71
        }
72
        cur = b.consumeChar(cur, ')');
1✔
73
        return cur;
1✔
74
    }
75

76
    private static int throwsSignaturesOpt(final int start, TypeScanner b) {
77
        int cur = start;
1✔
78
        if (b.charAt(cur) == '^') {
1✔
79
            List<JTypeMirror> thrown = new ArrayList<>(1);
1✔
80
            do {
81
                cur = b.consumeChar(cur, '^', "throws signature");
1✔
82
                char next = b.charAt(cur);
1✔
83
                if (next != 'T' && next != 'L') {
1!
84
                    // shouldn't be a base type
85
                    throw b.expected("an exception type", cur);
×
86
                }
87
                cur = typeSignature(cur, b);
1✔
88
                thrown.add(b.pop());
1✔
89
            } while (b.charAt(cur) == '^');
1✔
90
            b.pushList(thrown);
1✔
91
        } else {
1✔
92
            b.pushList(emptyList());
1✔
93
        }
94
        return cur;
1✔
95
    }
96

97
    static int typeVarBound(final int start, TypeScanner b) {
98
        List<JTypeMirror> bounds = new ArrayList<>();
1✔
99
        int cur = b.consumeChar(start, ':', "class bound");
1✔
100

101
        char next = b.charAt(cur);
1✔
102
        if (next == '[' || next == 'L' || next == 'T') {
1!
103
            // If the character after the ':' class bound marker is not the start of a
104
            // ReferenceTypeSignature, it means the class bound is empty (which is a valid case).
105
            cur = typeSignature(cur, b);
1✔
106
            bounds.add(b.pop());
1✔
107
        }
108

109
        while (b.charAt(cur) == ':') {
1✔
110
            cur = b.consumeChar(cur, ':');
1✔
111
            cur = typeSignature(cur, b);
1✔
112
            bounds.add(b.pop());
1✔
113
        }
114

115
        if (bounds.isEmpty()) {
1✔
116
            b.push(b.ts.OBJECT);
1✔
117
        } else {
118
            b.push(b.ts.glb(bounds));
1✔
119
        }
120
        return cur;
1✔
121
    }
122

123
    static int typeSignature(final int start, TypeScanner b) {
124
        return typeSignature(start, b, false);
1✔
125
    }
126

127
    private static int typeSignature(final int start, TypeScanner b, boolean acceptVoid) {
128
        char firstChar = b.charAt(start);
1✔
129
        switch (firstChar) {
1!
130
        case 'V':
131
            if (!acceptVoid) {
1!
132
                throw b.expected("a type, got void", start);
×
133
            }
134
            // intentional fallthrough
135
        case 'Z':
136
        case 'C':
137
        case 'B':
138
        case 'S':
139
        case 'I':
140
        case 'F':
141
        case 'J':
142
        case 'D':
143
            b.push(b.getBaseType(firstChar));
1✔
144
            return start + 1;
1✔
145
        case '[':
146
            return arrayType(start, b);
1✔
147
        case 'L':
148
            return classType(start, b);
1✔
149
        case 'T':
150
            return typeVar(start, b);
1✔
151
        default:
152
            throw b.expected("type", start);
×
153
        }
154
    }
155

156
    private static int classType(final int start, TypeScanner b) {
157

158
        StringBuilder internalName = new StringBuilder();
1✔
159

160
        int cur = b.consumeChar(start, 'L', "object type");
1✔
161
        cur = classId(cur, b, internalName);
1✔
162
        cur = typeArgsOpt(cur, b);
1✔
163

164
        JClassType t = b.makeClassType(internalName.toString(), b.popList());
1✔
165

166
        while (b.charAt(cur) == '.') {
1✔
167
            internalName.append('$');
1✔
168
            cur += 1;
1✔
169
            cur = identifier(cur, b, internalName);
1✔
170
            cur = typeArgsOpt(cur, b);
1✔
171

172
            t = b.parameterize(t, internalName.toString(), b.popList());
1✔
173
        }
174

175
        b.push(t);
1✔
176
        return b.consumeChar(cur, ';', "semicolon");
1✔
177
    }
178

179
    private static int typeArgsOpt(final int start, TypeScanner b) {
180
        if (b.charAt(start) == '<') {
1✔
181
            List<JTypeMirror> l = new ArrayList<>(2);
1✔
182
            int cur = b.consumeChar(start, '<');
1✔
183
            while (b.charAt(cur) != '>') {
1✔
184
                cur = typeArg(cur, b);
1✔
185
                l.add(b.pop());
1✔
186
            }
187
            cur = b.consumeChar(cur, '>');
1✔
188
            b.pushList(l);
1✔
189
            return cur;
1✔
190
        } else {
191
            b.pushList(emptyList());
1✔
192
            return start;
1✔
193
        }
194
    }
195

196
    private static int typeArg(final int start, TypeScanner b) {
197
        int cur = start;
1✔
198
        char firstChar = b.charAt(cur);
1✔
199
        switch (firstChar) {
1✔
200
        case '*':
201
            b.push(b.ts.UNBOUNDED_WILD);
1✔
202
            return cur + 1;
1✔
203
        case '+':
204
        case '-':
205
            cur = typeSignature(cur + 1, b);
1✔
206
            b.push(b.ts.wildcard(firstChar == '+', b.pop()));
1✔
207
            return cur;
1✔
208
        default:
209
            return typeSignature(cur, b);
1✔
210
        }
211
    }
212

213

214
    private static int arrayType(final int start, TypeScanner b) {
215
        int cur = b.consumeChar(start, '[', "array type");
1✔
216
        cur = typeSignature(cur, b);
1✔
217
        b.push(b.ts.arrayType(b.pop()));
1✔
218
        return cur;
1✔
219
    }
220

221

222
    private static int typeVar(final int start, TypeScanner b) {
223
        int cur = b.consumeChar(start, 'T', "type variable");
1✔
224
        StringBuilder nameBuilder = new StringBuilder();
1✔
225

226
        cur = identifier(cur, b, nameBuilder);
1✔
227
        cur = b.consumeChar(cur, ';');
1✔
228

229
        b.push(b.lookupTvar(nameBuilder.toString()));
1✔
230
        return cur;
1✔
231
    }
232

233

234
    private static int classId(final int start, SignatureScanner b, StringBuilder internalName) {
235
        int cur = start;
1✔
236
        cur = identifier(cur, b, null);
1✔
237
        while (b.charAt(cur) == '/') { // package specifier
1✔
238
            cur = cur + 1; // the slash
1✔
239
            cur = identifier(cur, b, null);
1✔
240
        }
241
        b.dumpChars(start, cur, internalName);
1✔
242
        return cur;
1✔
243
    }
244

245
    static int identifier(final int start, SignatureScanner b, @Nullable StringBuilder sb) {
246
        int cur = start;
1✔
247
        if (!isIdentifierChar(b.charAt(cur))) {
1!
248
            throw b.expected("identifier", cur);
×
249
        }
250
        do {
251
            cur++;
1✔
252
        } while (isIdentifierChar(b.charAt(cur)));
1✔
253

254
        if (sb != null) {
1✔
255
            b.dumpChars(start, cur, sb);
1✔
256
        }
257
        return cur;
1✔
258
    }
259

260

261
    private static boolean isIdentifierChar(char c) {
262
        switch (c) {
1✔
263
        case '.':
264
        case ';':
265
        case ':':
266
        case '[':
267
        case '/':
268
        case '<':
269
        case '>':
270
        case 0:
271
            return false;
1✔
272
        default:
273
            return true;
1✔
274
        }
275
    }
276

277
    interface ParseFunction {
278

279
        int parse(int offset, TypeScanner scanner);
280
    }
281

282
    abstract static class TypeScanner extends SignatureScanner {
1✔
283

284
        // 1-element "stacks", push must be followed by pop
285
        private @Nullable JTypeMirror type;
286
        private @Nullable List<JTypeMirror> list;
287

288
        private final TypeSystem ts;
289
        private final LexicalScope lexicalScope;
290

291
        TypeScanner(TypeSystem ts, LexicalScope lexicalScope, String descriptor) {
292
            super(descriptor);
1✔
293
            this.ts = ts;
1✔
294
            this.lexicalScope = lexicalScope;
1✔
295
        }
1✔
296

297
        TypeScanner(TypeSystem ts, LexicalScope lexicalScope, String chars, int start, int end) {
298
            super(chars, start, end);
1✔
299
            this.ts = ts;
1✔
300
            this.lexicalScope = lexicalScope;
1✔
301
        }
1✔
302

303
        void pushList(List<JTypeMirror> l) {
304
            assert this.list == null;
1!
305
            assert l != null;
1!
306
            this.list = l;
1✔
307
        }
1✔
308

309
        void push(JTypeMirror mirror) {
310
            assert this.type == null;
1!
311
            assert mirror != null;
1!
312
            this.type = mirror;
1✔
313
        }
1✔
314

315
        JTypeMirror pop() {
316
            assert this.type != null;
1!
317
            JTypeMirror t = this.type;
1✔
318
            this.type = null;
1✔
319
            return t;
1✔
320
        }
321

322
        List<JTypeMirror> popList() {
323
            assert this.list != null;
1!
324
            List<JTypeMirror> l = this.list;
1✔
325
            this.list = null;
1✔
326
            return l;
1✔
327
        }
328

329
        /**
330
         * Makes a class symbol from its internal name. This should return
331
         * non-null, if the symbol is not found (linkage error) then return
332
         * an unresolved symbol.
333
         */
334
        public abstract @NonNull JClassSymbol makeClassSymbol(String internalName, int observedArity);
335

336

337
        public JTypeMirror getBaseType(char baseType) {
338
            switch (baseType) {
1!
339
            case 'V': return ts.NO_TYPE;
1✔
340
            case 'Z': return ts.BOOLEAN;
1✔
341
            case 'C': return ts.CHAR;
1✔
342
            case 'B': return ts.BYTE;
1✔
343
            case 'S': return ts.SHORT;
1✔
344
            case 'I': return ts.INT;
1✔
345
            case 'F': return ts.FLOAT;
1✔
346
            case 'J': return ts.LONG;
1✔
347
            case 'D': return ts.DOUBLE;
1✔
348
            default: throw new IllegalArgumentException("'" + baseType + "' is not a valid base type descriptor");
×
349
            }
350
        }
351

352
        public JTypeMirror lookupTvar(String name) {
353
            @Nullable SubstVar mapped = lexicalScope.apply(name);
1✔
354
            if (mapped == null) {
1!
355
                throw new IllegalArgumentException(
×
356
                    "The lexical scope " + lexicalScope + " does not contain an entry for type variable " + name
357
                );
358
            }
359
            return mapped;
1✔
360
        }
361

362
        public JClassType makeClassType(String internalName, List<JTypeMirror> targs) {
363
            return (JClassType) ts.parameterise(makeClassSymbol(internalName, targs.size()), targs);
1✔
364
        }
365

366
        public JClassType parameterize(JClassType owner, String internalName, List<JTypeMirror> targs) {
367
            return owner.selectInner(makeClassSymbol(internalName, targs.size()), targs);
1✔
368
        }
369

370
    }
371

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