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

pmd / pmd / 4476

27 Feb 2025 08:31AM UTC coverage: 77.717% (+0.02%) from 77.694%
4476

push

github

adangel
[apex] New Rule: Avoid Stateful Database Results (#5425)

Merge pull request #5425 from mitchspano:stateful

17395 of 23322 branches covered (74.59%)

Branch coverage included in aggregate %.

22 of 22 new or added lines in 1 file covered. (100.0%)

99 existing lines in 9 files now uncovered.

38180 of 48187 relevant lines covered (79.23%)

0.8 hits per line

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

83.84
/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/internal/infer/PhaseOverloadSet.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.internal.infer;
6

7
import static net.sourceforge.pmd.lang.java.types.TypeOps.areOverrideEquivalent;
8
import static net.sourceforge.pmd.lang.java.types.internal.InternalMethodTypeItf.cast;
9
import static net.sourceforge.pmd.lang.java.types.internal.infer.ExprMirror.TypeSpecies.getSpecies;
10
import static net.sourceforge.pmd.util.OptionalBool.NO;
11
import static net.sourceforge.pmd.util.OptionalBool.UNKNOWN;
12
import static net.sourceforge.pmd.util.OptionalBool.YES;
13
import static net.sourceforge.pmd.util.OptionalBool.definitely;
14

15
import java.util.List;
16

17
import org.checkerframework.checker.nullness.qual.NonNull;
18

19
import net.sourceforge.pmd.lang.java.types.InternalApiBridge;
20
import net.sourceforge.pmd.lang.java.types.JMethodSig;
21
import net.sourceforge.pmd.lang.java.types.JTypeMirror;
22
import net.sourceforge.pmd.lang.java.types.JTypeVar;
23
import net.sourceforge.pmd.lang.java.types.Substitution;
24
import net.sourceforge.pmd.lang.java.types.TypeConversion;
25
import net.sourceforge.pmd.lang.java.types.TypeOps;
26
import net.sourceforge.pmd.lang.java.types.internal.infer.ExprMirror.BranchingMirror;
27
import net.sourceforge.pmd.lang.java.types.internal.infer.ExprMirror.InvocationMirror.MethodCtDecl;
28
import net.sourceforge.pmd.lang.java.types.internal.infer.ExprMirror.LambdaExprMirror;
29
import net.sourceforge.pmd.lang.java.types.internal.infer.ExprMirror.MethodRefMirror;
30
import net.sourceforge.pmd.lang.java.types.internal.infer.ExprMirror.TypeSpecies;
31
import net.sourceforge.pmd.util.OptionalBool;
32

33
/**
34
 * An {@link OverloadSet} that is specific to an invocation expression
35
 * and resolution phase. It selects the most specific methods using the
36
 * actual values of arguments, as specified in https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.12.2.5.
37
 */
38
final class PhaseOverloadSet extends OverloadSet<MethodCtDecl> {
1✔
39
    // TODO by mocking a call site we may be able to remove that logic
40
    //  Basically infer m1 against m2 with the same core routines as the
41
    //  main Infer.
42
    // m1 is more specific than m2 if m2 can handle more calls than m1
43

44
    // so try to infer applicability of m2 for argument types == formal param types of m1
45
    // yeah but this would ignore the shape of lambdas, we'd only get constraints
46
    // like f1(m1) <: f1(m2), ignoring that a lambda may be compatible with both types
47

48
    private final Infer infer;
49
    private final MethodResolutionPhase phase;
50
    private final MethodCallSite site;
51

52

53
    PhaseOverloadSet(Infer infer, MethodResolutionPhase phase, MethodCallSite site) {
1✔
54
        this.infer = infer;
1✔
55
        this.phase = phase;
1✔
56
        this.site = site;
1✔
57
    }
1✔
58

59
    public MethodResolutionPhase getPhase() {
60
        return phase;
×
61
    }
62

63
    public MethodCallSite getSite() {
64
        return site;
×
65
    }
66

67
    public Infer getInfer() {
68
        return infer;
×
69
    }
70

71
    /**
72
     * It's a given that the method is applicable to the site.
73
     *
74
     * <p>https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.12.2.5
75
     */
76
    @Override
77
    @SuppressWarnings("PMD.UselessOverridingMethod")
78
    void add(MethodCtDecl sig) {
79
        super.add(sig);
1✔
80
    }
1✔
81

82
    public @NonNull MethodCtDecl getMostSpecificOrLogAmbiguity(TypeInferenceLogger logger) {
83
        assert nonEmpty();
1!
84
        List<MethodCtDecl> overloads = getOverloadsMutable();
1✔
85
        MethodCtDecl main = overloads.get(0);
1✔
86
        if (overloads.size() != 1) {
1✔
87
            logger.ambiguityError(site, main, overloads);
1✔
88
            main = main.asFailed();
1✔
89
        }
90
        return main;
1✔
91
    }
92

93
    @Override
94
    protected OptionalBool shouldTakePrecedence(MethodCtDecl m1, MethodCtDecl m2) {
95
        return isMoreSpecific(cast(m1.getMethodType()).adaptedMethod(),
1✔
96
                              cast(m2.getMethodType()).adaptedMethod());
1✔
97
    }
98

99

100
    private OptionalBool isMoreSpecific(@NonNull JMethodSig m1, @NonNull JMethodSig m2) {
101

102
        OptionalBool m1OverM2 = isMoreSpecificForExpr(m1, m2);
1✔
103

104
        if (m1OverM2.isKnown()) {
1✔
105
            return m1OverM2;
1✔
106
        } else if (areOverrideEquivalent(m1, m2)) {
1✔
107
            JTypeMirror observingSite = site.getExpr().getReceiverType();
1✔
108
            if (observingSite == null) {
1✔
109
                observingSite = site.getExpr().getEnclosingType();
1✔
110
            }
111
            return OverloadSet.shouldAlwaysTakePrecedence(m1, m2, observingSite);
1✔
112
        }
113

114
        return UNKNOWN;
1✔
115
    }
116

117
    private OptionalBool isMoreSpecificForExpr(JMethodSig m1, JMethodSig m2) {
118
        boolean m1OverM2 = isInferredMoreSpecific(m1, m2);
1✔
119
        boolean m2OverM1 = isInferredMoreSpecific(m2, m1);
1✔
120
        if (m1OverM2 ^ m2OverM1) {
1✔
121
            return definitely(m1OverM2);
1✔
122
        }
123
        return UNKNOWN;
1✔
124
    }
125

126
    private boolean isInferredMoreSpecific(JMethodSig m1, JMethodSig m2) {
127
        // https://docs.oracle.com/javase/specs/jls/se8/html/jls-18.html#jls-18.5.4
128
        try {
129
            return doInfer(m1, m2);
1✔
130
        } catch (ResolutionFailedException e) {
1✔
131
            return false;
1✔
132
        }
133
    }
134

135
    private boolean doInfer(JMethodSig m1, JMethodSig m2) {
136
        MethodCallSite site = this.site.cloneForSpecificityCheck(infer);
1✔
137
        InferenceContext ctx = infer.newContextFor(m2);
1✔
138

139
        // even if m1 is generic, the type parameters of m1 are treated as type variables, not inference variables.
140

141
        JMethodSig m2p = ctx.mapToIVars(m2);
1✔
142

143
        List<ExprMirror> es = site.getExpr().getArgumentExpressions();
1✔
144

145
        int k = es.size();
1✔
146

147
        for (int i = 0; i < k; i++) {
1✔
148
            JTypeMirror ti = m2p.ithFormalParam(i, phase.requiresVarargs());
1✔
149
            JTypeMirror si = m1.ithFormalParam(i, phase.requiresVarargs());
1✔
150
            ExprMirror ei = es.get(i);
1✔
151

152
            if (si.equals(ti)) {
1✔
153
                continue;
1✔
154
            }
155

156
            // needs to go before subtyping checks, because those will always succeed
157
            if (unresolvedTypeFallback(si, ti, ei) == NO) {
1✔
158
                return false;
1✔
159
            }
160

161
            if (si.isSubtypeOf(ti)) {
1✔
162
                return true;
1✔
163
            } else if (ti.isSubtypeOf(si)) {
1✔
164
                return false;
1✔
165
            }
166

167
            JMethodSig sfun = TypeOps.findFunctionalInterfaceMethod(si);
1✔
168
            JMethodSig tfun = TypeOps.findFunctionalInterfaceMethod(ti);
1✔
169
            if (sfun == null || tfun == null) {
1✔
170
                if (phase.canBox()) {
1✔
171
                    JTypeMirror stdExprTy = ei.getStandaloneType();
1✔
172
                    if (stdExprTy != null
1!
173
                        // there is a boxing or unboxing conversion happening
174
                        && stdExprTy.isPrimitive() != si.isPrimitive()
1✔
175
                        && stdExprTy.isPrimitive() != ti.isPrimitive()) {
1!
176
                        // si or ti is more specific if it only involves
177
                        // the boxing/unboxing conversion, without widening
178
                        // afterwards.
179
                        if (stdExprTy.box().equals(si.box())) {
1✔
180
                            return true;
1✔
181
                        } else if (stdExprTy.box().equals(ti.box())) {
1✔
182
                            return false;
1✔
183
                        }
184
                    }
185
                }
186

UNCOV
187
                infer.checkConvertibleOrDefer(ctx, si, ti, ei, phase, site);
×
UNCOV
188
                continue;
×
189
            }
190

191
            // otherwise they're both functional interfaces
192
            if (!isFunctionTypeMoreSpecific(ctx, si, sfun, tfun, ei, site)) {
1✔
193
                return false;
1✔
194
            }
195
        }
196

197
        if (phase.requiresVarargs() && m2p.getArity() == k + 1) {
1!
198
            // that is, the invocation has no arguments for the varargs, eg Stream.of()
199
            infer.checkConvertibleOrDefer(ctx,
1✔
200
                                          m1.ithFormalParam(k, true),
1✔
201
                                          m2p.ithFormalParam(k, true),
1✔
202
                                          // m2Formals.get(k),
203
                                          site.getExpr(), phase, site);
1✔
204
        }
205

206
        ctx.solve();
1✔
207
        ctx.callListeners();
1✔
208

209
        return true;
1✔
210
    }
211

212

213
    private @NonNull OptionalBool unresolvedTypeFallback(JTypeMirror si, JTypeMirror ti, ExprMirror argExpr) {
214
        JTypeMirror standalone = argExpr.getStandaloneType();
1✔
215
        if (TypeOps.hasUnresolvedSymbolOrArray(standalone)) {
1✔
216
            if (standalone.equals(si)) {
1✔
217
                return YES;
1✔
218
            } else if (standalone.equals(ti)) {
1✔
219
                return NO;
1✔
220
            }
221
        }
222
        return UNKNOWN;
1✔
223
    }
224

225
    private boolean isFunctionTypeMoreSpecific(InferenceContext ctx,
226
                                               JTypeMirror si,
227
                                               JMethodSig sfun,
228
                                               JMethodSig tfun,
229
                                               ExprMirror ei, MethodCallSite site) {
230
        if (sfun.getArity() != tfun.getArity()
1✔
231
            || sfun.getTypeParameters().size() != tfun.getTypeParameters().size()) {
1!
232
            return false;
1✔
233
        }
234

235
        // Note that the following is not implemented entirely
236

237
        // The rest is described in https://docs.oracle.com/javase/specs/jls/se13/html/jls-18.html#jls-18.5.4
238

239
        JMethodSig capturedSFun = TypeOps.findFunctionalInterfaceMethod(TypeConversion.capture(si));
1✔
240
        assert capturedSFun != null;
1!
241

242
        if (!TypeOps.haveSameTypeParams(capturedSFun, sfun)) {
1!
UNCOV
243
            return false;
×
244
        }
245

246

247
        List<JTypeVar> sparams = sfun.getTypeParameters();
1✔
248
        List<JTypeVar> tparams = tfun.getTypeParameters();
1✔
249
        Substitution tToS = Substitution.mapping(tparams, sparams);
1✔
250
        for (int j = 0; j < sparams.size(); j++) {
1!
UNCOV
251
            JTypeVar aj = sparams.get(j);
×
252
            JTypeVar bj = tparams.get(j);
×
253

254
            JTypeMirror x = aj.getUpperBound();
×
255
            JTypeMirror y = bj.getUpperBound();
×
UNCOV
256
            if (TypeOps.mentionsAny(x, sfun.getTypeParameters()) && !ctx.isGround(y)) {
×
257
                return false;
×
258
            } else {
UNCOV
259
                InternalApiBridge.isSameTypeInInference(x, y.subst(tToS)); // adds an equality constraint
×
260
            }
261
        }
262

263
        // todo something about formal params
264

265
        JTypeMirror rs = sfun.getReturnType();
1✔
266
        JTypeMirror rt = tfun.getReturnType();
1✔
267
        return (!TypeOps.mentionsAny(rs, sparams) || ctx.isGround(rt))
1!
268
            && addGenericExprConstraintsRecursive(ctx, ei, rs, rt, tToS, site);
1✔
269
    }
270

271
    private boolean addGenericExprConstraintsRecursive(InferenceContext ctx, ExprMirror ei, JTypeMirror rs, JTypeMirror rt, Substitution tToS, MethodCallSite site) {
272
        if (ei instanceof LambdaExprMirror) {
1✔
273
            // Otherwise, if ei is an explicitly typed lambda expression:
274
            //
275
            //    If RT is void, true.
276
            //
277
            //    todo: Otherwise, if RS and RT are functional interface types, and ei has at least one result expression, then for each result expression in ei, this entire second step is repeated to infer constraints under which RS is more specific than RT θ' for the given result expression.
278
            //
279
            //    Otherwise, if RS is a primitive type and RT is not, and ei has at least one result expression, and each result expression of ei is a standalone expression (§15.2) of a primitive type, true.
280
            //
281
            //    Otherwise, if RT is a primitive type and RS is not, and ei has at least one result expression, and each result expression of ei is either a standalone expression of a reference type or a poly expression, true.
282
            //
283
            //    Otherwise, ‹RS <: RT θ'›.
284

285
            LambdaExprMirror lambda = (LambdaExprMirror) ei;
1✔
286
            if (!lambda.isExplicitlyTyped()) {
1✔
287
                return false;
1✔
288
            }
289

290
            if (getSpecies(rt) != getSpecies(rs)) {
1!
291
                TypeSpecies requiredSpecies = getSpecies(rs);
1✔
292
                boolean sameSpecies = true;
1✔
293
                boolean atLeastOne = false;
1✔
294
                for (ExprMirror rexpr : lambda.getResultExpressions()) {
1✔
295
                    atLeastOne = true;
1✔
296
                    sameSpecies &= requiredSpecies == rexpr.getStandaloneSpecies();
1✔
297
                }
1✔
298

299
                if (sameSpecies && atLeastOne) {
1!
300
                    return true;
1✔
301
                }
302
            }
303

UNCOV
304
            infer.checkConvertibleOrDefer(ctx, rs, rt.subst(tToS), ei, phase, site);
×
UNCOV
305
            return true;
×
306
        } else if (ei instanceof MethodRefMirror) {
1✔
307
            //  Otherwise, if ei is an exact method reference:
308
            //
309
            //    If RT is void, true.
310
            //
311
            //    Otherwise, if RS is a primitive type and RT is not, and the compile-time declaration for ei has a primitive return type, true.
312
            //
313
            //    Otherwise if RT is a primitive type and RS is not, and the compile-time declaration for ei has a reference return type, true.
314
            //
315
            //    Otherwise, ‹RS <: RT θ'›.
316

317
            JMethodSig exact = ExprOps.getExactMethod((MethodRefMirror) ei);
1✔
318
            if (exact == null) {
1✔
319
                return false;
1✔
320
            }
321

322
            if (getSpecies(rs) != getSpecies(rt)
1!
323
                && getSpecies(exact.getReturnType()) == getSpecies(rs)) {
1✔
324
                return true;
1✔
325
            }
326

UNCOV
327
            infer.checkConvertibleOrDefer(ctx, rs, rt.subst(tToS), ei, phase, site);
×
328
            return true;
×
329
        } else if (ei instanceof BranchingMirror) {
1!
UNCOV
330
            return ((BranchingMirror) ei).branchesMatch(e -> addGenericExprConstraintsRecursive(ctx, e, rs, rt, tToS, site));
×
331
        }
332

333
        return false;
1✔
334
    }
335
}
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