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

pmd / pmd / 19

29 May 2025 04:22PM UTC coverage: 77.723% (-0.03%) from 77.757%
19

push

github

adangel
Fix #5621: [java] Fix FPs with UnusedPrivateMethod (#5727)

Merge pull request #5727 from oowekyala:issue5621-unusedprivatemethod

17705 of 23734 branches covered (74.6%)

Branch coverage included in aggregate %.

50 of 52 new or added lines in 9 files covered. (96.15%)

81 existing lines in 6 files now uncovered.

38845 of 49024 relevant lines covered (79.24%)

0.8 hits per line

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

85.96
/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/internal/infer/ExprMirror.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.internal.infer.ExprMirror.TypeSpecies.UNKNOWN;
8
import static net.sourceforge.pmd.lang.java.types.internal.infer.ExprMirror.TypeSpecies.getSpecies;
9
import static net.sourceforge.pmd.lang.java.types.internal.infer.MethodResolutionPhase.STRICT;
10

11
import java.util.List;
12
import java.util.function.Predicate;
13

14
import org.checkerframework.checker.nullness.qual.NonNull;
15
import org.checkerframework.checker.nullness.qual.Nullable;
16

17
import net.sourceforge.pmd.lang.java.ast.JavaNode;
18
import net.sourceforge.pmd.lang.java.ast.MethodUsage;
19
import net.sourceforge.pmd.lang.java.symbols.JConstructorSymbol;
20
import net.sourceforge.pmd.lang.java.types.JClassType;
21
import net.sourceforge.pmd.lang.java.types.JMethodSig;
22
import net.sourceforge.pmd.lang.java.types.JTypeMirror;
23
import net.sourceforge.pmd.lang.java.types.JTypeVar;
24
import net.sourceforge.pmd.lang.java.types.OverloadSelectionResult;
25
import net.sourceforge.pmd.lang.java.types.TypeOps;
26
import net.sourceforge.pmd.lang.java.types.TypeSystem;
27
import net.sourceforge.pmd.lang.java.types.TypingContext;
28
import net.sourceforge.pmd.util.OptionalBool;
29

30
/**
31
 * Adapter class to manipulate expressions. The framework
32
 * ideally keeps focus on types and doesn't have a dependency
33
 * on the AST. Only the impl package can have such dependencies.
34
 */
