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

pmd / pmd / 196

16 Oct 2025 08:33AM UTC coverage: 78.642% (-0.02%) from 78.661%
196

push

github

web-flow
chore: fix dogfood issues from new rules (#6056)

18180 of 23973 branches covered (75.84%)

Branch coverage included in aggregate %.

2 of 27 new or added lines in 14 files covered. (7.41%)

2 existing lines in 1 file now uncovered.

39693 of 49617 relevant lines covered (80.0%)

0.81 hits per line

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

86.53
/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/ast/internal/LazyTypeResolver.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.ast.internal;
7

8
import static net.sourceforge.pmd.lang.java.ast.BinaryOp.ADD;
9
import static net.sourceforge.pmd.lang.java.types.TypeConversion.binaryNumericPromotion;
10
import static net.sourceforge.pmd.lang.java.types.TypeConversion.capture;
11
import static net.sourceforge.pmd.lang.java.types.TypeConversion.unaryNumericPromotion;
12
import static net.sourceforge.pmd.util.CollectionUtil.listOf;
13

14
import java.util.List;
15

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

19
import net.sourceforge.pmd.lang.ast.SemanticErrorReporter;
20
import net.sourceforge.pmd.lang.java.ast.ASTAmbiguousName;
21
import net.sourceforge.pmd.lang.java.ast.ASTAnnotation;
22
import net.sourceforge.pmd.lang.java.ast.ASTArrayAccess;
23
import net.sourceforge.pmd.lang.java.ast.ASTArrayAllocation;
24
import net.sourceforge.pmd.lang.java.ast.ASTArrayDimensions;
25
import net.sourceforge.pmd.lang.java.ast.ASTArrayInitializer;
26
import net.sourceforge.pmd.lang.java.ast.ASTAssignableExpr.AccessType;
27
import net.sourceforge.pmd.lang.java.ast.ASTAssignmentExpression;
28
import net.sourceforge.pmd.lang.java.ast.ASTBooleanLiteral;
29
import net.sourceforge.pmd.lang.java.ast.ASTCastExpression;
30
import net.sourceforge.pmd.lang.java.ast.ASTCharLiteral;
31
import net.sourceforge.pmd.lang.java.ast.ASTClassLiteral;
32
import net.sourceforge.pmd.lang.java.ast.ASTConditionalExpression;
33
import net.sourceforge.pmd.lang.java.ast.ASTConstructorCall;
34
import net.sourceforge.pmd.lang.java.ast.ASTEnumConstant;
35
import net.sourceforge.pmd.lang.java.ast.ASTExplicitConstructorInvocation;
36
import net.sourceforge.pmd.lang.java.ast.ASTExpression;
37
import net.sourceforge.pmd.lang.java.ast.ASTFieldAccess;
38
import net.sourceforge.pmd.lang.java.ast.ASTForeachStatement;
39
import net.sourceforge.pmd.lang.java.ast.ASTFormalParameter;
40
import net.sourceforge.pmd.lang.java.ast.ASTInfixExpression;
41
import net.sourceforge.pmd.lang.java.ast.ASTLambdaExpression;
42
import net.sourceforge.pmd.lang.java.ast.ASTLambdaParameter;
43
import net.sourceforge.pmd.lang.java.ast.ASTMethodCall;
44
import net.sourceforge.pmd.lang.java.ast.ASTMethodReference;
45
import net.sourceforge.pmd.lang.java.ast.ASTNullLiteral;
46
import net.sourceforge.pmd.lang.java.ast.ASTNumericLiteral;
47
import net.sourceforge.pmd.lang.java.ast.ASTPatternExpression;
48
import net.sourceforge.pmd.lang.java.ast.ASTPatternList;
49
import net.sourceforge.pmd.lang.java.ast.ASTRecordComponent;
50
import net.sourceforge.pmd.lang.java.ast.ASTRecordPattern;
51
import net.sourceforge.pmd.lang.java.ast.ASTStringLiteral;
52
import net.sourceforge.pmd.lang.java.ast.ASTSuperExpression;
53
import net.sourceforge.pmd.lang.java.ast.ASTSwitchExpression;
54
import net.sourceforge.pmd.lang.java.ast.ASTSwitchLabel;
55
import net.sourceforge.pmd.lang.java.ast.ASTSwitchLike;
56
import net.sourceforge.pmd.lang.java.ast.ASTThisExpression;
57
import net.sourceforge.pmd.lang.java.ast.ASTType;
58
import net.sourceforge.pmd.lang.java.ast.ASTTypeDeclaration;
59
import net.sourceforge.pmd.lang.java.ast.ASTTypeExpression;
60
import net.sourceforge.pmd.lang.java.ast.ASTTypeParameter;
61
import net.sourceforge.pmd.lang.java.ast.ASTTypePattern;
62
import net.sourceforge.pmd.lang.java.ast.ASTUnaryExpression;
63
import net.sourceforge.pmd.lang.java.ast.ASTUnnamedPattern;
64
import net.sourceforge.pmd.lang.java.ast.ASTVariableAccess;
65
import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator;
66
import net.sourceforge.pmd.lang.java.ast.ASTVariableId;
67
import net.sourceforge.pmd.lang.java.ast.ASTVoidType;
68
import net.sourceforge.pmd.lang.java.ast.BinaryOp;
69
import net.sourceforge.pmd.lang.java.ast.InternalApiBridge;
70
import net.sourceforge.pmd.lang.java.ast.JavaNode;
71
import net.sourceforge.pmd.lang.java.ast.JavaVisitorBase;
72
import net.sourceforge.pmd.lang.java.ast.TypeNode;
73
import net.sourceforge.pmd.lang.java.internal.JavaAstProcessor;
74
import net.sourceforge.pmd.lang.java.symbols.JClassSymbol;
75
import net.sourceforge.pmd.lang.java.symbols.JFieldSymbol;
76
import net.sourceforge.pmd.lang.java.symbols.JLocalVariableSymbol;
77
import net.sourceforge.pmd.lang.java.symbols.JRecordComponentSymbol;
78
import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol;
79
import net.sourceforge.pmd.lang.java.symbols.table.coreimpl.NameResolver;
80
import net.sourceforge.pmd.lang.java.symbols.table.internal.JavaSemanticErrors;
81
import net.sourceforge.pmd.lang.java.types.JArrayType;
82
import net.sourceforge.pmd.lang.java.types.JClassType;
83
import net.sourceforge.pmd.lang.java.types.JMethodSig;
84
import net.sourceforge.pmd.lang.java.types.JTypeMirror;
85
import net.sourceforge.pmd.lang.java.types.JVariableSig;
86
import net.sourceforge.pmd.lang.java.types.JVariableSig.FieldSig;
87
import net.sourceforge.pmd.lang.java.types.Substitution;
88
import net.sourceforge.pmd.lang.java.types.TypeConversion;
89
import net.sourceforge.pmd.lang.java.types.TypeOps;
90
import net.sourceforge.pmd.lang.java.types.TypeSystem;
91
import net.sourceforge.pmd.lang.java.types.TypesFromReflection;
92
import net.sourceforge.pmd.lang.java.types.TypingContext;
93
import net.sourceforge.pmd.lang.java.types.ast.ExprContext;
94
import net.sourceforge.pmd.lang.java.types.ast.ExprContext.ExprContextKind;
95
import net.sourceforge.pmd.lang.java.types.internal.infer.Infer;
96
import net.sourceforge.pmd.lang.java.types.internal.infer.TypeInferenceLogger;
97
import net.sourceforge.pmd.util.AssertionUtil;
98

99
/**
100
 * Resolves types of expressions. This is used as the implementation of
101
 * {@link TypeNode#getTypeMirror(TypingContext)} and is INTERNAL.
102
 */
103
public final class LazyTypeResolver extends JavaVisitorBase<TypingContext, @NonNull JTypeMirror> {
1✔
104

105
    private final TypeSystem ts;
106
    private final PolyResolution polyResolution;
107
    private final JClassType stringType;
108
    private final JavaAstProcessor processor;
109
    private final SemanticErrorReporter err;
110
    private final Infer infer;
111

112

113
    public LazyTypeResolver(JavaAstProcessor processor,
114
                            TypeInferenceLogger logger) {
1✔
115
        this.ts = processor.getTypeSystem();
1✔
116
        this.infer = new Infer(ts, processor.getJdkVersion(), logger);
1✔
117
        this.polyResolution = new PolyResolution(infer);
1✔
118
        this.stringType = (JClassType) TypesFromReflection.fromReflect(String.class, ts);
1✔
119
        this.processor = processor;
1✔
120
        this.err = processor.getLogger();
1✔
121
    }
1✔
122

123
    public ExprContext getConversionContextForExternalUse(ASTExpression e) {
124
        return polyResolution.getConversionContextForExternalUse(e);
1✔
125
    }
126

127
    public ExprContext getTopLevelContextIncludingInvocation(TypeNode e) {
128
        ExprContext toplevel = polyResolution.getTopLevelConversionContext(e);
1✔
129

130
        while (toplevel.hasKind(ExprContextKind.INVOCATION)) {
1✔
131
            ExprContext surrounding = polyResolution.getTopLevelConversionContext(toplevel.getInvocNodeIfInvocContext());
1✔
132
            if (!surrounding.isMissing()) {
1✔
133
                toplevel = surrounding;
1✔
134
            } else {
135
                break;
136
            }
137
        }
1✔
138
        return toplevel;
1✔
139
    }
140

141
    public Infer getInfer() {
142
        return infer;
1✔
143
    }
144

145
    public JavaAstProcessor getProcessor() {
146
        return processor;
1✔
147
    }
148

149
    public TypeSystem getTypeSystem() {
150
        return ts;
1✔
151
    }
152

153
    @Override
154
    public JTypeMirror visitJavaNode(JavaNode node, TypingContext ctx) {
155
        throw new IllegalArgumentException("Not a type node:" + node);
×
156
    }
157

158
    @Override
159
    public JTypeMirror visit(ASTFormalParameter node, TypingContext ctx) {
160
        return node.getVarId().getTypeMirror(ctx);
×
161
    }
162

163

164
    @Override
165
    public JTypeMirror visit(ASTTypeParameter node, TypingContext ctx) {
166
        return node.getSymbol().getTypeMirror();
1✔
167
    }
168

169
    @Override
170
    public JTypeMirror visitTypeDecl(ASTTypeDeclaration node, TypingContext ctx) {
171
        return ts.declaration(node.getSymbol());
1✔
172
    }
173

174
    @Override
175
    public JTypeMirror visit(ASTAnnotation node, TypingContext ctx) {
176
        JTypeMirror ty = node.getTypeNode().getTypeMirror(ctx);
1✔
177
        if (ty instanceof JClassType) {
1✔
178
            return ty;
1✔
179
        }
180
        // we throw this error because it would cause
181
        // class cast exceptions down the line if we don't abort now.
182
        throw getProcessor().getLogger().error(node, JavaSemanticErrors.EXPECTED_ANNOTATION_TYPE);
1✔
183
    }
184

185
    @Override
186
    public JTypeMirror visitType(ASTType node, TypingContext ctx) {
187
        return InternalApiBridge.buildTypeFromAstInternal(ts, Substitution.EMPTY, node);
1✔
188
    }
189

190
    @Override
191
    public JTypeMirror visit(ASTVoidType node, TypingContext ctx) {
192
        return ts.NO_TYPE;
1✔
193
    }
194

195

196
    @Override
197
    public @NonNull JTypeMirror visit(ASTRecordPattern node, TypingContext data) {
198
        JTypeMirror type = node.getTypeNode().getTypeMirror();
1✔
199
        if (node.getParent() instanceof ASTSwitchLabel) {
1✔
200
            // If the parent is a switch label, and the type
201
            // is generic, the type arguments of the type are
202
            // found in the scrutinee expression
203

204
            JTypeMirror scrutineeType = node.ancestors(ASTSwitchLike.class).firstOrThrow()
1✔
205
                                         .getTestedExpression().getTypeMirror(data);
1✔
206
            JTypeDeclSymbol symbol = type.getSymbol();
1✔
207
            if (symbol instanceof JClassSymbol) {
1!
208
                JTypeMirror inferred = infer.inferParameterizationForSubtype((JClassSymbol) symbol, scrutineeType);
1✔
209
                return TypeConversion.capture(inferred);
1✔
210
            }
211
        }
212
        return type;
1✔
213
    }
214

215

216
    @Override
217
    public @NonNull JTypeMirror visit(ASTTypePattern node, TypingContext data) {
218
        return node.getVarId().getTypeMirror();
1✔
219
    }
220

221

222
    @Override
223
    public @NonNull JTypeMirror visit(ASTUnnamedPattern node, TypingContext data) {
224
        if (node.getParent() instanceof ASTPatternList && node.getParent().getParent() instanceof ASTRecordPattern) {
1!
225
            return getTypeOfRecordComponent((ASTRecordPattern) node.getParent().getParent(), node.getIndexInParent());
1✔
226
        }
227
        // not allowed in any other context for now
228
        return ts.ERROR;
×
229
    }
230

231

232
    @Override
233
    public JTypeMirror visit(ASTVariableId node, TypingContext ctx) {
234
        boolean isTypeInferred = node.isTypeInferred();
1✔
235
        if (isTypeInferred && node.getInitializer() != null) {
1✔
236
            // var k = foo()
237

238
            ASTExpression initializer = node.getInitializer();
1✔
239
            return initializer == null ? ts.ERROR : TypeOps.projectUpwards(initializer.getTypeMirror(ctx));
1!
240

241
        } else if (isTypeInferred && node.isForeachVariable()) {
1✔
242
            // for (var k : map.keySet())
243

244
            ASTForeachStatement foreachStmt = node.ancestors(ASTForeachStatement.class).firstOrThrow();
1✔
245
            JTypeMirror iterableType = foreachStmt.getIterableExpr().getTypeMirror(ctx);
1✔
246
            iterableType = capture(iterableType);
1✔
247

248
            if (iterableType instanceof JArrayType) {
1✔
249
                return ((JArrayType) iterableType).getComponentType(); // component type is necessarily a type
1✔
250
            } else {
251
                JTypeMirror asSuper = iterableType.getAsSuper(ts.getClassSymbol(Iterable.class));
1✔
252
                if (asSuper instanceof JClassType) {
1✔
253
                    if (asSuper.isRaw()) {
1!
254
                        return ts.OBJECT;
×
255
                    }
256
                    JTypeMirror componentType = ((JClassType) asSuper).getTypeArgs().get(0);
1✔
257
                    return TypeOps.projectUpwards(componentType);
1✔
258
                } else {
259
                    return ts.ERROR;
1✔
260
                }
261
            }
262

263
        } else if (isTypeInferred && node.isLambdaParameter()) {
1✔
264
            ASTLambdaParameter param = (ASTLambdaParameter) node.getParent();
1✔
265
            ASTLambdaExpression lambda = (ASTLambdaExpression) node.ancestors().get(2);
1✔
266
            JTypeMirror contextualResult = ctx.apply(node.getSymbol());
1✔
267
            if (contextualResult != null) {
1!
268
                return contextualResult;
×
269
            }
270
            // force resolution of the enclosing lambda
271
            JMethodSig mirror = lambda.getFunctionalMethod();
1✔
272
            if (isUnresolved(mirror)) {
1✔
273
                return ts.UNKNOWN;
1✔
274
            }
275
            JTypeMirror parmty = mirror.getFormalParameters().get(param.getIndexInParent());
1✔
276
            // project upwards to remove captures
277
            return TypeOps.projectUpwards(parmty);
1✔
278

279
        } else if (node.isEnumConstant()) {
1✔
280

281
            TypeNode enumClass = node.getEnclosingType();
1✔
282
            return enumClass.getTypeMirror(ctx);
1✔
283

284
        } else if (isTypeInferred && node.isPatternBinding()) {
1!
285
            JavaNode parent = node.getParent();
1✔
286
            if (parent instanceof ASTTypePattern) {
1!
287
                // we should be in a record pattern, it's illegal
288
                // to use var as a type outside of there
289
                if (parent.getParent() instanceof ASTPatternList
1!
290
                    && parent.getParent().getParent() instanceof ASTRecordPattern) {
1!
291
                    return getTypeOfRecordComponent((ASTRecordPattern) parent.getParent().getParent(),
1✔
292
                                                    parent.getIndexInParent());
1✔
293
                } else if (parent.getParent() instanceof ASTTypeExpression
×
294
                    && parent.getParent().getParent() instanceof ASTInfixExpression) {
×
295
                    // in instanceof
296
                    return ((ASTInfixExpression) parent.getParent().getParent())
×
297
                        .getLeftOperand().getTypeMirror(ctx);
×
298
                }
299
            }
300
            return ts.ERROR;
×
301
        }
302

303
        ASTType typeNode = node.getTypeNode();
1✔
304
        if (typeNode == null) {
1!
305
            return ts.ERROR;
×
306
        }
307

308
        // Type common to all declarations in the same statement
309
        JTypeMirror baseType = typeNode.getTypeMirror(ctx);
1✔
310
        ASTArrayDimensions extras = node.getExtraDimensions();
1✔
311

312
        return extras != null
1✔
313
               ? ts.arrayType(baseType, extras.size())
1✔
314
               : baseType;
1✔
315
    }
316

317

318
    private JTypeMirror getTypeOfRecordComponent(ASTRecordPattern record, int compIndex) {
319
        JTypeMirror type = record.getTypeMirror();
1✔
320
        @Nullable JTypeDeclSymbol recordType = type.getSymbol();
1✔
321
        if (recordType instanceof JClassSymbol && type instanceof JClassType) {
1!
322
            if (recordType.isUnresolved()) {
1!
323
                return ts.UNKNOWN;
×
324
            }
325
            List<JRecordComponentSymbol> components = ((JClassSymbol) recordType).getRecordComponents();
1✔
326
            if (compIndex < components.size()) {
1!
327
                JRecordComponentSymbol sym = components.get(compIndex);
1✔
328
                return ((JClassType) type).getDeclaredField(sym.getSimpleName()).getTypeMirror();
1✔
329
            }
330
        }
331
        return ts.ERROR;
×
332
    }
333

334

335
    @Override
336
    public @NonNull JTypeMirror visit(ASTRecordComponent node, TypingContext data) {
337
        return node.getVarId().getTypeMirror();
1✔
338
    }
339

340
    /*
341
        EXPRESSIONS
342
     */
343

344
    @Override
345
    public JTypeMirror visit(ASTAssignmentExpression node, TypingContext ctx) {
346
        // The type of the assignment expression is the type of the variable after capture conversion
347
        return TypeConversion.capture(node.getLeftOperand().getTypeMirror(ctx));
1✔
348
    }
349

350
    /**
351
     * Poly expressions need context and are resolved by {@link PolyResolution}.
352
     *
353
     * <p>Note that some poly expression are only poly part of the time.
354
     * In particular, method calls with explicit type arguments, and non-diamond
355
     * constructor calls, are standalone. To reduce the number of branches in the
356
     * code they still go through Infer, so that their method type is set like all
357
     * the others.
358
     */
359
    private JTypeMirror handlePoly(TypeNode node) {
360
        return polyResolution.computePolyType(node);
1✔
361
    }
362

363
    @Override
364
    public JTypeMirror visit(ASTMethodCall node, TypingContext ctx) {
365
        return handlePoly(node);
1✔
366
    }
367

368
    @Override
369
    public JTypeMirror visit(ASTConditionalExpression node, TypingContext ctx) {
370
        return handlePoly(node);
1✔
371
    }
372

373
    @Override
374
    public JTypeMirror visit(ASTLambdaExpression node, TypingContext ctx) {
375
        return handlePoly(node);
1✔
376
    }
377

378
    @Override
379
    public JTypeMirror visit(ASTSwitchExpression node, TypingContext ctx) {
380
        return handlePoly(node);
1✔
381
    }
382

383
    @Override
384
    public JTypeMirror visit(ASTMethodReference node, TypingContext ctx) {
385
        return handlePoly(node);
1✔
386
    }
387

388
    @Override
389
    public JTypeMirror visit(ASTConstructorCall node, TypingContext ctx) {
390
        return handlePoly(node);
1✔
391
    }
392

393
    @Override
394
    public JTypeMirror visit(ASTExplicitConstructorInvocation node, TypingContext ctx) {
395
        return handlePoly(node);
1✔
396
    }
397

398
    @Override
399
    public JTypeMirror visit(ASTEnumConstant node, TypingContext ctx) {
400
        return handlePoly(node);
1✔
401
    }
402

403
    @Override
404
    public JTypeMirror visit(ASTInfixExpression node, TypingContext ctx) {
405
        BinaryOp op = node.getOperator();
1✔
406
        switch (op) {
1!
407
        case CONDITIONAL_OR:
408
        case CONDITIONAL_AND:
409
        case EQ:
410
        case NE:
411
        case LE:
412
        case GE:
413
        case GT:
414
        case INSTANCEOF:
415
        case LT:
416
            // HMM so we don't even check?
417
            return ts.BOOLEAN;
1✔
418
        case OR:
419
        case XOR:
420
        case AND: {
421
            // those may be boolean or bitwise
422
            final JTypeMirror lhs = node.getLeftOperand().getTypeMirror(ctx).unbox();
1✔
423
            final JTypeMirror rhs = node.getRightOperand().getTypeMirror(ctx).unbox();
1✔
424

425
            if (lhs.isNumeric() && rhs.isNumeric()) {
1✔
426
                // NUMERIC(N) & NUMERIC(M)  -> promote(N, M)
427
                return binaryNumericPromotion(lhs, rhs);
1✔
428
            } else if (lhs.equals(rhs)) {
1✔
429
                // BOOL       & BOOL        -> BOOL
430
                // UNRESOLVED & UNRESOLVED  -> UNKNOWN
431
                return lhs;
1✔
432
            } else if (isUnresolved(lhs) ^ isUnresolved(rhs)) {
1✔
433
                // UNRESOLVED & NUMERIC(N)  -> promote(N)
434
                // NUMERIC(N) & UNRESOLVED  -> promote(N)
435

436
                // BOOL       & UNRESOLVED  -> BOOL
437
                // UNRESOLVED & BOOL        -> BOOL
438

439
                // UNRESOLVED & anything    -> ERROR
440

441
                JTypeMirror resolved = isUnresolved(lhs) ? rhs : lhs;
1✔
442
                return resolved.isNumeric() ? unaryNumericPromotion(resolved)
1!
443
                                            : resolved == ts.BOOLEAN ? resolved
×
444
                                                                     : ts.ERROR;
×
445
            } else {
446
                // anything else, including error types & such: ERROR
447
                return ts.ERROR;
1✔
448
            }
449
        }
450
        case LEFT_SHIFT:
451
        case RIGHT_SHIFT:
452
        case UNSIGNED_RIGHT_SHIFT:
453
            return unaryNumericPromotion(node.getLeftOperand().getTypeMirror(ctx));
1✔
454
        case ADD:
455
        case SUB:
456
        case MUL:
457
        case DIV:
458
        case MOD:
459
            final JTypeMirror lhs = node.getLeftOperand().getTypeMirror(ctx);
1✔
460
            final JTypeMirror rhs = node.getRightOperand().getTypeMirror(ctx);
1✔
461
            if (op == ADD && (lhs.equals(stringType) || rhs.equals(stringType))) {
1✔
462
                // string concatenation
463
                return stringType;
1✔
464
            } else if (isUnresolved(lhs)) {
1✔
465
                return rhs;
1✔
466
            } else if (isUnresolved(rhs)) {
1✔
467
                return lhs;
1✔
468
            } else {
469
                return binaryNumericPromotion(lhs, rhs);
1✔
470
            }
471
        }
NEW
472
        throw AssertionUtil.shouldNotReachHere("Unknown operator for " + node);
×
473
    }
474

475
    private boolean isUnresolved(JTypeMirror t) {
476
        return t == ts.UNKNOWN;
1✔
477
    }
478

479
    private boolean isUnresolved(JMethodSig m) {
480
        return m == null || m == ts.UNRESOLVED_METHOD;
1!
481
    }
482

483
    @Override
484
    public JTypeMirror visit(ASTUnaryExpression node, TypingContext ctx) {
485
        switch (node.getOperator()) {
1!
486
        case UNARY_PLUS:
487
        case UNARY_MINUS:
488
        case COMPLEMENT:
489
            return unaryNumericPromotion(node.getOperand().getTypeMirror(ctx));
1✔
490
        case NEGATION:
491
            return ts.BOOLEAN;
1✔
492
        case PRE_INCREMENT:
493
        case PRE_DECREMENT:
494
        case POST_INCREMENT:
495
        case POST_DECREMENT:
496
            return node.getOperand().getTypeMirror(ctx);
1✔
497
        }
NEW
498
        throw AssertionUtil.shouldNotReachHere("Unknown operator for " + node);
×
499
    }
500

501
    @Override
502
    public JTypeMirror visit(ASTPatternExpression node, TypingContext ctx) {
503
        return node.getPattern().getTypeMirror(ctx);
1✔
504
    }
505

506
    @Override
507
    public JTypeMirror visit(ASTCastExpression node, TypingContext ctx) {
508
        return node.getCastType().getTypeMirror(ctx);
1✔
509
    }
510

511
    @Override
512
    public JTypeMirror visit(ASTNullLiteral node, TypingContext ctx) {
513
        return ts.NULL_TYPE;
1✔
514
    }
515

516
    @Override
517
    public JTypeMirror visit(ASTCharLiteral node, TypingContext ctx) {
518
        return ts.CHAR;
1✔
519
    }
520

521
    @Override
522
    public JTypeMirror visit(ASTStringLiteral node, TypingContext ctx) {
523
        return stringType;
1✔
524
    }
525

526
    @Override
527
    public JTypeMirror visit(ASTNumericLiteral node, TypingContext ctx) {
528
        if (node.isIntegral()) {
1✔
529
            return node.isLongLiteral() ? ts.LONG : ts.INT;
1✔
530
        } else {
531
            return node.isFloatLiteral() ? ts.FLOAT : ts.DOUBLE;
1✔
532
        }
533
    }
534

535
    @Override
536
    public JTypeMirror visit(ASTBooleanLiteral node, TypingContext ctx) {
537
        return ts.BOOLEAN;
1✔
538
    }
539

540
    @Override
541
    public JTypeMirror visit(ASTClassLiteral node, TypingContext ctx) {
542
        JClassSymbol klassSym = ts.getClassSymbol(Class.class);
1✔
543
        assert klassSym != null : Class.class + " is missing from the classpath?";
1!
544
        if (node.getTypeNode() instanceof ASTVoidType) {
1✔
545
            // void.class : Class<Void>
546
            return ts.parameterise(klassSym, listOf(ts.BOXED_VOID));
1✔
547
        } else {
548
            return ts.parameterise(klassSym, listOf(node.getTypeNode().getTypeMirror(ctx).box()));
1✔
549
        }
550
    }
551

552

553
    @Override
554
    public JTypeMirror visit(ASTArrayAllocation node, TypingContext ctx) {
555
        return node.getTypeNode().getTypeMirror(ctx);
1✔
556
    }
557

558
    @Override
559
    public JTypeMirror visit(ASTArrayInitializer node, TypingContext ctx) {
560
        JavaNode parent = node.getParent();
1✔
561
        if (parent instanceof ASTArrayAllocation) {
1✔
562
            return ((ASTArrayAllocation) parent).getTypeMirror(ctx);
1✔
563
        } else if (parent instanceof ASTVariableDeclarator) {
1✔
564
            ASTVariableId id = ((ASTVariableDeclarator) parent).getVarId();
1✔
565
            return id.isTypeInferred() ? ts.ERROR : id.getTypeMirror(ctx);
1!
566
        } else if (parent instanceof ASTArrayInitializer) {
1!
567
            JTypeMirror tm = ((ASTArrayInitializer) parent).getTypeMirror(ctx);
1✔
568
            return tm instanceof JArrayType ? ((JArrayType) tm).getComponentType()
1!
569
                                            : ts.ERROR;
×
570
        }
571
        return ts.ERROR;
×
572
    }
573

574

575
    @Override
576
    public JTypeMirror visit(ASTVariableAccess node, TypingContext ctx) {
577
        if (node.getParent() instanceof ASTSwitchLabel) {
1✔
578
            // may be an enum constant, in which case symbol table doesn't help (this is documented on JSymbolTable#variables())
579
            ASTSwitchLike switchParent = node.ancestors(ASTSwitchLike.class).firstOrThrow();
1✔
580
            JTypeMirror testedType = switchParent.getTestedExpression().getTypeMirror(ctx);
1✔
581
            JTypeDeclSymbol testedSym = testedType.getSymbol();
1✔
582
            if (testedSym instanceof JClassSymbol && ((JClassSymbol) testedSym).isEnum()) {
1!
583
                JFieldSymbol enumConstant = ((JClassSymbol) testedSym).getDeclaredField(node.getName());
1✔
584
                if (enumConstant != null) {
1!
585
                    // field exists and can be resolved
586
                    InternalApiBridge.setTypedSym(node, ts.sigOf(testedType, enumConstant));
1✔
587
                }
588
                return testedType;
1✔
589
            } // fallthrough
590
        }
591

592
        @Nullable JVariableSig result = node.getSymbolTable().variables().resolveFirst(node.getName());
1✔
593
        if (result == null) {
1✔
594
            // An out-of-scope field. Use context to resolve it.
595
            return polyResolution.getContextTypeForStandaloneFallback(node);
1✔
596
        }
597
        InternalApiBridge.setTypedSym(node, result);
1✔
598

599
        JTypeMirror resultMirror = null;
1✔
600

601
        if (result.getSymbol() instanceof JLocalVariableSymbol) {
1✔
602
            ASTVariableId id = result.getSymbol().tryGetNode();
1✔
603
            // id may be null if this is a fake formal param sym, for record components
604
            if (id != null && id.isLambdaParameter()) {
1!
605
                // then the type of the parameter depends on the type
606
                // of the lambda, which most likely depends on the overload
607
                // resolution of an enclosing invocation context
608
                resultMirror = id.getTypeMirror();
1✔
609
            }
610
        }
611

612
        if (resultMirror == null) {
1✔
613
            resultMirror = result.getTypeMirror();
1✔
614
        }
615

616
        // https://docs.oracle.com/javase/specs/jls/se14/html/jls-6.html#jls-6.5.6
617
        // Only capture if the name is on the RHS
618
        return node.getAccessType() == AccessType.READ ? TypeConversion.capture(resultMirror)
1✔
619
                                                       : resultMirror;
1✔
620
    }
621

622
    @Override
623
    public @NonNull JTypeMirror visit(ASTLambdaParameter node, TypingContext ctx) {
624
        if (node.getTypeNode() != null) {
1✔
625
            // explicitly typed
626
            return node.getTypeNode().getTypeMirror(ctx);
1✔
627
        }
628
        ASTLambdaExpression lambda = node.ancestors(ASTLambdaExpression.class).firstOrThrow();
1✔
629
        lambda.getTypeMirror(ctx);
1✔
630

631
        JMethodSig m = lambda.getFunctionalMethod(); // this forces resolution of the lambda
1✔
632
        if (!isUnresolved(m)) {
1✔
633
            if (m.getArity() != node.getOwner().getArity()) {
1!
634
                err.warning(node.getOwner(), "Lambda shape does not conform to the functional method {0}", m);
×
635
                return ts.ERROR;
×
636
            }
637
            return m.getFormalParameters().get(node.getIndexInParent());
1✔
638
        }
639
        return ts.UNKNOWN;
1✔
640
    }
641

642
    @Override
643
    public JTypeMirror visit(ASTFieldAccess node, TypingContext ctx) {
644
        JTypeMirror qualifierT = TypeOps.getMemberSource(node.getQualifier().getTypeMirror(ctx));
1✔
645
        if (isUnresolved(qualifierT)) {
1✔
646
            return polyResolution.getContextTypeForStandaloneFallback(node);
1✔
647
        }
648

649
        @Nullable ASTTypeDeclaration enclosingType = node.getEnclosingType();
1✔
650
        @Nullable JClassSymbol enclosingSymbol =
651
            enclosingType == null ? null : enclosingType.getSymbol();
1✔
652
        NameResolver<FieldSig> fieldResolver =
1✔
653
            TypeOps.getMemberFieldResolver(qualifierT, node.getRoot().getPackageName(), enclosingSymbol, node.getName());
1✔
654

655
        FieldSig sig = fieldResolver.resolveFirst(node.getName()); // could be an ambiguity error
1✔
656
        InternalApiBridge.setTypedSym(node, sig);
1✔
657

658
        if (sig == null) {
1✔
659
            return polyResolution.getContextTypeForStandaloneFallback(node);
1✔
660
        }
661

662
        // https://docs.oracle.com/javase/specs/jls/se14/html/jls-6.html#jls-6.5.6
663
        // Only capture if the name is on the RHS
664
        return node.getAccessType() == AccessType.READ ? TypeConversion.capture(sig.getTypeMirror())
1✔
665
                                                       : sig.getTypeMirror();
1✔
666
    }
667

668

669
    @Override
670
    public JTypeMirror visit(ASTArrayAccess node, TypingContext ctx) {
671
        JTypeMirror compType;
672
        JTypeMirror arrType = node.getQualifier().getTypeMirror(ctx);
1✔
673
        if (arrType instanceof JArrayType) {
1✔
674
            compType = ((JArrayType) arrType).getComponentType();
1✔
675
        } else if (isUnresolved(arrType)) {
1✔
676
            compType = polyResolution.getContextTypeForStandaloneFallback(node);
1✔
677
        } else {
678
            compType = ts.ERROR;
1✔
679
        }
680
        return capture(compType);
1✔
681
    }
682

683
    @Override
684
    public JTypeMirror visit(ASTSuperExpression node, TypingContext ctx) {
685
        if (node.getQualifier() != null) {
1✔
686
            return node.getQualifier().getTypeMirror(ctx);
1✔
687
        } else {
688
            return ((JClassType) node.getEnclosingType().getTypeMirror(ctx)).getSuperClass();
1✔
689
        }
690
    }
691

692
    @Override
693
    public JTypeMirror visit(ASTThisExpression node, TypingContext ctx) {
694
        return node.getQualifier() != null
1✔
695
               ? node.getQualifier().getTypeMirror(ctx)
1✔
696
               : node.getEnclosingType().getTypeMirror(ctx);
1✔
697
    }
698

699
    @Override
700
    public JTypeMirror visit(ASTAmbiguousName node, TypingContext ctx) {
701
        return ts.UNKNOWN;
1✔
702
    }
703
}
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