• 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

97.18
/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeConversion.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 java.util.Arrays.asList;
8
import static net.sourceforge.pmd.lang.java.types.JPrimitiveType.PrimitiveTypeKind.BYTE;
9
import static net.sourceforge.pmd.lang.java.types.JPrimitiveType.PrimitiveTypeKind.CHAR;
10
import static net.sourceforge.pmd.lang.java.types.JPrimitiveType.PrimitiveTypeKind.DOUBLE;
11
import static net.sourceforge.pmd.lang.java.types.JPrimitiveType.PrimitiveTypeKind.FLOAT;
12
import static net.sourceforge.pmd.lang.java.types.JPrimitiveType.PrimitiveTypeKind.LONG;
13
import static net.sourceforge.pmd.lang.java.types.JPrimitiveType.PrimitiveTypeKind.SHORT;
14

15
import java.util.ArrayList;
16
import java.util.List;
17

18
import org.checkerframework.checker.nullness.qual.Nullable;
19

20
import net.sourceforge.pmd.lang.java.types.internal.infer.InferenceVar;
21
import net.sourceforge.pmd.util.CollectionUtil;
22

23
/**
24
 * Utility class for type conversions, as defined in <a href="https://docs.oracle.com/javase/specs/jls/se10/html/jls-5.html">JLS§5</a>.
25
 */
