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

pmd / pmd / 4380

30 Jan 2025 09:37AM UTC coverage: 77.689% (+0.005%) from 77.684%
4380

push

github

adangel
[java] Fix tests

17353 of 23278 branches covered (74.55%)

Branch coverage included in aggregate %.

38134 of 48144 relevant lines covered (79.21%)

0.8 hits per line

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

86.5
/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

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

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

111

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

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

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

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

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

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

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

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

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

162

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

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

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

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

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

194

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

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

214

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

220

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

230

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

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

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

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

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

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

276
        } else if (node.isEnumConstant()) {
1✔
277

278
            TypeNode enumClass = node.getEnclosingType();
1✔
279
            return enumClass.getTypeMirror(ctx);
1✔
280

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

300
        ASTType typeNode = node.getTypeNode();
1✔
301
        if (typeNode == null) {
1!
302
            return ts.ERROR;
×
303
        }
304

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

309
        return extras != null
1✔
310
               ? ts.arrayType(baseType, extras.size())
1✔
311
               : baseType;
1✔
312
    }
313

314

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

331

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

337
    /*
338
        EXPRESSIONS
339
     */
340

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

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

360
    @Override
361
    public JTypeMirror visit(ASTMethodCall node, TypingContext ctx) {
362
        return handlePoly(node);
1✔
363
    }
364

365
    @Override
366
    public JTypeMirror visit(ASTConditionalExpression node, TypingContext ctx) {
367
        return handlePoly(node);
1✔
368
    }
369

370
    @Override
371
    public JTypeMirror visit(ASTLambdaExpression node, TypingContext ctx) {
372
        return handlePoly(node);
1✔
373
    }
374

375
    @Override
376
    public JTypeMirror visit(ASTSwitchExpression node, TypingContext ctx) {
377
        return handlePoly(node);
1✔
378
    }
379

380
    @Override
381
    public JTypeMirror visit(ASTMethodReference node, TypingContext ctx) {
382
        return handlePoly(node);
1✔
383
    }
384

385
    @Override
386
    public JTypeMirror visit(ASTConstructorCall node, TypingContext ctx) {
387
        return handlePoly(node);
1✔
388
    }
389

390
    @Override
391
    public JTypeMirror visit(ASTExplicitConstructorInvocation node, TypingContext ctx) {
392
        return handlePoly(node);
1✔
393
    }
394

395
    @Override
396
    public JTypeMirror visit(ASTEnumConstant node, TypingContext ctx) {
397
        return handlePoly(node);
1✔
398
    }
399

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

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

433
                // BOOL       & UNRESOLVED  -> BOOL
434
                // UNRESOLVED & BOOL        -> BOOL
435

436
                // UNRESOLVED & anything    -> ERROR
437

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

469
        default:
470
            throw new AssertionError("Unknown operator for " + node);
×
471
        }
472
    }
473

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

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

482
    @Override
483
    public JTypeMirror visit(ASTUnaryExpression node, TypingContext ctx) {
484
        switch (node.getOperator()) {
1!
485
        case UNARY_PLUS:
486
        case UNARY_MINUS:
487
        case COMPLEMENT:
488
            return unaryNumericPromotion(node.getOperand().getTypeMirror(ctx));
1✔
489
        case NEGATION:
490
            return ts.BOOLEAN;
1✔
491
        case PRE_INCREMENT:
492
        case PRE_DECREMENT:
493
        case POST_INCREMENT:
494
        case POST_DECREMENT:
495
            return node.getOperand().getTypeMirror(ctx);
1✔
496
        default:
497
            throw new AssertionError("Unknown operator for " + node);
×
498
        }
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 = capture(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