• 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

96.82
/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeSystem.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.types;
6

7
import static net.sourceforge.pmd.util.CollectionUtil.emptyList;
8
import static net.sourceforge.pmd.util.CollectionUtil.immutableSetOf;
9

10
import java.io.Serializable;
11
import java.util.Collection;
12
import java.util.Collections;
13
import java.util.EnumMap;
14
import java.util.HashMap;
15
import java.util.List;
16
import java.util.Map;
17
import java.util.Objects;
18
import java.util.Set;
19
import java.util.function.Function;
20

21
import org.checkerframework.checker.nullness.qual.NonNull;
22
import org.checkerframework.checker.nullness.qual.Nullable;
23
import org.pcollections.HashTreePSet;
24
import org.pcollections.PSet;
25

26
import net.sourceforge.pmd.internal.util.AssertionUtil;
27
import net.sourceforge.pmd.lang.java.ast.JavaNode;
28
import net.sourceforge.pmd.lang.java.symbols.JClassSymbol;
29
import net.sourceforge.pmd.lang.java.symbols.JExecutableSymbol;
30
import net.sourceforge.pmd.lang.java.symbols.JFieldSymbol;
31
import net.sourceforge.pmd.lang.java.symbols.JFormalParamSymbol;
32
import net.sourceforge.pmd.lang.java.symbols.JLocalVariableSymbol;
33
import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol;
34
import net.sourceforge.pmd.lang.java.symbols.JTypeParameterSymbol;
35
import net.sourceforge.pmd.lang.java.symbols.SymbolResolver;
36
import net.sourceforge.pmd.lang.java.symbols.SymbolicValue.SymAnnot;
37
import net.sourceforge.pmd.lang.java.symbols.internal.UnresolvedClassStore;
38
import net.sourceforge.pmd.lang.java.symbols.internal.asm.AsmSymbolResolver;
39
import net.sourceforge.pmd.lang.java.symbols.internal.asm.Classpath;
40
import net.sourceforge.pmd.lang.java.types.BasePrimitiveSymbol.RealPrimitiveSymbol;
41
import net.sourceforge.pmd.lang.java.types.BasePrimitiveSymbol.VoidSymbol;
42
import net.sourceforge.pmd.lang.java.types.JPrimitiveType.PrimitiveTypeKind;
43
import net.sourceforge.pmd.util.CollectionUtil;
44

45
/**
46
 * Root context object for type analysis. Type systems own a global
47
 * {@link SymbolResolver}, which creates and caches external symbols.
48
 * Methods of this class promote symbols to types, and compose types together.
49
 * {@link TypeOps} and {@link TypeConversion} have some more operations on types.
50
 *
51
 * <p>Some special types are presented as constant fields, eg {@link #OBJECT}
52
 * or {@link #NULL_TYPE}. These are always comparable by reference.
53
 * Note that the primitive wrapper types are not exposed as constants
54
 * here, but can be accessed by using the {@link JTypeMirror#box() box}
55
 * method on some primitive constant.
56
 *
57
 * <p>The lifetime of a type system is the analysis: it is shared by
58
 * all compilation units.
59
 * TODO this is hacked together by comparing the ClassLoader, but this
60
 * should be in the language instance
61
 *
62
 * <p>Nodes have a reference to the type system they were created for:
63
 * {@link JavaNode#getTypeSystem()}.
64
 */