26
@SuppressWarnings("PMD.CompareObjectsWithEquals")
27
public final class TypeConversion {
28

29
    private TypeConversion() {
30

31
    }
32

33
    /**
34
     * Performs <a href="https://docs.oracle.com/javase/specs/jls/se9/html/jls-5.html#jls-5.6.1">Unary numeric promotion
35
     * (JLS§5.6.1)</a>.
36
     * <p>This occurs in the following situations:
37
     * <ul>
38
     * <li>Each dimension expression in an array creation expression (§15.10.1)
39
     * <li>The index expression in an array access expression (§15.10.3)
40
     * <li>The operand of a unary plus operator + (§15.15.3)
41
     * <li>The operand of a unary minus operator - (§15.15.4)
42
     * <li>The operand of a bitwise complement operator ~ (§15.15.5)
43
     * <li>Each operand, separately, of a shift operator &lt;&lt;, &gt;&gt;, or &gt;&gt;&gt; (§15.19).
44
     * </ul>
45
     *
46
     * <p>Returns {@link TypeSystem#ERROR} if the given type is
47
     * not a numeric type, {@link TypeSystem#UNKNOWN} if the type
48
     * is unresolved.
49
     */
50
    public static JTypeMirror unaryNumericPromotion(JTypeMirror t) {
51
        t = t.unbox();
1✔
52

53
        TypeSystem ts = t.getTypeSystem();
1✔
54

55
        if (t.isPrimitive(BYTE) || t.isPrimitive(SHORT) || t.isPrimitive(CHAR)) {
1✔
56
            return ts.INT;
1✔
57
        }
58

59
        return t.isNumeric() || t == ts.UNKNOWN ? t : ts.ERROR;
1✔
60
    }
61

62
    /**
63
     * JLS§5.6.2
64
     * https://docs.oracle.com/javase/specs/jls/se9/html/jls-5.html#jls-5.6.2
65
     *
66
     * Binary numeric promotion is performed on the operands of certain operators:
67
     * <ul>
68
     * <li>The multiplicative operators *, /, and % (§15.17)
69
     * <li>The addition and subtraction operators for numeric types + and - (§15.18.2)
70
     * <li>The numerical comparison operators &lt;, &lt;=, &gt;, and &gt;= (§15.20.1)
71
     * <li>The numerical equality operators == and != (§15.21.1)
72
     * <li>The integer bitwise operators &amp;, ^, and | (§15.22.1)
73
     * <li>In certain cases, the conditional operator ? : (§15.25)
74
     * </ul>
75
     * <p>Returns {@link TypeSystem#ERROR} if either of the parameters
76
     * is not numeric. This DOES NOT care for unresolved types.
77
     */
78
    public static JTypeMirror binaryNumericPromotion(JTypeMirror t, JTypeMirror s) {
79
        JTypeMirror t1 = t.unbox();
1✔
80
        JTypeMirror s1 = s.unbox();
1✔
81

82
        TypeSystem ts = t.getTypeSystem();
1✔
83

84
        if (t1.isPrimitive(DOUBLE) || s1.isPrimitive(DOUBLE)) {
1✔
85
            return ts.DOUBLE;
1✔
86
        } else if (t1.isPrimitive(FLOAT) || s1.isPrimitive(FLOAT)) {
1✔
87
            return ts.FLOAT;
1✔
88
        } else if (t1.isPrimitive(LONG) || s1.isPrimitive(LONG)) {
1✔
89
            return ts.LONG;
1✔
90
        } else if (t1.isNumeric() && s1.isNumeric()) {
1✔
91
            return ts.INT;
1✔
92
        } else {
93
            // this is a typing error, both types should be referring to a numeric type
94
            return ts.ERROR;
1✔
95
        }
96
    }
97

98
    /**
99
     * Is t convertible to s by boxing/unboxing/widening conversion?
100
     * Only t can undergo conversion.
101
     */
102
    public static boolean isConvertibleUsingBoxing(JTypeMirror t, JTypeMirror s) {
103
        return isConvertibleCommon(t, s, false);
1✔
104
    }
105

106
    /**
107
     * Is t convertible to s by boxing/unboxing conversion?
108
     * Only t can undergo conversion.
109
     */
110
    public static boolean isConvertibleInCastContext(JTypeMirror t, JTypeMirror s) {
111
        return isConvertibleCommon(t, s, true);
1✔
112
    }
113

114
    private static boolean isConvertibleCommon(JTypeMirror t, JTypeMirror s, boolean isCastContext) {
115
        TypeSystem ts = t.getTypeSystem();
1✔
116
        if (t == ts.UNKNOWN || t == ts.ERROR) {
1✔
117
            return true;
×
118
        }
119

120
        if (t instanceof InferenceVar || s instanceof InferenceVar) {
1✔
121
            return t.box().isSubtypeOf(s.box());
×
122
        }
123

124
        if (t.isPrimitive() == s.isPrimitive()) {
1✔
125
            return t.isConvertibleTo(s).bySubtyping();
1✔
126
        }
127

128
        if (isCastContext) {
1✔
129
            return t.isPrimitive() ? t.box().isConvertibleTo(s).bySubtyping()
1✔
130
                                   : t.isConvertibleTo(s.box()).bySubtyping();
1✔
131
        } else {
132
            return t.isPrimitive() ? t.box().isConvertibleTo(s).somehow()
1✔
133
                                   : t.unbox().isConvertibleTo(s).somehow();
1✔
134
        }
135
    }
136

137

138
    /**
139
     * Perform capture conversion on the type t. This replaces wildcards
140
     * with fresh type variables. Capture conversion is not applied recursively.
141
     * Capture conversion on any type other than a parameterized type (§4.5) acts
142
     * as an identity conversion (§5.1.1).
143
     *
144
     * @return The capture conversion of t
145
     */
146
    public static JTypeMirror capture(JTypeMirror t) {
147
        return t instanceof JClassType ? capture((JClassType) t) : t;
1✔
148
    }
149

150
    /**
151
     * Perform capture conversion on the type t. This replaces wildcards
152
     * with fresh type variables. Capture conversion is not applied recursively.
153
     * Capture conversion on any type other than a parameterized type (§4.5) acts
154
     * as an identity conversion (§5.1.1).
155
     *
156
     * @return The capture conversion of t
157
     */
158
    public static JClassType capture(JClassType type) {
159
        if (type == null) {
1✔
160
            return null;
1✔
161
        }
162

163
        final @Nullable JClassType enclosing = capture(type.getEnclosingType());
1✔
164
        if (enclosing == type.getEnclosingType() && !isWilcardParameterized(type)) {
1✔
165
            return type; // 99% take this path
1✔
166
        }
167

168
        TypeSystem ts = type.getTypeSystem();
1✔
169
        List<JTypeMirror> typeArgs = type.getTypeArgs();
1✔
170
        List<JTypeVar> typeParams = type.getFormalTypeParams();
1✔
171

172
        // This is the algorithm described at https://docs.oracle.com/javase/specs/jls/se10/html/jls-5.html#jls-5.1.10
173

174
        // Let G name a generic type declaration (§8.1.2, §9.1.2)
175
        // with n type parameters A1,...,An with corresponding bounds U1,...,Un.
176

177
        // There exists a capture conversion from a parameterized type G<T1,...,Tn> (§4.5)
178
        // to a parameterized type G<S1,...,Sn>, where, for 1 ≤ i ≤ n :
179
        // -> see the loop
180

181
        // typeParams is A1..An
182
        // typeArgs is T1..Tn
183
        // freshVars is S1..Sn
184

185
        List<JTypeMirror> freshVars = makeFreshVars(type);
1✔
186

187
        // types may be non-well formed if the symbol is unresolved
188
        // in this case the typeParams list is most likely empty
189
        boolean wellFormed = typeParams.size() == freshVars.size();
1✔
190

191
        // Map of Ai to Si, for the substitution
192
        Substitution subst = wellFormed ? Substitution.mapping(typeParams, freshVars) : Substitution.EMPTY;
1✔
193

194
        for (int i = 0; i < typeArgs.size(); i++) {
1✔
195
            JTypeMirror fresh = freshVars.get(i);       // Si
1✔
196
            JTypeMirror arg = typeArgs.get(i);          // Ti
1✔
197

198
            // we mutate the bounds to preserve the correct instance in
199
            // the substitutions
200

201
            if (arg instanceof JWildcardType) {
1✔
202
                JWildcardType w = (JWildcardType) arg;        // Ti alias
1✔
203
                TypeVarImpl.CapturedTypeVar freshVar = (TypeVarImpl.CapturedTypeVar) fresh; // Si alias
1✔
204

205
                JTypeMirror prevUpper = wellFormed ? typeParams.get(i).getUpperBound() : ts.OBJECT; // Ui
1✔
206
                JTypeMirror substituted = TypeOps.subst(prevUpper, subst);
1✔
207

208
                if (w.isUnbounded()) {
1✔
209
                    // If Ti is a wildcard type argument (§4.5.1) of the form ?,
210
                    // then Si is a fresh type variable whose upper bound is Ui[A1:=S1,...,An:=Sn]
211
                    // and whose lower bound is the null type (§4.1).
212

213
                    freshVar.setUpperBound(substituted);
1✔
214
                    freshVar.setLowerBound(ts.NULL_TYPE);
1✔
215

216
                } else if (w.isUpperBound()) {
1✔
217
                    // If Ti is a wildcard type argument of the form ? extends Bi,
218
                    // then Si is a fresh type variable whose upper bound is glb(Bi, Ui[A1:=S1,...,An:=Sn])
219
                    // and whose lower bound is the null type.
220
                    freshVar.setUpperBound(ts.glb(asList(substituted, w.getBound())));
1✔
221
                    freshVar.setLowerBound(ts.NULL_TYPE);
1✔
222

223
                } else {
224
                    // If Ti is a wildcard type argument of the form ? super Bi,
225
                    // then Si is a fresh type variable whose upper bound is Ui[A1:=S1,...,An:=Sn]
226
                    // and whose lower bound is Bi.
227
                    freshVar.setUpperBound(substituted);
1✔
228
                    freshVar.setLowerBound(w.getBound());
1✔
229
                }
230
            }
231
        }
232

233
        if (enclosing != null) {
1✔
234
            return enclosing.selectInner(type.getSymbol(), freshVars);
1✔
235
        } else {
236
            return type.withTypeArguments(freshVars);
1✔
237
        }
238
    }
239

240
    /**
241
     * Returns true if the type is a parameterized class type, which has
242
     * wildcards as type arguments. Capture variables don't count.
243
     */
244
    public static boolean isWilcardParameterized(JTypeMirror t) {
245
        return t instanceof JClassType
1✔
246
                && CollectionUtil.any(((JClassType) t).getTypeArgs(), it -> it instanceof JWildcardType);
1✔
247
    }
248

249

250
    private static List<JTypeMirror> makeFreshVars(JClassType type) {
251
        List<JTypeMirror> freshVars = new ArrayList<>(type.getTypeArgs().size());
1✔
252
        for (JTypeMirror typeArg : type.getTypeArgs()) {
1✔
253
            if (typeArg instanceof JWildcardType) {
1✔
254
                freshVars.add(TypeVarImpl.freshCapture((JWildcardType) typeArg));
1✔
255
            } else {
256
                freshVars.add(typeArg);
1✔
257
            }
258
        }
1✔
259
        return freshVars;
1✔
260
    }
261

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