• 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

79.0
/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/internal/infer/InferenceVar.java
1
/*
2
 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3
 */
4

5

6
package net.sourceforge.pmd.lang.java.types.internal.infer;
7

8
import java.util.Collections;
9
import java.util.EnumMap;
10
import java.util.EnumSet;
11
import java.util.LinkedHashSet;
12
import java.util.Map;
13
import java.util.Map.Entry;
14
import java.util.Set;
15
import java.util.function.Function;
16

17
import org.checkerframework.checker.nullness.qual.NonNull;
18
import org.checkerframework.checker.nullness.qual.Nullable;
19
import org.pcollections.HashTreePSet;
20
import org.pcollections.PSet;
21

22
import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol;
23
import net.sourceforge.pmd.lang.java.symbols.SymbolicValue.SymAnnot;
24
import net.sourceforge.pmd.lang.java.types.JTypeMirror;
25
import net.sourceforge.pmd.lang.java.types.JTypeVar;
26
import net.sourceforge.pmd.lang.java.types.JTypeVisitor;
27
import net.sourceforge.pmd.lang.java.types.SubstVar;
28
import net.sourceforge.pmd.lang.java.types.TypePrettyPrint;
29
import net.sourceforge.pmd.lang.java.types.TypeSystem;
30

31
/**
32
 * Represents an inference variable. Inference variables are just
33
 * placeholder for types, used during the inference process.
34
 * After type inference they should have been erased and hence this
35
 * type is of no importance outside the implementation of this framework.
36
 */