65
@SuppressWarnings("PMD.CompareObjectsWithEquals")
1✔
66
public final class TypeSystem {
67

68
    /**
69
     * Top type of the reference type system. This is the type for the
70
     * {@link Object} class. Note that even interfaces have this type
71
     * as a supertype ({@link JTypeMirror#getSuperTypeSet()}).
72
     */
73
    public final JClassType OBJECT;
74

75
    /**
76
     * The bottom type of the reference type system. This is named
77
     * the <i>null type</i> in the JLS and is not denotable in Java
78
     * programs.
79
     *
80
     * <p>This implementation uses this as the type of the 'null' literal.
81
     *
82
     * <p>The null type has no symbol.
83
     */
84
    public final JTypeMirror NULL_TYPE = new NullType(this);
1✔
85

86

87
    /** Primitive type {@code boolean}. */
88
    public final JPrimitiveType BOOLEAN;
89
    /** Primitive type {@code char}. */
90
    public final JPrimitiveType CHAR;
91
    /** Primitive type {@code byte}. */
92
    public final JPrimitiveType BYTE;
93
    /** Primitive type {@code short}. */
94
    public final JPrimitiveType SHORT;
95
    /** Primitive type {@code int}. */
96
    public final JPrimitiveType INT;
97
    /** Primitive type {@code long}. */
98
    public final JPrimitiveType LONG;
99
    /** Primitive type {@code float}. */
100
    public final JPrimitiveType FLOAT;
101
    /** Primitive type {@code double}. */
102
    public final JPrimitiveType DOUBLE;
103

104
    /**
105
     * The set of all primitive types. See {@link #getPrimitive(PrimitiveTypeKind)}.
106
     */
107
    public final Set<JPrimitiveType> allPrimitives;
108
    private final Map<PrimitiveTypeKind, JPrimitiveType> primitivesByKind;
109

110
    /**
111
     * A constant to represent the normal absence of a type. The
112
     * primitive {@code void.class} represents that type, and this
113
     * is the return type of a void method.
114
     *
115
     * <p>Note that the type of the class literal {@code void.class}
116
     * is {@code Class<java.lang.Void>}, not NO_TYPE.
117
     *
118
     * <p>{@code java.lang.Void} is represented by {@link #BOXED_VOID}.
119
     * Note that {@code BOXED_VOID.unbox() != NO_TYPE}, {@code NO_TYPE.box() != BOXED_VOID}.
120
     *
121
     * <p>{@code NO_TYPE.isPrimitive()} returns false, even though
122
     * {@code void.class.isPrimitive()} returns true.
123
     */
124
    public final JTypeMirror NO_TYPE;
125

126
    /**
127
     * A constant to represent an unresolved type. This means, that resolution
128
     * was attempted but failed and shouldn't be tried again. The symbol
129
     * is a {@link JClassSymbol}.
130
     *
131
     * <p>Note that {@link TypeOps#isConvertible(JTypeMirror, JTypeMirror)}
132
     * considers this type a subtype of anything, even primitive types.
133
     */
134
    public final JTypeMirror UNKNOWN;
135

136
    /**
137
     * A constant to represent a typing error. This would have been
138
     * reported by a compiler, and is used to propagate errors.
139
     *
140
     * <p>Note that {@link TypeOps#isConvertible(JTypeMirror, JTypeMirror)}
141
     * considers this type a subtype of anything, even primitive types.
142
     */
143
    public final JTypeMirror ERROR;
144

145
    /**
146
     * Sentinel value for an unresolved method. This type corresponds to
147
     * a method declaration in the type {@link #UNKNOWN}, returning {@link #UNKNOWN}.
148
     */
149
    public final JMethodSig UNRESOLVED_METHOD = new UnresolvedMethodSig(this);
1✔
150

151
    /*
152
     * Common, non-special types.
153
     */
154

155
    /** The unbounded wildcard, "?". */
156
    public final JWildcardType UNBOUNDED_WILD;
157

158
    /** The interface Cloneable. This is included because it is a supertype of array types. */
159
    public final JClassType CLONEABLE;
160
    /** The interface Serializable. This is included because it is a supertype of array types. */
161
    public final JClassType SERIALIZABLE;
162

163
    /**
164
     * This is the boxed type of {@code Void.class}, not to be confused with
165
     * {@code void.class}, which in this framework is represented by
166
     * {@link #NO_TYPE}.
167
     *
168
     * <p>Note that {@code BOXED_VOID.unbox() != NO_TYPE}, {@code NO_TYPE.box() != BOXED_VOID}.
169
     */
170
    public final JClassType BOXED_VOID;
171

172

173
    /** Contains special types, that must be shared to be comparable by reference. */
174
    private final Map<JTypeDeclSymbol, JTypeMirror> sharedTypes;
175
    // test only
176
    final SymbolResolver resolver;
177

178
    /**
179
     * Builds a new type system. Its public fields will be initialized
180
     * with fresh types, unrelated to other types.
181
     *
182
     * @param bootstrapResourceLoader Classloader used to resolve class files
183
     *                                to populate the fields of the new type
184
     *                                system
185
     */
186
    public static TypeSystem usingClassLoaderClasspath(ClassLoader bootstrapResourceLoader) {
187
        return usingClasspath(Classpath.forClassLoader(bootstrapResourceLoader));
1✔
188
    }
189

190
    /**
191
     * Builds a new type system. Its public fields will be initialized
192
     * with fresh types, unrelated to other types.
193
     *
194
     * @param bootstrapResourceLoader Classpath used to resolve class files
195
     *                                to populate the fields of the new type
196
     *                                system
197
     */
198
    public static TypeSystem usingClasspath(Classpath bootstrapResourceLoader) {
199
        return new TypeSystem(ts -> new AsmSymbolResolver(ts, bootstrapResourceLoader));
1✔
200
    }
201

202
    /**
203
     * Builds a new type system. Its public fields will be initialized
204
     * with fresh types, unrelated to other types.
205
     *
206
     * @param symResolverMaker A function that creates a new symbol
207
     *                         resolver, which will be owned by the
208
     *                         new type system. Because of cyclic
209
     *                         dependencies, the new type system
210
     *                         is leaked before its initialization
211
     *                         completes, so fields of the type system
212
     *                         are unusable at that time.
213
     *                         The resolver is used to create some shared
214
     *                         types: {@link #OBJECT}, {@link #CLONEABLE},
215
     *                         {@link #SERIALIZABLE}, {@link #BOXED_VOID}.
216
     */
217
    public TypeSystem(Function<TypeSystem, ? extends SymbolResolver> symResolverMaker) {
1✔
218
        this.resolver = symResolverMaker.apply(this); // leak the this
1✔
219

220
        // initialize primitives. their constructor also initializes their box + box erasure
221

222
        BOOLEAN = createPrimitive(PrimitiveTypeKind.BOOLEAN, Boolean.class);
1✔
223
        CHAR = createPrimitive(PrimitiveTypeKind.CHAR, Character.class);
1✔
224
        BYTE = createPrimitive(PrimitiveTypeKind.BYTE, Byte.class);
1✔
225
        SHORT = createPrimitive(PrimitiveTypeKind.SHORT, Short.class);
1✔
226
        INT = createPrimitive(PrimitiveTypeKind.INT, Integer.class);
1✔
227
        LONG = createPrimitive(PrimitiveTypeKind.LONG, Long.class);
1✔
228
        FLOAT = createPrimitive(PrimitiveTypeKind.FLOAT, Float.class);
1✔
229
        DOUBLE = createPrimitive(PrimitiveTypeKind.DOUBLE, Double.class);
1✔
230

231
        BOOLEAN.superTypes = immutableSetOf(BOOLEAN);
1✔
232
        CHAR.superTypes = immutableSetOf(CHAR, INT, LONG, FLOAT, DOUBLE);
1✔
233
        BYTE.superTypes = immutableSetOf(BYTE, SHORT, INT, LONG, FLOAT, DOUBLE);
1✔
234
        SHORT.superTypes = immutableSetOf(SHORT, INT, LONG, FLOAT, DOUBLE);
1✔
235
        INT.superTypes = immutableSetOf(INT, LONG, FLOAT, DOUBLE);
1✔
236
        LONG.superTypes = immutableSetOf(LONG, FLOAT, DOUBLE);
1✔
237
        FLOAT.superTypes = immutableSetOf(FLOAT, DOUBLE);
1✔
238
        DOUBLE.superTypes = immutableSetOf(DOUBLE);
1✔
239

240
        this.allPrimitives = immutableSetOf(BOOLEAN, CHAR, BYTE, SHORT, INT, LONG, FLOAT, DOUBLE);
1✔
241
        this.primitivesByKind = new EnumMap<>(PrimitiveTypeKind.class);
1✔
242
        primitivesByKind.put(PrimitiveTypeKind.BOOLEAN, BOOLEAN);
1✔
243
        primitivesByKind.put(PrimitiveTypeKind.CHAR, CHAR);
1✔
244
        primitivesByKind.put(PrimitiveTypeKind.BYTE, BYTE);
1✔
245
        primitivesByKind.put(PrimitiveTypeKind.SHORT, SHORT);
1✔
246
        primitivesByKind.put(PrimitiveTypeKind.INT, INT);
1✔
247
        primitivesByKind.put(PrimitiveTypeKind.LONG, LONG);
1✔
248
        primitivesByKind.put(PrimitiveTypeKind.FLOAT, FLOAT);
1✔
249
        primitivesByKind.put(PrimitiveTypeKind.DOUBLE, DOUBLE);
1✔
250

251
        // note that those intentionally have names that are invalid as java identifiers
252
        // todo those special types should probably have a special implementation here,
253
        //  so as not to depend on UnresolvedClassStore
254
        UnresolvedClassStore unresolvedSyms = new UnresolvedClassStore(this);
1✔
255
        JClassSymbol unresolvedTypeSym = unresolvedSyms.makeUnresolvedReference("(*unknown*)", 0);
1✔
256
        UNKNOWN = new SentinelType(this, "(*unknown*)", unresolvedTypeSym);
1✔
257

258
        JClassSymbol errorTypeSym = unresolvedSyms.makeUnresolvedReference("(*error*)", 0);
1✔
259
        ERROR = new SentinelType(this, "(*error*)", errorTypeSym);
1✔
260

261
        JClassSymbol primitiveVoidSym = new VoidSymbol(this);
1✔
262
        NO_TYPE = new SentinelType(this, "void", primitiveVoidSym);
1✔
263

264
        // reuse instances for common types
265

266
        // this map is vital to preserve some of the invariants of
267
        // the framework, e.g., that primitive types are never represented
268
        // by a ClassType, or that OBJECT is unique
269

270
        // this is only appropriate for non-generic types
271

272
        Map<JClassSymbol, JTypeMirror> shared = new HashMap<>();
1✔
273

274
        OBJECT = addSpecial(Object.class, shared);
1✔
275
        SERIALIZABLE = addSpecial(Serializable.class, shared);
1✔
276
        CLONEABLE = addSpecial(Cloneable.class, shared);
1✔
277
        BOXED_VOID = addSpecial(Void.class, shared);
1✔
278

279
        shared.put(primitiveVoidSym, NO_TYPE);
1✔
280
        shared.put(unresolvedTypeSym, UNKNOWN);
1✔
281
        shared.put(errorTypeSym, ERROR);
1✔
282

283
        for (JPrimitiveType prim : allPrimitives) {
1✔
284
            // primitives have a special implementation for their box
285
            shared.put(prim.getSymbol(), prim);
1✔
286
            shared.put(prim.box().getSymbol(), prim.box());
1✔
287
        }
1✔
288

289
        // make it really untouchable
290
        this.sharedTypes = Collections.unmodifiableMap(new HashMap<>(shared));
1✔
291

292
        UNBOUNDED_WILD = new WildcardTypeImpl(this, true, OBJECT, HashTreePSet.empty());
1✔
293
    }
1✔
294

295
    /**
296
     * Returns the bootstrap symbol resolver. Concrete analysis passes
297
     * may decorate this with different resolvers.
298
     */
299
    public SymbolResolver bootstrapResolver() {
300
        return resolver;
1✔
301
    }
302

303
    // helpers for the constructor, cannot use typeOf, only for trusted types
304

305
    private JClassType addSpecial(Class<?> klass, Map<JClassSymbol, JTypeMirror> shared) {
306
        JClassSymbol sym = getBootStrapSymbol(klass);
1✔
307
        JClassType nonErased = new ClassTypeImpl(this, sym, HashTreePSet.empty());
1✔
308
        shared.put(sym, nonErased);
1✔
309
        return nonErased;
1✔
310
    }
311

312
    private JClassSymbol getBootStrapSymbol(Class<?> clazz) {
313
        AssertionUtil.requireParamNotNull("clazz", clazz);
1✔
314
        JClassSymbol sym = resolver.resolveClassFromBinaryName(clazz.getName());
1✔
315
        return Objects.requireNonNull(sym, "sym");
1✔
316
    }
317

318
    private @NonNull JPrimitiveType createPrimitive(PrimitiveTypeKind kind, Class<?> box) {
319
        return new JPrimitiveType(this, kind, new RealPrimitiveSymbol(this, kind), getBootStrapSymbol(box), HashTreePSet.empty());
1✔
320
    }
321

322

323
    // type creation routines
324

325
    /**
326
     * Returns the class symbol for the given reflected class. This asks
327
     * the classloader of this type system. Returns null if the parameter
328
     * is null, or the class is not available in the analysis classpath.
329
     *
330
     * @param clazz Class
331
     */
332
    public @Nullable JClassSymbol getClassSymbol(@Nullable Class<?> clazz) {
333
        if (clazz == null) {
1✔
334
            return null;
1✔
335
        } else if (clazz.isPrimitive()) {
1✔
336
            PrimitiveTypeKind kind = PrimitiveTypeKind.fromName(clazz.getName());
1✔
337
            if (kind == null) { // void
1✔
338
                return (JClassSymbol) NO_TYPE.getSymbol();
1✔
339
            }
340
            return getPrimitive(kind).getSymbol();
1✔
341
        } else if (clazz.isArray()) {
1✔
342
            return new ArraySymbolImpl(this, getClassSymbol(clazz.getComponentType()));
1✔
343
        }
344

345
        return resolver.resolveClassFromBinaryName(clazz.getName());
1✔
346
    }
347

348
    /**
349
     * Returns a symbol for the binary name. Returns null if the name is
350
     * null or the symbol is not found on the classpath. The class must
351
     * not be an array.
352
     *
353
     * @param binaryName Binary name
354
     *
355
     * @return A symbol, or null
356
     *
357
     * @throws IllegalArgumentException if the argument is not a binary name
358
     */
359
    public @Nullable JClassSymbol getClassSymbol(String binaryName) {
360
        return getClassSymbolImpl(binaryName, false);
1✔
361
    }
362

363
    /**
364
     * Returns a symbol for the canonical name. Returns null if the name is
365
     * null or the symbol is not found on the classpath. The class must
366
     * not be an array.
367
     *
368
     * <p>Canonical names separate nested classes with {@code .}
369
     * (periods) instead of {@code $} (dollars) as the JVM does. Users
370
     * usually use canonical names, but lookup is much more costly.
371
     *
372
     * @param canonicalName Canonical name
373
     *
374
     * @return A symbol, or null
375
     *
376
     * @throws IllegalArgumentException if the argument is not a binary name
377
     */
378
    public @Nullable JClassSymbol getClassSymbolFromCanonicalName(String canonicalName) {
379
        return getClassSymbolImpl(canonicalName, true);
1✔
380
    }
381

382
    private @Nullable JClassSymbol getClassSymbolImpl(String name, boolean isCanonical) {
383
        if (name == null) {
1✔
384
            return null;
1✔
385
        }
386
        if ("void".equals(name)) {
1✔
387
            return (JClassSymbol) NO_TYPE.getSymbol();
1✔
388
        }
389
        PrimitiveTypeKind kind = PrimitiveTypeKind.fromName(name);
1✔
390
        if (kind != null) { // void
1✔
391
            return getPrimitive(kind).getSymbol();
1✔
392
        }
393

394
        AssertionUtil.assertValidJavaBinaryNameNoArray(name);
1✔
395

396
        return isCanonical ? resolver.resolveClassFromCanonicalName(name)
1✔
397
                           : resolver.resolveClassFromBinaryName(name);
1✔
398
    }
399

400
    /**
401
     * Returns a type mirror for the given symbol. If the symbol declares
402
     * type parameters, then the resulting type is raw (differs from the
403
     * behaviour of {@link #declaration(JClassSymbol)}), meaning all its
404
     * supertypes are erased.
405
     *
406
     * <p>If the symbol is a {@link JTypeParameterSymbol type parameter},
407
     * returns a {@link JTypeVar}.
408
     *
409
     * <p>If the symbol is a {@link JClassSymbol}, then:
410
     * <ul>
411
     * <li>If it represents a primitive type, the corresponding {@link JPrimitiveType}
412
     * is returned (one of {@link #INT}, {@link #CHAR}, etc.).
413
     * <li>If it represents an array type, a new {@link JArrayType} is
414
     * returned. The component type will be built with a recursive call.
415
     * <li>If it represents a class or interface type, a {@link JClassType}
416
     * is returned.
417
     * <ul>
418
     *     <li>If the parameter {@code isErased} is true, and if the
419
     *     symbol declares type parameters, then it will be a
420
     *     {@linkplain JClassType#isRaw() raw type}. This means,
421
     *     which means all its generic supertypes are {@linkplain JClassType#hasErasedSuperTypes() erased}.
422
     *     <li>Otherwise, the generic supertypes are preserved. In particular,
423
     *     if the symbol declares type parameters itself, then it will
424
     *     be a {@linkplain JClassType#isGenericTypeDeclaration() generic type declaration}.
425
     * </ul>
426
     * If the symbol is a non-static member of another class, then the given
427
     * type's {@linkplain JClassType#getEnclosingType() enclosing type} is
428
     * created, applying the above rules about erasure recursively. A type
429
     * is either completely erased, or completely parameterized.
430
     * </li>
431
     * </ul>
432
     *
433
     * @param symbol   Symbol for the type declaration
434
     * @param isErased Whether the type should be consider erased, if it
435
     *                 represents a class or interface type. This does not
436
     *                 erase type variables, or array types for that matter.
437
     *
438
     * @return A type, or null if the symbol is null
439
     */
440
    public JTypeMirror typeOf(@Nullable JTypeDeclSymbol symbol, boolean isErased) {
441
        if (symbol == null) {
1✔
442
            return null;
1✔
443
        }
444

445
        // takes care of primitives, and constants like OBJECT or UNRESOLVED_TYPE
446
        JTypeMirror common = specialCache(symbol);
1✔
447
        if (common != null) {
1✔
448
            return common;
1✔
449
        }
450

451
        if (symbol instanceof JClassSymbol) {
1✔
452
            JClassSymbol classSym = (JClassSymbol) symbol;
1✔
453
            if (classSym.isArray()) {
1✔
454
                JTypeMirror component = typeOf(classSym.getArrayComponent(), isErased);
1✔
455
                assert component != null : "the symbol necessarily has an array component symbol";
1✔
456
                return arrayType(component, classSym);
1✔
457
            } else {
458
                return new ClassTypeImpl(this, classSym, emptyList(), isErased, HashTreePSet.empty());
1✔
459
            }
460
        } else if (symbol instanceof JTypeParameterSymbol) {
1✔
461
            return ((JTypeParameterSymbol) symbol).getTypeMirror();
1✔
462
        }
463
        throw AssertionUtil.shouldNotReachHere("Uncategorized type symbol " + symbol.getClass() + ": " + symbol);
×
464
    }
465

466
    // test only for now
467
    JClassType forceErase(JClassType t) {
468
        JClassType erasure = t.getErasure();
1✔
469
        if (erasure == t) {
1✔
470
            return new ErasedClassType(this, t.getSymbol(), t.getTypeAnnotations());
1✔
471
        }
472
        return erasure;
×
473
    }
474

475
    /**
476
     * Like {@link #typeOf(JTypeDeclSymbol, boolean)}, defaulting the
477
     * erased parameter to true. If the symbol is not generic,
478
     * the returned symbol is not actually raw.
479
     *
480
     * @param klass Symbol
481
     *
482
     * @return An erased class type
483
     */
484
    public JTypeMirror rawType(@Nullable JTypeDeclSymbol klass) {
485
        return typeOf(klass, true);
1✔
486
    }
487

488
    /**
489
     * Like {@link #typeOf(JTypeDeclSymbol, boolean)}, defaulting the
490
     * erased parameter to false. If the symbol is not generic,
491
     * the returned symbol is not actually a generic type declaration.
492
     *
493
     * @param klass Symbol
494
     *
495
     * @return An erased class type
496
     */
497
    public JTypeMirror declaration(@Nullable JClassSymbol klass) {
498
        return typeOf(klass, false);
1✔
499
    }
500

501
    /**
502
     * Produce a parameterized type with the given symbol and type arguments.
503
     * The type argument list must match the declared formal type parameters in
504
     * length. Non-generic symbols are accepted by this method, provided the
505
     * argument list is empty. If the symbol is unresolved, any type argument
506
     * list is accepted.
507
     *
508
     * <p>This method is equivalent to {@code rawType(klass).withTypeArguments(typeArgs)},
509
     * but that code would require a cast.
510
     *
511
     * @param klass    A symbol
512
     * @param typeArgs List of type arguments
513
     *
514
     * @throws IllegalArgumentException see {@link JClassType#withTypeArguments(List)}
515
     */
516
    // todo how does this behave with nested generic types
517
    public @NonNull JTypeMirror parameterise(@NonNull JClassSymbol klass, @NonNull List<? extends JTypeMirror> typeArgs) {
518
        if (typeArgs.isEmpty()) {
1✔
519
            return rawType(klass); // note this ensures that OBJECT and such is preserved
1✔
520
        }
521
        // if the type arguments are mismatched, the constructor will throw
522
        return new ClassTypeImpl(this, klass, CollectionUtil.defensiveUnmodifiableCopy(typeArgs), true, HashTreePSet.empty());
1✔
523
    }
524

525

526
    /**
527
     * Creates a new array type from an arbitrary element type.
528
     *
529
     * <pre>{@code
530
     * arrayType(T, 0)          = T
531
     * arrayType(T, 1)          = T[]
532
     * arrayType(T, 3)          = T[][][]
533
     * arrayType(T[], 2)        = T[][][]
534
     * }</pre>
535
     *
536
     * @param element       Element type
537
     * @param numDimensions Number of dimensions
538
     *
539
     * @return A new array type
540
     *
541
     * @throws IllegalArgumentException If numDimensions is negative
542
     * @throws IllegalArgumentException If the element type is a {@link JWildcardType},
543
     *                                  the null type, or {@link #NO_TYPE void}.
544
     * @throws NullPointerException     If the element type is null
545
     */
546
    public JTypeMirror arrayType(@NonNull JTypeMirror element, int numDimensions) {
547
        AssertionUtil.requireNonNegative("numDimensions", numDimensions);
1✔
548
        checkArrayElement(element); // note we throw even if numDimensions == 0
1✔
549

550
        if (numDimensions == 0) {
1✔
551
            return element;
1✔
552
        }
553

554
        JArrayType res = new JArrayType(this, element);
1✔
555
        while (--numDimensions > 0) {
1✔
556
            res = new JArrayType(this, res);
1✔
557
        }
558
        return res;
1✔
559
    }
560

561
    /**
562
     * Like {@link #arrayType(JTypeMirror, int)}, with one dimension.
563
     *
564
     * @param component Component type
565
     *
566
     * @return An array type
567
     *
568
     * @throws IllegalArgumentException If the element type is a {@link JWildcardType},
569
     *                                  the null type, or {@link #NO_TYPE void}.
570
     * @throws NullPointerException     If the element type is null
571
     */
572
    public JArrayType arrayType(@NonNull JTypeMirror component) {
573
        return arrayType(component, null);
1✔
574
    }
575

576
    /** Trusted constructor. */
577
    private JArrayType arrayType(@NonNull JTypeMirror component, @Nullable JClassSymbol symbol) {
578
        checkArrayElement(component);
1✔
579
        return new JArrayType(this, component, symbol, HashTreePSet.empty());
1✔
580
    }
581

582

583
    private void checkArrayElement(@NonNull JTypeMirror element) {
584
        AssertionUtil.requireParamNotNull("elementType", element);
1✔
585

586
        if (element instanceof JWildcardType
1✔
587
            || element == NULL_TYPE
588
            || element == NO_TYPE) {
589
            throw new IllegalArgumentException("The type < " + element + " > is not a valid array element type");
×
590
        }
591
    }
1✔
592

593
    public JMethodSig sigOf(JExecutableSymbol methodSym) {
594
        return sigOf(methodSym, Substitution.EMPTY);
1✔
595
    }
596

597
    public JMethodSig sigOf(JExecutableSymbol methodSym, Substitution subst) {
598
        JClassType klass = (JClassType) declaration(methodSym.getEnclosingClass());
1✔
599
        return new ClassMethodSigImpl(klass.subst(subst), methodSym);
1✔
600
    }
601

602
    public JVariableSig.FieldSig sigOf(JTypeMirror decl, JFieldSymbol fieldSym) {
603
        return JVariableSig.forField(decl, fieldSym);
1✔
604
    }
605

606
    public JVariableSig sigOf(JClassType decl, JLocalVariableSymbol fieldSym) {
607
        return JVariableSig.forLocal(decl, fieldSym);
1✔
608
    }
609

610
    public JVariableSig sigOf(JClassType decl, JFormalParamSymbol fieldSym) {
611
        return JVariableSig.forLocal(decl, fieldSym);
1✔
612
    }
613

614

615
    /**
616
     * Builds a wildcard type with a single bound.
617
     *
618
     * <pre>{@code
619
     *
620
     * wildcard(true, T)      = ? extends T
621
     * wildcard(false, T)     = ? super T
622
     * wildcard(true, OBJECT) = ?
623
     *
624
     * }</pre>
625
     *
626
     * @param isUpperBound If true, this is an "extends" wildcard, otherwise a "super"
627
     * @param bound        Bound of the wildcard
628
     *
629
     * @return A wildcard
630
     *
631
     * @throws NullPointerException     If the bound is null
632
     * @throws IllegalArgumentException If the bound is a primitive type,
633
     *                                  or a wildcard type
634
     * @throws IllegalArgumentException If the bound is OBJECT and this
635
     *                                  is a lower-bounded wildcard (? super Object)
636
     */
637
    public JWildcardType wildcard(boolean isUpperBound, @NonNull JTypeMirror bound) {
638
        Objects.requireNonNull(bound, "Argument shouldn't be null");
1✔
639
        if (bound.isPrimitive() || bound instanceof JWildcardType) {
1✔
640
            throw new IllegalArgumentException("<" + bound + "> cannot be a wildcard bound");
1✔
641
        }
642
        return isUpperBound && bound == OBJECT ? UNBOUNDED_WILD
1✔
643
                                               : new WildcardTypeImpl(this, isUpperBound, bound, HashTreePSet.empty());
1✔
644
    }
645

646
    /**
647
     * Maps a type decl symbol to its shared representation. Eg this
648
     * maps the symbol for {@code int.class} to {@link #INT}. Only
649
     * non-generic types are cached.
650
     */
651
    private @Nullable JTypeMirror specialCache(JTypeDeclSymbol raw) {
652
        return sharedTypes.get(raw);
1✔
653
    }
654

655

656
    /**
657
     * Gets the primitive type identified by the given kind.
658
     *
659
     * @param kind Kind of primitive type
660
     *
661
     * @return A primitive type
662
     *
663
     * @throws NullPointerException if kind is null
664
     */
665
    public @NonNull JPrimitiveType getPrimitive(@NonNull PrimitiveTypeKind kind) {
666
        AssertionUtil.requireParamNotNull("kind", kind);
1✔
667
        return primitivesByKind.get(kind);
1✔
668
    }
669

670
    /**
671
     * The least upper bound, or "lub", of a set of reference types is
672
     * a shared supertype that is more specific than any other shared
673
     * supertype (that is, no other shared supertype is a subtype of the
674
     * least upper bound).
675
     *
676
     * @throws IllegalArgumentException If types is empty
677
     * @throws NullPointerException     If types is null
678
     */
679
    public JTypeMirror lub(Collection<? extends JTypeMirror> types) {
680
        return Lub.lub(this, types);
1✔
681
    }
682

683
    /**
684
     * Returns the greatest lower bound of the given set of types.
685
     * This is defined in JLS§5.1.10 (Capture Conversion):
686
     *
687
     * <pre>
688
     * glb(V1,...,Vm) = V1 &amp; ... &amp; Vm
689
     * glb(V) = V
690
     * </pre>
691
     *
692
     * <p>This may alter the components, so that:
693
     * <ul>
694
     * <li>No intersection type is a component: {@code ((A & B) & C) = (A & (B & C)) = (A & B & C)}
695
     * <li>No two types in the intersection are subtypes of one another
696
     * (the intersection is minimal): {@code A <: B => (A & B) = A}, in particular, {@code (A & A) = A}
697
     * <li>The intersection has a single component that is a
698
     * class, array, or type variable. If all components are interfaces,
699
     * then that component is {@link #OBJECT}.
700
     * </ul>
701
     *
702
     * <p>If after these transformations, only a single component remains,
703
     * then that is the returned type. Otherwise a {@link JIntersectionType}
704
     * is created. Note that the intersection may be unsatisfiable (eg {@code A[] & Runnable}),
705
     * but we don't attempt to minimize this to {@link #NULL_TYPE}.
706
     *
707
     * <p>See also JLS§4.9 (Intersection types).
708
     *
709
     * @throws IllegalArgumentException If some component is not a class, interface, array, or type variable
710
     * @throws IllegalArgumentException If there is more than one minimal class or array type
711
     * @throws IllegalArgumentException If types is empty
712
     * @throws NullPointerException     If types is null
713
     */
714
    public JTypeMirror glb(Collection<? extends JTypeMirror> types) {
715
        return Lub.glb(this, types);
1✔
716
    }
717

718
    // package-private
719
    JClassType erasedType(@NonNull JClassSymbol symbol) {
720
        JTypeMirror t = specialCache(symbol);
1✔
721
        if (t != null) {
1✔
722
            return (JClassType) t.getErasure();
1✔
723
        } else {
724
            return new ErasedClassType(this, symbol, HashTreePSet.empty());
1✔
725
        }
726
    }
727

728

729
    /**
730
     * Returns a new type variable for the given symbol. This is only
731
     * intended to be used by the implementor of {@link JTypeParameterSymbol}.
732
     */
733
    public JTypeVar newTypeVar(JTypeParameterSymbol symbol) {
734
        // note: here we don't pass the symbol's annotations. These stay on
735
        // the symbol as they can be visually noisy since they would be
736
        // repeated at each use-site
737
        return new TypeVarImpl.RegularTypeVar(this, symbol, HashTreePSet.empty());
1✔
738
    }
739

740
    private static final class NullType implements JTypeMirror {
741

742
        private final TypeSystem ts;
743

744
        NullType(TypeSystem ts) {
1✔
745
            this.ts = ts;
1✔
746
        }
1✔
747

748
        @Override
749
        public JTypeMirror withAnnotations(PSet<SymAnnot> newTypeAnnots) {
750
            return this;
×
751
        }
752

753
        @Override
754
        public PSet<SymAnnot> getTypeAnnotations() {
755
            return HashTreePSet.empty();
×
756
        }
757

758
        @Override
759
        public JTypeMirror subst(Function<? super SubstVar, ? extends @NonNull JTypeMirror> subst) {
760
            return this;
1✔
761
        }
762

763
        @Override
764
        public TypeSystem getTypeSystem() {
765
            return ts;
1✔
766
        }
767

768
        @Override
769
        public boolean isBottom() {
770
            return true;
1✔
771
        }
772

773
        @Override
774
        public @Nullable JClassSymbol getSymbol() {
775
            return null;
1✔
776
        }
777

778
        @Override
779
        public <T, P> T acceptVisitor(JTypeVisitor<T, P> visitor, P p) {
780
            return visitor.visitNullType(this, p);
1✔
781
        }
782

783
        @Override
784
        public String toString() {
785
            return "null";
1✔
786
        }
787
    }
788
}
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