• 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

75.0
/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/JIntersectionType.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;
7

8
import java.util.Collection;
9
import java.util.Collections;
10
import java.util.List;
11
import java.util.Objects;
12
import java.util.function.Function;
13
import java.util.function.Predicate;
14
import java.util.stream.Collectors;
15
import java.util.stream.Stream;
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.annotation.Experimental;
23
import net.sourceforge.pmd.lang.java.symbols.JClassSymbol;
24
import net.sourceforge.pmd.lang.java.symbols.JMethodSymbol;
25
import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol;
26
import net.sourceforge.pmd.lang.java.symbols.SymbolicValue.SymAnnot;
27
import net.sourceforge.pmd.util.CollectionUtil;
28

29
/**
30
 * An intersection type. Intersections type act as the
31
 * {@linkplain TypeSystem#glb(Collection) greatest lower bound}
32
 * for a set of types.
33
 *
34
 * <p>https://docs.oracle.com/javase/specs/jls/se8/html/jls-4.html#jls-4.9
35
 */
36
@SuppressWarnings("PMD.CompareObjectsWithEquals")
1✔
37
public final class JIntersectionType implements JTypeMirror {
38

39

40
    private final TypeSystem ts;
41
    private final JTypeMirror primaryBound;
42
    private final List<JTypeMirror> components;
43
    private JClassType induced;
44

45
    /**
46
     * @param primaryBound may be Object if every bound is an interface
47
     * @param allBounds    including the superclass, unless Object
48
     */
49
    JIntersectionType(TypeSystem ts,
50
                      JTypeMirror primaryBound,
51
                      List<? extends JTypeMirror> allBounds) {
1✔
52
        this.primaryBound = primaryBound;
1✔
53
        this.components = Collections.unmodifiableList(allBounds);
1✔
54
        this.ts = ts;
1✔
55

56
        assert Lub.isExclusiveIntersectionBound(primaryBound)
1✔
57
            : "Wrong primary intersection bound: " + toString(primaryBound, allBounds);
×
58
        assert primaryBound != ts.OBJECT || allBounds.size() > 1
1✔
59
            : "Intersection of a single bound: " + toString(primaryBound, allBounds); // should be caught by GLB
×
60

61
        checkWellFormed(primaryBound, allBounds);
1✔
62

63
    }
1✔
64

65
    @Override
66
    public PSet<SymAnnot> getTypeAnnotations() {
67
        return HashTreePSet.empty();
1✔
68
    }
69

70
    @Override
71
    public JTypeMirror withAnnotations(PSet<SymAnnot> newTypeAnnots) {
72
        return new JIntersectionType(
×
73
            ts,
74
            primaryBound.withAnnotations(newTypeAnnots),
×
75
            CollectionUtil.map(components, c -> c.withAnnotations(newTypeAnnots))
×
76
        );
77
    }
78

79
    /**
80
     * Returns the list of components. Their erasure must be pairwise disjoint.
81
     * If the intersection's superclass is {@link TypeSystem#OBJECT},
82
     * then it is excluded from this set.
83
     */
84
    public List<JTypeMirror> getComponents() {
85
        return components;
1✔
86
    }
87

88

89
    /**
90
     * The primary bound of this intersection, which may be a type variable,
91
     * array type, or class type (not an interface). If all bounds are interfaces,
92
     * then this returns {@link TypeSystem#OBJECT}.
93
     */
94
    public @NonNull JTypeMirror getPrimaryBound() {
95
        return primaryBound;
1✔
96
    }
97

98

99
    /**
100
     * Returns all additional bounds on the primary bound, which are
101
     * necessarily interface types.
102
     */
103
    @SuppressWarnings({"unchecked", "rawtypes"}) // safe because of checkWellFormed
104
    public @NonNull List<JClassType> getInterfaces() {
105
        return (List) (primaryBound == ts.OBJECT ? components
1✔
106
                                                 : components.subList(1, components.size()));
1✔
107
    }
108

109
    /**
110
     * Every intersection type induces a notional class or interface
111
     * for the purpose of identifying its members. This may be a functional
112
     * interface. This returns null for the non-implemented cases.
113
     *
114
     * @experimental this is only relevant to check for functional
115
     *     interface parameterization, eg {@code Runnable & Serializable}. Do
116
     *     not use this to find out the members of this type, rather, use {@link #streamMethods(Predicate)}
117
     *     or so.
118
     */
119
    @Experimental
120
    public @Nullable JClassType getInducedClassType() {
121
        JTypeMirror primary = getPrimaryBound();
1✔
122
        if (primary instanceof JTypeVar || primary instanceof JArrayType) {
1✔
123
            // Normally, should generate an interface which has all the members of Ti
124
            // But as per the experimental notice, this case may be ignored until needed
125
            return null;
1✔
126
        }
127

128
        if (induced == null) {
1✔
129
            JClassSymbol sym = new FakeIntersectionSymbol("", (JClassType) primary, getInterfaces());
1✔
130
            this.induced = (JClassType) ts.declaration(sym);
1✔
131
        }
132
        return induced;
1✔
133
    }
134

135
    @Override
136
    public <T, P> T acceptVisitor(JTypeVisitor<T, P> visitor, P p) {
137
        return visitor.visitIntersection(this, p);
1✔
138
    }
139

140

141
    @Override
142
    public Stream<JMethodSig> streamMethods(Predicate<? super JMethodSymbol> prefilter) {
143
        return getComponents().stream().flatMap(it -> it.streamMethods(prefilter));
×
144
    }
145

146

147
    @Override
148
    public JIntersectionType subst(Function<? super SubstVar, ? extends @NonNull JTypeMirror> subst) {
149
        JTypeMirror newPrimary = primaryBound.subst(subst);
1✔
150
        List<JClassType> myItfs = getInterfaces();
1✔
151
        List<JClassType> newBounds = TypeOps.substClasses(myItfs, subst);
1✔
152
        return newPrimary == getPrimaryBound() && newBounds == myItfs // NOPMD UseEqualsToCompareObjectReferences
1✔
153
               ? this
1✔
154
               : new JIntersectionType(ts, newPrimary, newBounds);
×
155
    }
156

157
    @Override
158
    public @Nullable JTypeDeclSymbol getSymbol() {
159
        return null; // the induced type may have a symbol though
1✔
160
    }
161

162
    @Override
163
    public TypeSystem getTypeSystem() {
164
        return ts;
1✔
165
    }
166

167

168
    @Override
169
    public JTypeMirror getErasure() {
170
        return getPrimaryBound().getErasure();
×
171
    }
172

173
    @Override
174
    public String toString() {
175
        return TypePrettyPrint.prettyPrint(this);
1✔
176
    }
177

178
    @Override
179
    public boolean equals(Object o) {
180
        if (this == o) {
1✔
181
            return true;
×
182
        }
183
        if (!(o instanceof JIntersectionType)) {
1✔
184
            return false;
1✔
185
        }
186
        JIntersectionType that = (JIntersectionType) o;
1✔
187
        return TypeOps.isSameType(this, that);
1✔
188
    }
189

190
    @Override
191
    public int hashCode() {
192
        return Objects.hash(components);
1✔
193
    }
194

195
    private static void checkWellFormed(JTypeMirror primary, List<? extends JTypeMirror> flattened) {
196
        assert flattened.get(0) == primary || primary == primary.getTypeSystem().OBJECT
1✔
197
            : "Not a well-formed intersection " + flattened;
198
        for (int i = 0; i < flattened.size(); i++) {
1✔
199
            JTypeMirror ci = flattened.get(i);
1✔
200
            Objects.requireNonNull(ci, "Null intersection component");
1✔
201
            if (Lub.isExclusiveIntersectionBound(ci)) {
1✔
202
                if (i != 0) {
1✔
203
                    throw malformedIntersection(primary, flattened);
×
204
                }
205
            } else if (ci instanceof JClassType) {
1✔
206
                // must be an interface, as per isExclusiveBlabla
207
                assert ci.isInterface() || TypeOps.hasUnresolvedSymbol(ci);
1✔
208
            } else {
209
                throw malformedIntersection(primary, flattened);
×
210
            }
211
        }
212
    }
1✔
213

214
    private static RuntimeException malformedIntersection(JTypeMirror primary, List<? extends JTypeMirror> flattened) {
215
        return new IllegalArgumentException(
×
216
            "Malformed intersection: " + toString(primary, flattened)
×
217
        );
218
    }
219

220
    private static String toString(JTypeMirror primary, List<? extends JTypeMirror> flattened) {
221
        return flattened.stream().map(JTypeMirror::toString).collect(Collectors.joining(" & ",
×
222
                                                                                        primary.toString() + " & ",
×
223
                                                                                        ""));
224
    }
225
}
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