35
public interface ExprMirror {
36

37
    /**
38
     * Returns a node which is used as a location to report messages.
39
     * Do not use this any other way.
40
     */
41
    JavaNode getLocation();
42

43

44
    /**
45
     * If this expression is of a standalone form, returns the type of
46
     * the expression. Otherwise returns null.
47
     *
48
     * <p>Note that standalone types can directly be set on the type
49
     * node.
50
     *
51
     * @return The type of the expression if it is standalone
52
     */
53
    @Nullable JTypeMirror getStandaloneType();
54

55
    /**
56
     * For a standalone expr, finish type inference by computing properties
57
     * that are guarded by the type res lock. For instance for a standalone
58
     * ctor call, the standalone type is trivially known (it's the type node).
59
     * But we still need to do overload resolution.
60
     */
61
    default void finishStandaloneInference(@NonNull JTypeMirror standaloneType) {
62
        // do nothing
63
    }
1✔
64

65

66
    /**
67
     * Set the type of the underlying ast node. Used when we need
68
     * to find out the type of a poly to infer the type of another,
69
     * that way, we don't repeat computation.
70
     */
71
    void setInferredType(JTypeMirror mirror);
72

73
    /** Return the value set in the last call to {@link #setInferredType(JTypeMirror)}. */
74
    @Nullable JTypeMirror getInferredType();
75

76
    /**
77
     * Returns typing information for the lambdas parameters in scope
78
     * in this expression and its subexpressions. When overload resolution
79
     * involves lambdas, we might have to try several target types for each
80
     * lambda. Each of those may give a different type to the lambda parameters,
81
     * and hence, to every expression in the lambda body. These "tentative"
82
     * typing are kept in the {@link TypingContext} object and only
83
     * committed to the AST for the overload that is selected in the end.
84
     */
85
    TypingContext getTypingContext();
86

87
    /**
88
     * Returns the species that this expression produces. The species
89
     * may be known even if the expr is not standalone. For example a
90
     * diamond constructor call is not standalone, but its species is
91
     * obviously REFERENCE.
92
     *
93
     * <p>This is used for specificity tests for lambdas. They use species
94
     * because invocation needs to be done exactly once, and the actual
95
     * type of the expression may differ depending on the selected overload.
96
     * Eg given the signatures {@code <T>foo(Supplier<T>)} and {@code foo(Runnable)},
97
     * the expression {@code foo(() -> new List<>())} must select the supplier
98
     * overload, even before the invocation type of {@code List<>} is known.
99
     * The overload selection compares the expected species of both function
100
     * types (REFERENCE for Supplier, VOID for Runnable), and determines that
101
     * the supplier is more appropriate.
102
     */
103
    default @NonNull TypeSpecies getStandaloneSpecies() {
104
        JTypeMirror std = getStandaloneType();
1✔
105
        return std == null ? UNKNOWN : getSpecies(std);
1!
106
    }
107

108

109
    /**
110
     * Returns true if this mirror and its subexpressions are equivalent
111
     * to the underlying AST node. This is only relevant when making mirrors
112
     * that are not exactly equal to the AST node (eg, omitting explicit type arguments),
113
     * in order to check if the transformation does not change the meaning of the program.
114
     * It verifies that method and constructor calls are overload-selected
115
     * to the same compile-time declaration, and that nested lambdas
116
     * have the same type as in the AST.
117
     *
118
     * <p>This mirror's state, as filled-in during type resolution by
119
     * {@link Infer} using the various setters of {@link ExprMirror}
120
     * interfaces, is compared to the AST's corresponding state. Consequently,
121
     * if this state is missing (meaning, that no overload resolution
122
     * has been run using this mirror), the analysis cannot be performed
123
     * and an exception is thrown.
124
     *
125
     * @throws IllegalStateException If this mirror has not been used for overload resolution
126
     */
127
    boolean isEquivalentToUnderlyingAst();
128

129
    /** A general category of types. */
130
    enum TypeSpecies {
1✔
131
        PRIMITIVE,
1✔
132
        REFERENCE,
1✔
133
        VOID,
1✔
134
        UNKNOWN;
1✔
135

136

137
        public static TypeSpecies getSpecies(JTypeMirror t) {
138
            if (t.isPrimitive()) {
1✔
139
                return PRIMITIVE;
1✔
140
            } else if (t.isVoid()) {
1✔
141
                return VOID;
1✔
142
            } else if (TypeOps.isSpecialUnresolved(t)) {
1!
143
                return UNKNOWN;
×
144
            }
145
            return REFERENCE;
1✔
146
        }
147
    }
148

149

150
    interface PolyExprMirror extends ExprMirror {
151

152

153
        /**
154
         * Returns the class declaration wherein this invocation occurs.
155
         * Returns null if it's unresolved.
156
         */
157
        @NonNull JClassType getEnclosingType();
158

159

160
        @Override
161
        default @Nullable JTypeMirror getStandaloneType() {
162
            return null;
1✔
163
        }
164

165

166
        /**
167
         * If inference failed to determine the type of this node, returns
168
         * a fallback for it. This should not query the context of the expression,
169
         * or nodes whose type is unstable because it may be being inferred.
170
         *
171
         * <p>If no fallback should be used, returns null.
172
         */
173
        default @Nullable JTypeMirror unresolvedType() {
174
            return null;
1✔
175
        }
176
    }
177

178

179
    /** Mirrors a conditional or switch expression. */
180
    interface BranchingMirror extends PolyExprMirror {
181

182
        /**
183
         * Returns true if every result expression matches the given
184
         * predicate.
185
         */
186
        boolean branchesMatch(Predicate<? super ExprMirror> condition);
187

188
        /**
189
         * Record on the AST node that is is a standalone expression.
190
         * This accounts for special cases in the spec which are made
191
         * for numeric and boolean conditional expressions. For those
192
         * types of standalone exprs, the branches may have an additional
193
         * implicit unboxing/widening conversion, that does not depend
194
         * on the usual target type (the context of the ternary itself),
195
         * but just on the other branch.
196
         */
197
        default void setStandalone() {
198
            // do nothing by default
199
        }
1✔
200

201

202
        @Override
203
        default boolean isEquivalentToUnderlyingAst() {
204
            return branchesMatch(ExprMirror::isEquivalentToUnderlyingAst);
×
205
        }
206
    }
207

208
    /**
209
     * Mirror of some expression that targets a functional interface type:
210
     * lambda or method reference.
211
     */
212
    interface FunctionalExprMirror extends PolyExprMirror {
213

214
        /**
215
         * For a method ref or lambda, this is the type of the functional interface.
216
         * E.g. in {@code stringStream.map(String::isEmpty)}, this is
217
         * {@code java.util.function.Function<java.lang.String, java.lang.Boolean>}
218
         *
219
         * <p>May be null if we're resetting some partial data.
220
         */
221
        @Override
222
        void setInferredType(@Nullable JTypeMirror mirror);
223

224

225
        /**
226
         * This is the method that is overridden in getInferredType.
227
         * E.g. in {@code stringStream.map(String::isEmpty)}, this is
228
         * {@code java.util.function.Function<java.lang.String, java.lang.Boolean>.apply(java.lang.String) ->
229
         * java.lang.Boolean}
230
         *
231
         * <p>May be null if we're resetting some partial data.
232
         */
233
        void setFunctionalMethod(@Nullable JMethodSig methodType);
234

235
        /**
236
         * If the matching between this expr and its target type failed,
237
         * finish the inference by setting the data to UNKNOWN, or likely
238
         * values. This is used as a fallback.
239
         *
240
         * @param targetType Target type for the expression, null if there is none
241
         */
242
        void finishFailedInference(@Nullable JTypeMirror targetType);
243
    }
244

245
    /**
246
     * Common interface for {@link InvocationMirror} and {@link MethodRefMirror},
247
     * both of which wrap nods that implement {@link MethodUsage}.
248
     */
249
    interface MethodUsageMirror extends PolyExprMirror {
250

251
        /**
252
         * Set the compile-time declaration that was resolved for this method usage.
253
         */
254
        void setCompileTimeDecl(InvocationMirror.MethodCtDecl methodType);
255

256
        /**
257
         * Return the type in which the search for accessible methods start.
258
         * For method references it is the type of the LHS and is specified by
259
         * the JLS. For method invocations it is the type of the receiver, or
260
         * the type of the enclosing type. For constructor invocations this
261
         * is not defined and will return null.
262
         */
263
        @Nullable JTypeMirror getTypeToSearch();
264
    }
265

266
    /**
267
     * Mirror of a method reference expression.
268
     */
269
    interface MethodRefMirror extends FunctionalExprMirror, MethodUsageMirror {
270

271
        /** True if this references a ctor. */
272
        boolean isConstructorRef();
273

274

275
        /**
276
         * Returns the type to search as defined by the first section of
277
         * <a href="https://docs.oracle.com/javase/specs/jls/se9/html/jls-15.html#jls-15.13.1">JLS§15.13.1</a>
278
         * , except it may also return an array type (the jls makes an exception for it,
279
         * while we don't).
280
         */
281
        @Override
282
        JTypeMirror getTypeToSearch();
283

284

285
        /**
286
         * Returns the type of the left hand-side, if it is not an expression.
287
         * Note that the following qualifier super forms are considered "expressions",
288
         * that have a context-dependent type (depends on the type of the {@code this} expr):
289
         * <pre>
290
         * super :: [TypeArguments] Identifier
291
         * TypeName.super :: [TypeArguments] Identifier
292
         * </pre>
293
         */
294
        @Nullable
295
        JTypeMirror getLhsIfType();
296

297

298
        /**
299
         * Returns the name of the invoked method, or {@link JConstructorSymbol#CTOR_NAME}
300
         * if this is a constructor reference.
301
         */
302
        String getMethodName();
303

304

305
        /** Returns the explicit type arguments (the ones to the right of the "::"). */
306
        @NonNull List<JTypeMirror> getExplicitTypeArguments();
307

308
        /**
309
         * This is the method that is referenced.
310
         * E.g. in {@code stringStream.map(String::isEmpty)}, this is
311
         * {@code java.lang.String.isEmpty() -> boolean}
312
         */
313
        @Override
314
        void setCompileTimeDecl(InvocationMirror.MethodCtDecl methodType);
315

316

317
        /**
318
         * UNRESOLVED_METHOD if not yet computed, null if computed but
319
         * inexact, otherwise the real method.
320
         */
321
        @Nullable
322
        JMethodSig getCachedExactMethod();
323

324

325
        void setCachedExactMethod(@Nullable JMethodSig sig);
326

327
    }
328

329
    /** Mirrors a lambda expression. */
330
    interface LambdaExprMirror extends FunctionalExprMirror {
331

332
        /**
333
         * Returns the types of the explicit parameters. If the lambda
334
         * is implicitly typed, then returns null. If some parameters
335
         * have a var type, returns {@link TypeSystem#UNKNOWN} for those.
336
         *
337
         * <p>Note that a degenerate case of explicitly typed lambda
338
         * expression is a lambda with zero formal parameters.
339
         */
340
        @Nullable List<JTypeMirror> getExplicitParameterTypes();
341

342
        /**
343
         * See {@link #getExplicitParameterTypes()}.
344
         */
345
        default boolean isExplicitlyTyped() {
346
            return getExplicitParameterTypes() != null;
1✔
347
        }
348

349
        /**
350
         * Return the number of parameters of the lambda, regardless of
351
         * whether it's explicitly typed or not.
352
         */
353
        int getParamCount();
354

355

356
        /**
357
         * Returns all the expressions that appear in {@code return}
358
         * statements within the lambda. If this is an expression-bodied
359
         * lambda, returns the expression.
360
         */
361
        Iterable<ExprMirror> getResultExpressions();
362

363

364
        /**
365
         * Returns true if the body is value-compatible {@literal (JLS§15.27.2)}.
366
         * <blockquote>
367
         *     A block lambda body is value-compatible if it cannot complete
368
         *     normally (§14.21) and every return statement in the block
369
         *     has the form return Expression;.
370
         * </blockquote>
371
         */
372
        boolean isValueCompatible();
373

374
        /**
375
         * Returns true if the body is void-compatible {@literal (JLS§15.27.2)}.
376
         * <blockquote>
377
         *     A block lambda body is void-compatible if every return
378
         *     statement in the block has the form return;.
379
         * </blockquote>
380
         */
381
        boolean isVoidCompatible();
382

383
        /**
384
         * Set the currently considered type of the parameters.
385
         * This may change depending on which target type we are currently
386
         * considering. The type of parameters (and therefore the typing context)
387
         * may influence the type of the return values of the lambda.
388
         *
389
         * @param formalParameters formal parameter types of the lambda
390
         */
391
        void updateTypingContext(List<? extends JTypeMirror> formalParameters);
392
    }
393

394
    /**
395
     * Adapter over a method or constructor invocation expression.
396
     */
397
    interface InvocationMirror extends PolyExprMirror, MethodUsageMirror {
398

399

400
        /**
401
         * Enumerates *accessible* method (or ctor) signatures with
402
         * *the same name* as this invocation. Name and accessibility
403
         * will not be checked later.
404
         *
405
         * The details on how to determine this are here:
406
         *
407
         * https://docs.oracle.com/javase/specs/jls/se9/html/jls-15.html#jls-15.12.1
408
         */
409
        Iterable<JMethodSig> getAccessibleCandidates();
410

411

412
        /**
413
         * Returns the erased receiver type. This is only used to adapt the
414
         * {@code Object::getClass} method, other types of invocations don't
415
         * need to implement this.
416
         */
417
        default @Nullable JTypeMirror getErasedReceiverType() {
418
            return null;
×
419
        }
420

421

422
        /**
423
         * Returns the erased receiver type. This is only used for method
424
         * invocations.
425
         */
426
        @Nullable JTypeMirror getReceiverType();
427

428

429
        /**
430
         * Returns the explicit type arguments, eg in {@code Arrays.<String>asList("q")},
431
         * or {@code new <String> Foo("q")}. If none are mentioned, returns an empty list.
432
         */
433
        List<JTypeMirror> getExplicitTypeArguments();
434

435

436
        /**
437
         * @throws IndexOutOfBoundsException If there's no explicit type argument at the given index
438
         */
439
        JavaNode getExplicitTargLoc(int i);
440

441

442
        /**
443
         * Returns the name of the invoked method. If this is a
444
         * constructor call, returns {@link JConstructorSymbol#CTOR_NAME}.
445
         */
446
        String getName();
447

448

449
        /** Returns the expressions corresponding to the arguments of the call. */
450
        List<ExprMirror> getArgumentExpressions();
451

452

453
        /** Return the size of the argument list. */
454
        int getArgumentCount();
455

456

457
        /**
458
         * {@inheritDoc}
459
         *
460
         * @implSpec Should cache this value and return it when {@link #getCtDecl()}
461
         * is called.
462
         */
463
        @Override
464
        void setCompileTimeDecl(MethodCtDecl methodType);
465

466

467
        /**
468
         * Returns the method type set with {@link #setCompileTimeDecl(MethodCtDecl)}
469
         * or null if that method was never called. This is used to perform
470
         * overload resolution exactly once per call site.
471
         */
472
        @Nullable MethodCtDecl getCtDecl();
473

474
        @Override
475
        default @Nullable JTypeMirror getTypeToSearch() {
NEW
476
            return getReceiverType();
×
477
        }
478

479
        /**
480
         * Information about the overload-resolution for a specific method.
481
         */
482
        class MethodCtDecl implements OverloadSelectionResult {
483
            // note this data is gathered by the MethodCallSite during
484
            // applicability inference, stashed in this object, and
485
            // restored when we do invocation.
486

487
            private final JMethodSig methodType;
488
            private final MethodResolutionPhase resolvePhase;
489
            private final boolean canSkipInvocation;
490
            private final OptionalBool needsUncheckedConversion;
491
            private final boolean failed;
492
            private final @Nullable MethodUsageMirror expr;
493

494
            MethodCtDecl(JMethodSig methodType,
495
                         MethodResolutionPhase resolvePhase,
496
                         boolean canSkipInvocation,
497
                         OptionalBool needsUncheckedConversion,
498
                         boolean failed,
499
                         @Nullable MethodUsageMirror expr) {
1✔
500
                this.methodType = methodType;
1✔
501
                this.resolvePhase = resolvePhase;
1✔
502
                this.canSkipInvocation = canSkipInvocation;
1✔
503
                this.needsUncheckedConversion = needsUncheckedConversion;
1✔
504
                this.failed = failed;
1✔
505
                this.expr = expr;
1✔
506
            }
1✔
507

508
            // package-private:
509

510
            public MethodCtDecl withMethod(JMethodSig method) {
511
                return withMethod(method, failed);
1✔
512
            }
513

514
            MethodCtDecl withMethod(JMethodSig method, boolean failed) {
515
                return new MethodCtDecl(method, resolvePhase, canSkipInvocation, needsUncheckedConversion, failed, expr);
1✔
516
            }
517

518
            public MethodCtDecl withExpr(MethodUsageMirror expr) {
519
                return new MethodCtDecl(methodType, resolvePhase, canSkipInvocation, needsUncheckedConversion, failed, expr);
1✔
520
            }
521

522
            MethodCtDecl asFailed() {
523
                return withMethod(methodType, true);
1✔
524
            }
525

526
            boolean canSkipInvocation() {
527
                return canSkipInvocation;
1✔
528
            }
529

530
            MethodResolutionPhase getResolvePhase() {
531
                return resolvePhase;
1✔
532
            }
533

534
            static MethodCtDecl unresolved(TypeSystem ts) {
535
                return new MethodCtDecl(ts.UNRESOLVED_METHOD, STRICT, true, OptionalBool.UNKNOWN, true, null);
1✔
536
            }
537

538
            // public:
539

540

541
            @Override
542
            public JMethodSig getMethodType() {
543
                return methodType;
1✔
544
            }
545

546
            @Override
547
            public boolean needsUncheckedConversion() {
548
                return needsUncheckedConversion.isTrue();
1✔
549
            }
550

551
            @Override
552
            public boolean isVarargsCall() {
553
                return resolvePhase.requiresVarargs();
1✔
554
            }
555

556
            @Override
557
            public boolean isFailed() {
558
                return failed;
1✔
559
            }
560

561
            @Override
562
            public String toString() {
563
                return "CtDecl[phase=" + resolvePhase + ", method=" + methodType + ']';
×
564
            }
565

566
            @Override
567
            public @Nullable JTypeMirror getTypeToSearch() {
568
                return expr != null ? expr.getTypeToSearch() : null;
1✔
569
            }
570
        }
571
    }
572

573
    /**
574
     * An invocation mirror reflecting a constructor invocation expression.
575
     */
576
    interface CtorInvocationMirror extends InvocationMirror {
577

578
        /**
579
         * Return the type name being instantiated. If the constructor call
580
         * is a diamond invocation (or no type args), returns the generic type declaration.
581
         * Otherwise returns the parameterised type. If the call declares
582
         * an anonymous class, then this does *not* return the anonymous
583
         * type, but its explicit supertype.
584
         *
585
         * <ul>
586
         *  <li>e.g. for {@code new ArrayList<>()}, returns {@code ArrayList<T>}.
587
         *  <li>e.g. for {@code new ArrayList()}, returns {@code ArrayList}.
588
         *  <li>e.g. for {@code new ArrayList<String>()}, returns {@code ArrayList<String>}.
589
         *  <li>e.g. for {@code new Runnable() {}} (anonymous), returns {@code Runnable}.
590
         * </ul>
591
         *
592
         * <p>Note that this returns a {@link JClassType} in valid code.
593
         * Other return values may be eg {@link TypeSystem#UNKNOWN}, or
594
         * a {@link JTypeVar}, but indicate malformed code.
595
         */
596
        @NonNull JTypeMirror getNewType();
597

598
        /**
599
         * True if this creates an anonymous class. Since java 9 those
600
         * can also be diamond-inferred.
601
         */
602
        boolean isAnonymous();
603

604
        /**
605
         * Return true if this is a diamond constructor call. In that
606
         * case the type parameters of the created instance must be inferred.
607
         * Returns false if the constructor call mentions no type arguments.
608
         * <ul>
609
         *  <li>e.g. for {@code new ArrayList<>()}, returns true.
610
         *  <li>e.g. for {@code new ArrayList()}, returns false.
611
         *  <li>e.g. for {@code new ArrayList<String>()}, returns false.
612
         * </ul>
613
         */
614
        boolean isDiamond();
615

616

617
        /**
618
         * {@inheritDoc}
619
         *
620
         * <p>Returns the constructor of the {@link #getNewType()}. If
621
         * this is an anonymous class declaration implementing an interface,
622
         * then returns the constructors of class {@link Object}.
623
         *
624
         * <p>This default implementation uses {@link #getAccessibleCandidates(JTypeMirror)},
625
         * which should be implemented instead.
626
         */
627
        @Override
628
        default Iterable<JMethodSig> getAccessibleCandidates() {
629
            return getAccessibleCandidates(getNewType());
1✔
630
        }
631

632
        /**
633
         * Returns the accessible candidates for this node, as if {@link #getNewType()}
634
         * returned the type passed as parameter. Since candidates depend on the
635
         * new type, this allows us to write simple "spy" wrappers to redo an invocation
636
         * in different conditions (ie, pretending the newtype is the parameter)
637
         *
638
         * @param newType Assumed value of {@link #getNewType()}
639
         */
640
        Iterable<JMethodSig> getAccessibleCandidates(JTypeMirror newType);
641

642

643
        /** Must return {@link JConstructorSymbol#CTOR_NAME}. */
644
        @Override
645
        default String getName() {
646
            return JConstructorSymbol.CTOR_NAME;
×
647
        }
648

649

650
    }
651
}
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