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

pmd / pmd / 315

18 Dec 2025 03:00PM UTC coverage: 78.963% (+0.2%) from 78.784%
315

push

github

adangel
[doc] Explain how to build or pull snapshot dependencies for single module builds (#6287)

18491 of 24300 branches covered (76.09%)

Branch coverage included in aggregate %.

40287 of 50137 relevant lines covered (80.35%)

0.81 hits per line

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

63.6
/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/internal/infer/TypeInferenceLogger.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 static net.sourceforge.pmd.lang.java.types.internal.InternalMethodTypeItf.cast;
9

10
import java.io.PrintStream;
11
import java.util.ArrayDeque;
12
import java.util.Deque;
13
import java.util.Iterator;
14
import java.util.List;
15
import java.util.Set;
16
import java.util.function.Supplier;
17
import java.util.regex.Matcher;
18
import java.util.regex.Pattern;
19

20
import org.apache.commons.lang3.StringUtils;
21
import org.apache.commons.lang3.SystemUtils;
22
import org.checkerframework.checker.nullness.qual.NonNull;
23
import org.checkerframework.checker.nullness.qual.Nullable;
24

25
import net.sourceforge.pmd.lang.java.ast.JavaNode;
26
import net.sourceforge.pmd.lang.java.internal.JavaLanguageProperties;
27
import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol;
28
import net.sourceforge.pmd.lang.java.types.JMethodSig;
29
import net.sourceforge.pmd.lang.java.types.JTypeMirror;
30
import net.sourceforge.pmd.lang.java.types.TypePrettyPrint;
31
import net.sourceforge.pmd.lang.java.types.TypePrettyPrint.TypePrettyPrinter;
32
import net.sourceforge.pmd.lang.java.types.internal.infer.ExprMirror.CtorInvocationMirror;
33
import net.sourceforge.pmd.lang.java.types.internal.infer.ExprMirror.FunctionalExprMirror;
34
import net.sourceforge.pmd.lang.java.types.internal.infer.ExprMirror.InvocationMirror.MethodCtDecl;
35
import net.sourceforge.pmd.lang.java.types.internal.infer.InferenceVar.BoundKind;
36
import net.sourceforge.pmd.util.StringUtil;
37

38
/**
39
 * A strategy to log the execution traces of {@link Infer}.
40
 * The default does nothing, so the logger calls can be optimized out
41
 * at runtime, while not having to check that logging is enabled at the
42
 * call sites.
43
 *
44
 * <p>To enable logging for the CLI, use the language property ({@link JavaLanguageProperties})
45
 * {@code xTypeInferenceLogging}. From tests, see {@code JavaParsingHelper#logTypeInferenceVerbose()}.
46
 */
47
@SuppressWarnings("PMD.UncommentedEmptyMethodBody")
48
public interface TypeInferenceLogger {
49

50
    // computeCompileTimeDecl
51

52

53
    default void polyResolutionFailure(JavaNode node) { }
×
54

55
    default void noApplicableCandidates(MethodCallSite site) { }
1✔
56

57
    default void noCompileTimeDeclaration(MethodCallSite site) { }
1✔
58

59
    default void startInference(JMethodSig sig, MethodCallSite site, MethodResolutionPhase phase) { }
1✔
60

61
    default void endInference(@Nullable JMethodSig result) { }
1✔
62

63
    default void fallbackInvocation(JMethodSig ctdecl, MethodCallSite site) { }
1✔
64

65
    default void skipInstantiation(JMethodSig partiallyInferred, MethodCallSite site) { }
1✔
66

67
    default void ambiguityError(MethodCallSite site, @Nullable MethodCtDecl selected, List<MethodCtDecl> m1) { }
1✔
68

69
    // instantiateImpl
70

71

72
    default void ctxInitialization(InferenceContext ctx, JMethodSig sig) { }
1✔
73

74
    default void applicabilityTest(InferenceContext ctx) { }
1✔
75

76
    default void finishApplicabilityTest() { }
1✔
77

78
    default void startArgsChecks() { }
1✔
79

80
    default void startArg(int i, ExprMirror expr, JTypeMirror formal) { }
1✔
81

82
    default void skipArgAsNonPertinent(int i, ExprMirror expr) { }
1✔
83

84
    default void functionalExprNeedsInvocationCtx(JTypeMirror targetT, ExprMirror expr) { }
1✔
85

86
    default void functionalExprHasUnresolvedTargetType(JTypeMirror targetT, FunctionalExprMirror expr) { }
1✔
87

88
    default void endArg() { }
1✔
89

90
    default void endArgsChecks() { }
1✔
91

92
    default void startReturnChecks() { }
1✔
93

94
    default void endReturnChecks() { }
1✔
95

96
    default void propagateAndAbort(InferenceContext context, InferenceContext parent) { }
1✔
97

98
    default void contextDependenciesChanged(InferenceContext ctx) { }
1✔
99

100
    // ivar events
101

102

103
    default void boundAdded(InferenceContext ctx, InferenceVar var, BoundKind kind, JTypeMirror bound, boolean isSubstitution) { }
1✔
104

105
    default void ivarMerged(InferenceContext ctx, InferenceVar var, InferenceVar delegate) { }
1✔
106

107
    default void ivarInstantiated(InferenceContext ctx, InferenceVar var, JTypeMirror inst) { }
1✔
108

109
    default void ivarDependencyRegistered(InferenceContext ctx, InferenceVar var, Set<InferenceVar> deps) { }
1✔
110

111
    // Context management
112

113
    default <T> T inContext(String st, Supplier<T> action) {
114
        return action.get();
1✔
115
    }
116

117
    /**
118
     * Log that the instantiation of the method type m for the given
119
     * call site failed. The exception provides a detail message.
120
     * Such an event is perfectly normal and may happen repeatedly
121
     * when performing overload resolution.
122
     *
123
     * <p>Exceptions occurring in an {@link MethodResolutionPhase#isInvocation() invocation phase}
124
     * are compile-time errors though.
125
     *
126
     * @param exception Failure record
127
     */
128
    default void logResolutionFail(ResolutionFailure exception) { }
1✔
129

130
    default boolean isNoop() {
131
        return false;
1✔
132
    }
133

134
    /**
135
     * Return an instance for concurrent use in another thread.
136
     * If this is Noop, then return the same instance because it's
137
     * thread-safe.
138
     */
139
    TypeInferenceLogger newInstance();
140

141
    static TypeInferenceLogger noop() {
142
        return SimpleLogger.NOOP;
1✔
143
    }
144

145

146
    class SimpleLogger implements TypeInferenceLogger {
147

148
        static final TypeInferenceLogger NOOP = new TypeInferenceLogger() {
1✔
149
            @Override
150
            public boolean isNoop() {
151
                return true;
1✔
152
            }
153

154
            @Override
155
            public TypeInferenceLogger newInstance() {
156
                return this;
×
157
            }
158
        };
159

160

161
        protected final PrintStream out;
162
        private String indent;
163
        /**
164
         * Four spaces.
165
         */
166
        protected static final String BASE_INDENT = "    ";
167

168
        protected static final String ANSI_RESET = "\u001B[0m";
169
        protected static final String ANSI_BLUE = "\u001B[34m";
170
        protected static final String ANSI_PURPLE = "\u001B[35m";
171
        protected static final String ANSI_GRAY = "\u001B[37m";
172
        protected static final String ANSI_RED = "\u001B[31m";
173
        protected static final String ANSI_YELLOW = "\u001B[33m";
174

175
        private static final String TO_BLUE =
1✔
176
            Matcher.quoteReplacement(ANSI_BLUE) + "$0" + Matcher.quoteReplacement(ANSI_RESET);
1✔
177

178
        private static final String TO_WHITE =
1✔
179
            Matcher.quoteReplacement(ANSI_GRAY) + "$0" + Matcher.quoteReplacement(ANSI_RESET);
1✔
180

181
        private static final Pattern IVAR_PATTERN = Pattern.compile("['^][α-ωa-z]\\d*");
1✔
182
        private static final Pattern IDENT_PATTERN = Pattern.compile("\\b(?<!['^])(?!extends|super|capture|of|)[\\w]++(?!\\.)<?|-?>++");
1✔
183

184
        protected String color(Object str, String color) {
185
            return SystemUtils.IS_OS_UNIX ? color + str + ANSI_RESET : str.toString();
1!
186
        }
187

188
        protected static String colorIvars(Object str) {
189
            return doColor(str, IVAR_PATTERN, TO_BLUE);
1✔
190
        }
191

192
        protected static String colorPunct(Object str) {
193
            return doColor(str, IDENT_PATTERN, TO_WHITE);
1✔
194
        }
195

196
        protected static String doColor(Object str, Pattern pattern, String replacement) {
197
            if (SystemUtils.IS_OS_UNIX) {
1!
198
                return pattern.matcher(str.toString()).replaceAll(replacement);
1✔
199
            }
200
            return str.toString();
×
201
        }
202

203
        public SimpleLogger(PrintStream out) {
1✔
204
            this.out = out;
1✔
205
            this.indent = "";
1✔
206
        }
1✔
207

208
        protected void addIndentSegment(String segment) {
209
            indent += segment;
1✔
210
        }
1✔
211

212
        protected void removeIndentSegment(String segment) {
213
            assert indent.endsWith(segment) : "mismatched end section!";
1!
214
            indent = indent.substring(0, indent.length() - segment.length());
1✔
215
        }
1✔
216

217
        protected void setIndent(String indent) {
218
            this.indent = indent;
1✔
219
        }
1✔
220

221
        protected String getIndent() {
222
            return indent;
1✔
223
        }
224

225
        protected void println(String str) {
226
            out.print(indent);
1✔
227
            out.println(str);
1✔
228
        }
1✔
229

230
        @Override
231
        public <T> T inContext(String st, Supplier<T> action) {
232
            String indent = "│   ";
1✔
233
            println("START " + st);
1✔
234
            addIndentSegment(indent);
1✔
235
            try {
236
                return action.get();
1✔
237
            } finally {
238
                removeIndentSegment(indent);
1✔
239
                println("â””  END " + st);
1✔
240
            }
241
        }
242

243
        protected void endSection(String footer) {
244
            removeIndentSegment(BASE_INDENT);
1✔
245
            println(footer);
1✔
246
        }
1✔
247

248
        protected void startSection(String header) {
249
            println(header);
1✔
250
            addIndentSegment(BASE_INDENT);
1✔
251
        }
1✔
252

253
        @Override
254
        public void logResolutionFail(ResolutionFailure exception) {
255
            if (exception.getCallSite() instanceof MethodCallSite && exception != ResolutionFailure.UNKNOWN) {
1!
256
                ((MethodCallSite) exception.getCallSite()).acceptFailure(exception);
1✔
257
            }
258
        }
1✔
259

260
        @Override
261
        public void noApplicableCandidates(MethodCallSite site) {
262
            if (!site.isLogEnabled()) {
×
263
                return;
×
264
            }
265
            @Nullable JTypeMirror receiver = site.getExpr().getErasedReceiverType();
×
266
            if (receiver != null) {
×
267
                JTypeDeclSymbol symbol = receiver.getSymbol();
×
268
                if (symbol == null || symbol.isUnresolved()) {
×
269
                    return;
×
270
                }
271
            }
272

273
            if (site.getExpr() instanceof CtorInvocationMirror) {
×
274
                startSection("[WARNING] No potentially applicable constructors in "
×
275
                            + ((CtorInvocationMirror) site.getExpr()).getNewType());
×
276
            } else {
277
                startSection("[WARNING] No potentially applicable methods in " + receiver);
×
278
            }
279
            printExpr(site.getExpr());
×
280

281
            Iterator<JMethodSig> iter = site.getExpr().getAccessibleCandidates().iterator();
×
282
            if (iter.hasNext()) {
×
283
                startSection("Accessible signatures:");
×
284
                iter.forEachRemaining(it -> println(ppMethod(it)));
×
285
                endSection("");
×
286
            } else {
287
                println("No accessible signatures");
×
288
            }
289
            endSection("");
×
290
        }
×
291

292
        @Override
293
        public void noCompileTimeDeclaration(MethodCallSite site) {
294
            if (!site.isLogEnabled()) {
1!
295
                return;
1✔
296
            }
297
            startSection("[WARNING] Compile-time declaration resolution failed.");
×
298
            printExpr(site.getExpr());
×
299
            summarizeFailures(site);
×
300
            endSection("");
×
301
        }
×
302

303
        private void summarizeFailures(MethodCallSite site) {
304
            startSection("Summary of failures:");
×
305
            site.getResolutionFailures()
×
306
                .forEach((phase, failures) -> {
×
307
                    startSection(phase.toString() + ":");
×
308
                    failures.forEach(it -> println(String.format("%-64s // while checking %s", it.getReason(), ppMethod(it.getFailedMethod()))));
×
309
                    endSection("");
×
310
                });
×
311
            endSection("");
×
312
        }
×
313

314
        @Override
315
        public void fallbackInvocation(JMethodSig ctdecl, MethodCallSite site) {
316
            if (!site.isLogEnabled()) {
×
317
                return;
×
318
            }
319
            startSection("[WARNING] Invocation type resolution failed");
×
320
            printExpr(site.getExpr());
×
321
            summarizeFailures(site);
×
322
            println("-> Falling back on " + ppHighlight(ctdecl)
×
323
                        + " (this may cause future mistakes)");
324
            endSection("");
×
325
        }
×
326

327
        @Override
328
        public void functionalExprHasUnresolvedTargetType(JTypeMirror targetT, FunctionalExprMirror expr) {
329
            println("[WARNING] Target type for functional expression is unresolved: " + targetT);
×
330
            println("Will treat the expression as matching (this may cause future mistakes)");
×
331
        }
×
332

333
        @Override
334
        public void ambiguityError(MethodCallSite site, @Nullable MethodCtDecl selected, List<MethodCtDecl> methods) {
335
            println("");
×
336
            printExpr(site.getExpr());
×
337
            startSection("[WARNING] Ambiguity error: all methods are maximally specific");
×
338
            for (MethodCtDecl m : methods) {
×
339
                println(color(cast(m.getMethodType()).originalMethod(), ANSI_RED));
×
340
            }
×
341

342
            if (selected != null) {
×
343
                endSection("Will select " + color(cast(selected.getMethodType()).originalMethod(), ANSI_BLUE));
×
344
            } else {
345
                endSection(""); // no fallback?
×
346
            }
347
        }
×
348

349
        protected void printExpr(ExprMirror expr) {
350
            String exprText = expr.getLocationText().toString();
1✔
351
            exprText = exprText.replaceAll("\\R\\s+", "");
1✔
352
            exprText = StringUtil.escapeJava(StringUtils.truncate(exprText, 100));
1✔
353
            println("At:   " + fileLocation(expr));
1✔
354
            println("Expr: " + color(exprText, ANSI_YELLOW));
1✔
355
        }
1✔
356

357
        private String fileLocation(ExprMirror mirror) {
358
            return mirror.getLocation().getReportLocation().startPosToStringWithFile();
1✔
359
        }
360

361
        protected @NonNull String ppMethod(JMethodSig sig) {
362
            return TypePrettyPrint.prettyPrint(sig, new TypePrettyPrinter().printMethodHeader(false));
1✔
363
        }
364

365
        protected @NonNull String ppHighlight(JMethodSig sig) {
366
            String s = ppMethod(sig);
1✔
367
            int paramStart = s.indexOf('(');
1✔
368
            String name = s.substring(0, paramStart);
1✔
369
            String rest = s.substring(paramStart);
1✔
370
            return color(name, ANSI_BLUE) + colorIvars(colorPunct(rest));
1✔
371
        }
372

373
        protected @NonNull String ppBound(InferenceVar ivar, BoundKind kind, JTypeMirror bound) {
374
            return ivar + kind.getSym() + colorIvars(colorPunct(bound));
1✔
375
        }
376

377
        @Override
378
        public TypeInferenceLogger newInstance() {
379
            return new SimpleLogger(out);
×
380
        }
381
    }
382

383
    /**
384
     * This is mega verbose, should only be used for unit tests.
385
     */
386
    class VerboseLogger extends SimpleLogger {
387

388

389
        private final Deque<String> marks = new ArrayDeque<>();
1✔
390

391
        public VerboseLogger(PrintStream out) {
392
            super(out);
1✔
393
            mark();
1✔
394
        }
1✔
395

396
        void mark() {
397
            marks.push(getIndent());
1✔
398
        }
1✔
399

400
        void rollback(String lastWords) {
401
            final String savedIndent = marks.pop();
1✔
402
            setIndent(savedIndent); // back to normal
1✔
403
            if (!lastWords.isEmpty()) {
1!
404
                addIndentSegment(BASE_INDENT);
1✔
405
                println(lastWords);
1✔
406
                setIndent(savedIndent);
1✔
407
            }
408
        }
1✔
409

410
        @Override
411
        public void startInference(JMethodSig sig, MethodCallSite site, MethodResolutionPhase phase) {
412
            mark();
1✔
413
            startSection(String.format("Phase %-17s%s", phase, ppHighlight(sig)));
1✔
414
        }
1✔
415

416

417
        @Override
418
        public void ctxInitialization(InferenceContext ctx, JMethodSig sig) {
419
            println(String.format("Context %-11d%s", ctx.getId(), ppHighlight(ctx.mapToIVars(sig))));
1✔
420
        }
1✔
421

422
        @Override
423
        public void applicabilityTest(InferenceContext ctx) {
424
            println(String.format("Solving with context %d for applicability testing", ctx.getId()));
1✔
425
            addIndentSegment("|   ");
1✔
426
        }
1✔
427

428
        @Override
429
        public void finishApplicabilityTest() {
430
            removeIndentSegment("|   ");
1✔
431
        }
1✔
432

433
        @Override
434
        public void endInference(@Nullable JMethodSig result) {
435
            rollback(result != null ? "Success: " + ppHighlight(result)
1✔
436
                                    : "FAILED! SAD!");
1✔
437
        }
1✔
438

439
        @Override
440
        public void skipInstantiation(JMethodSig partiallyInferred, MethodCallSite site) {
441
            println("Skipping instantiation of " + partiallyInferred + ", it's already complete");
1✔
442
        }
1✔
443

444

445
        @Override
446
        public void startArgsChecks() {
447
            startSection("ARGUMENTS");
1✔
448
        }
1✔
449

450
        @Override
451
        public void startReturnChecks() {
452
            startSection("RETURN");
1✔
453
        }
1✔
454

455

456
        @Override
457
        public void propagateAndAbort(InferenceContext context, InferenceContext parent) {
458
            println("Ctx " + parent.getId() + " adopts " + color(context.getFreeVars(), ANSI_BLUE) + " from ctx "
1✔
459
                        + context.getId());
1✔
460
        }
1✔
461

462
        @Override
463
        public void startArg(int i, ExprMirror expr, JTypeMirror formalType) {
464
            startSection("Checking arg " + i + " against " + colorIvars(formalType));
1✔
465
            printExpr(expr);
1✔
466
        }
1✔
467

468
        @Override
469
        public void skipArgAsNonPertinent(int i, ExprMirror expr) {
470
            startSection("Argument " + i + " is not pertinent to applicability");
1✔
471
            printExpr(expr);
1✔
472
            endSection("");
1✔
473
        }
1✔
474

475
        @Override
476
        public void functionalExprNeedsInvocationCtx(JTypeMirror targetT, ExprMirror expr) {
477
            println("Target type is not a functional interface yet: " + targetT);
×
478
            println("Will wait for invocation phase before discarding.");
×
479
        }
×
480

481

482
        @Override
483
        public void endArgsChecks() {
484
            endSection("");
1✔
485
        }
1✔
486

487
        @Override
488
        public void endArg() {
489
            endSection("");
1✔
490
        }
1✔
491

492
        @Override
493
        public void endReturnChecks() {
494
            endSection("");
1✔
495
        }
1✔
496

497
        @Override
498
        public void boundAdded(InferenceContext ctx, InferenceVar ivar, BoundKind kind, JTypeMirror bound, boolean isSubstitution) {
499
            String message = isSubstitution ? "Changed bound" : "New bound";
1!
500
            println(addCtxInfo(ctx, message) + ppBound(ivar, kind, bound));
1✔
501
        }
1✔
502

503
        @Override
504
        public void ivarMerged(InferenceContext ctx, InferenceVar var, InferenceVar delegate) {
505
            println(addCtxInfo(ctx, "Ivar merged") + var + " <=> " + delegate);
1✔
506
        }
1✔
507

508
        @Override
509
        public void ivarInstantiated(InferenceContext ctx, InferenceVar var, JTypeMirror inst) {
510
            println(addCtxInfo(ctx, "Ivar instantiated") + color(var + " := ", ANSI_BLUE) + colorIvars(inst));
1✔
511
        }
1✔
512

513
        @Override
514
        public void ivarDependencyRegistered(InferenceContext ctx, InferenceVar var, Set<InferenceVar> deps) {
515
            println(addCtxInfo(ctx, "Ivar dependency registered: ") + color(var + " -> ", ANSI_BLUE) + colorIvars(deps));
1✔
516
        }
1✔
517

518
        @Override
519
        public void contextDependenciesChanged(InferenceContext ctx) {
520
            println("Recomputing dependency graph (ctx " + ctx.getId() + ")");
×
521
        }
×
522

523
        private @NonNull String addCtxInfo(InferenceContext ctx, String event) {
524
            return String.format("%-20s(ctx %d):   ", event, ctx.getId());
1✔
525
        }
526

527
        @Override
528
        public void logResolutionFail(ResolutionFailure exception) {
529
            super.logResolutionFail(exception);
1✔
530
            println("Failed: " + exception.getReason());
1✔
531
        }
1✔
532

533
        @Override
534
        public TypeInferenceLogger newInstance() {
535
            return new VerboseLogger(out);
×
536
        }
537

538
    }
539

540

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