37
@SuppressWarnings("PMD.CompareObjectsWithEquals")
38
public final class InferenceVar implements JTypeMirror, SubstVar {
39

40
    // we used to use greek letters (for style), but they're hard to type
41
    private static final String NAMES = "abcdefghijklmnopqrstuvwxyz"; // + "αβγδεζηθκλμνξπρςυφχψω"
42

43
    private final InferenceContext ctx;
44
    private JTypeVar tvar;
45
    private final int id;
46

47
    // Equal ivars share the same BoundSet
48
    private BoundSet boundSet = new BoundSet();
1✔
49
    private boolean hasNonTrivialBound;
50

51
    InferenceVar(InferenceContext ctx, JTypeVar tvar, int id) {
1✔
52
        this.ctx = ctx;
1✔
53
        this.tvar = tvar;
1✔
54
        this.id = id;
1✔
55
    }
1✔
56

57
    @Override
58
    public JTypeMirror withAnnotations(PSet<SymAnnot> newTypeAnnots) {
59
        return this;
×
60
    }
61

62
    @Override
63
    public PSet<SymAnnot> getTypeAnnotations() {
64
        return HashTreePSet.empty();
×
65
    }
66

67
    public String getName() {
68
        // note: the type inference logger depends on this naming pattern
69
        String prefix = isCaptured() ? "^" : "'";
1✔
70
        return prefix + NAMES.charAt(id % NAMES.length()) + generationNum();
1✔
71
    }
72

73
    @Override
74
    public TypeSystem getTypeSystem() {
75
        return ctx.ts;
1✔
76
    }
77

78
    /**
79
     * Returns the bounds of a certain kind that apply to
80
     * this variable.
81
     */
82
    Set<JTypeMirror> getBounds(BoundKind kind) {
83
        return boundSet.bounds.getOrDefault(kind, Collections.emptySet());
1✔
84
    }
85

86

87
    Set<JTypeMirror> getBounds(Set<BoundKind> kinds) {
88
        Set<JTypeMirror> bounds = new LinkedHashSet<>();
1✔
89
        for (BoundKind k : kinds) {
1✔
90
            bounds.addAll(getBounds(k));
1✔
91
        }
1✔
92
        return bounds;
1✔
93
    }
94

95

96
    /**
97
     * Adds a new bound on this variable.
98
     */
99
    public void addBound(BoundKind kind, JTypeMirror type) {
100
        this.hasNonTrivialBound = true;
1✔
101
        addBound(kind, type, false);
1✔
102
    }
1✔
103

104
    public void addPrimaryBound(BoundKind kind, JTypeMirror type) {
105
        addBound(kind, type, true);
1✔
106
    }
1✔
107

108
    /**
109
     * @param isPrimaryBound Whether this is the default bound conferred
110
     *                       by the bound on a type parameter declaration.
111
     *                       This is treated specially by java 7 inference.
112
     */
113
    private void addBound(BoundKind kind, JTypeMirror type, boolean isPrimaryBound) {
114
        if (this.isEquivalentTo(type)) {
1✔
115
            // may occur because of transitive propagation
116
            // alpha <: alpha is always true and not interesting
117
            return;
1✔
118
        }
119

120
        if (boundSet.bounds.computeIfAbsent(kind, k -> new LinkedHashSet<>()).add(type)) {
1✔
121
            ctx.onBoundAdded(this, kind, type, isPrimaryBound);
1✔
122
        }
123
    }
1✔
124

125
    /**
126
     * Returns true if the node has no bounds except the ones given
127
     * by the upper bound of the type parameter. In the Java 7 inference
128
     * process, this indicates that we should use additional constraints
129
     * binding the return type of the method to the target type (determined by
130
     * an assignment context).
131
     *
132
     * <p>Remove this if you remove support for java 7 at some point.
133
     */
134
    boolean hasOnlyPrimaryBound() {
135
        return !hasNonTrivialBound;
1✔
136
    }
137

138
    /**
139
     * Returns the instantiation of this inference variable if
140
     * it has already been determined. Returns null otherwise.
141
     */
142
    @Nullable
143
    JTypeMirror getInst() {
144
        return boundSet.inst;
1✔
145
    }
146

147

148
    void setInst(JTypeMirror inst) {
149
        this.boundSet.inst = inst;
1✔
150
    }
1✔
151

152
    /**
153
     * Apply a substitution to the bounds of this variable. Called when
154
     * an ivar is instantiated.
155
     *
156
     * @param substitution The substitution to apply
157
     */
158
    void substBounds(Function<? super SubstVar, ? extends JTypeMirror> substitution) {
159

160
        for (Entry<BoundKind, Set<JTypeMirror>> entry : boundSet.bounds.entrySet()) {
1✔
161
            BoundKind kind = entry.getKey();
1✔
162
            Set<JTypeMirror> prevBounds = entry.getValue();
1✔
163

164

165
            // put the new bounds before updating
166
            Set<JTypeMirror> newBounds = new LinkedHashSet<>();
1✔
167
            boundSet.bounds.put(kind, newBounds);
1✔
168

169
            for (JTypeMirror prev : prevBounds) {
1✔
170
                // add substituted bound
171
                JTypeMirror newBound = prev.subst(substitution);
1✔
172
                if (newBound == prev || prevBounds.contains(newBound)) { // NOPMD CompareObjectsWithEquals
1✔
173
                    // not actually new, don't call listeners, etc
174
                    newBounds.add(newBound);
1✔
175
                } else {
176
                    addBound(kind, newBound);
1✔
177
                }
178
            }
1✔
179
        }
1✔
180

181
        if (tvar.isCaptured()) {
1✔
182
            tvar = tvar.substInBounds(substitution);
1✔
183
        }
184
    }
1✔
185

186
    JTypeVar getBaseVar() {
187
        return tvar;
1✔
188
    }
189

190

191
    boolean isCaptured() {
192
        return tvar.isCaptured();
1✔
193
    }
194

195
    public boolean isEquivalentTo(JTypeMirror t) {
196
        return this == t || t instanceof InferenceVar
1✔
197
            && ((InferenceVar) t).boundSet == this.boundSet; // NOPMD CompareObjectsWithEquals
198
    }
199

200
    public boolean isSubtypeNoSideEffect(@NonNull JTypeMirror other) {
201
        return isEquivalentTo(other) || other.isTop();
×
202
    }
203

204
    public boolean isSupertypeNoSideEffect(@NonNull JTypeMirror other) {
205
        return isEquivalentTo(other) || other.isBottom();
×
206
    }
207

208
    /**
209
     * Sets the bounds of this ivar and the other to the union of both sets.
210
     */
211
    void adoptAllBounds(InferenceVar candidate) {
212
        if (isEquivalentTo(candidate)) {
1✔
213
            return;
×
214
        }
215

216
        for (BoundKind kind : BoundKind.values()) {
1✔
217
            for (JTypeMirror bound : candidate.getBounds(kind)) {
1✔
218
                addBound(kind, bound);
1✔
219
            }
1✔
220
        }
221
        candidate.boundSet = this.boundSet;
1✔
222
        ctx.onIvarMerged(candidate, this);
1✔
223
    }
1✔
224

225
    @Override
226
    public @Nullable JTypeDeclSymbol getSymbol() {
227
        JTypeMirror inst = getInst();
×
228
        return inst != null ? inst.getSymbol()
×
229
                            : new InferenceVarSym(ctx.ts, this);
×
230
    }
231

232

233
    @Override
234
    public JTypeMirror subst(Function<? super SubstVar, ? extends @NonNull JTypeMirror> subst) {
235
        return subst.apply(this);
1✔
236
    }
237

238
    @Override
239
    public <T, P> T acceptVisitor(JTypeVisitor<T, P> visitor, P p) {
240
        return visitor.visitInferenceVar(this, p);
1✔
241
    }
242

243

244
    @Override
245
    public String toString() {
246
        return TypePrettyPrint.prettyPrint(this);
1✔
247
    }
248

249
    private String generationNum() {
250
        int n = id / NAMES.length();
1✔
251
        return n == 0 ? "" : "" + n;
1✔
252
    }
253

254
    StringBuilder formatBounds(StringBuilder sb) {
255
        sb.append(" {");
×
256
        boolean any = false;
×
257
        for (BoundKind bk : BoundKind.ALL) {
×
258
            for (JTypeMirror bound : getBounds(bk)) {
×
259
                sb.append(any ? ", " : " ").append(bk.format(this, bound));
×
260
                any = true;
×
261
            }
×
262
        }
×
263
        sb.append(any ? " }" : "}");
×
264
        return sb;
×
265
    }
266

267

268
    public enum BoundKind {
1✔
269
        UPPER(" <: ") {
1✔
270
            @Override
271
            public BoundKind complement() {
272
                return LOWER;
1✔
273
            }
274

275
            @Override
276
            public Set<BoundKind> complementSet(boolean eqIsAll) {
277
                return EQ_LOWER;
1✔
278
            }
279

280
        },
281
        EQ(" = ") {
1✔
282
            @Override
283
            public BoundKind complement() {
284
                return this;
1✔
285
            }
286

287
            @Override
288
            public Set<BoundKind> complementSet(boolean eqIsAll) {
289
                return eqIsAll ? ALL : JUST_EQ;
1✔
290
            }
291
        },
292
        LOWER(" >: ") {
1✔
293
            @Override
294
            public BoundKind complement() {
295
                return UPPER;
1✔
296
            }
297

298
            @Override
299
            public Set<BoundKind> complementSet(boolean eqIsAll) {
300
                return EQ_UPPER;
1✔
301
            }
302
        };
303

304
        // These sets are shared because otherwise *literal millions* of enumsets are created, with the same constants
305
        static final Set<BoundKind> ALL = EnumSet.allOf(BoundKind.class);
1✔
306
        static final Set<BoundKind> EQ_LOWER = EnumSet.of(EQ, LOWER);
1✔
307
        private static final Set<BoundKind> EQ_UPPER = EnumSet.of(EQ, UPPER);
1✔
308
        private static final Set<BoundKind> JUST_EQ = Collections.singleton(EQ);
1✔
309

310
        private final String sym;
311

312
        BoundKind(String sym) {
1✔
313
            this.sym = sym;
1✔
314
        }
1✔
315

316
        public String format(JTypeMirror ivar, JTypeMirror bound) {
317
            return ivar + sym + bound;
×
318
        }
319

320
        /**
321
         * Returns the complementary bound kind.
322
         * <pre>
323
         *     complement(LOWER) = UPPER
324
         *     complement(UPPER) = LOWER
325
         *     complement(EQ) = EQ
326
         * </pre>
327
         */
328
        public abstract BoundKind complement();
329

330

331
        /**
332
         * Returns the complement of this kind. There's two ways to complement EQ:
333
         * - With eqIsAll, this returns all constants.
334
         * - Otherwise this returns just EQ.
335
         */
336
        public abstract Set<BoundKind> complementSet(boolean eqIsAll);
337

338
        String getSym() {
339
            return sym;
×
340
        }
341

342
        @Override
343
        public String toString() {
344
            return sym;
×
345
        }
346
    }
347

348
    /** Equal inference vars share the same boundset. */
349
    private static final class BoundSet {
1✔
350

351
        JTypeMirror inst;
352
        Map<BoundKind, Set<JTypeMirror>> bounds = new EnumMap<>(BoundKind.class);
1✔
353

354
    }
355
}
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