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

pmd / pmd / 4476

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

push

github

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

Merge pull request #5425 from mitchspano:stateful

17395 of 23322 branches covered (74.59%)

Branch coverage included in aggregate %.

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

99 existing lines in 9 files now uncovered.

38180 of 48187 relevant lines covered (79.23%)

0.8 hits per line

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

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

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
            JTypeMirror parmty = mirror.getFormalParameters().get(param.getIndexInParent());
1✔
275
            // project upwards to remove captures
276
            return TypeOps.projectUpwards(parmty);
1✔
277

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

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

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

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

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

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

316

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

333

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

339
    /*
340
        EXPRESSIONS
341
     */
342

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

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

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

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

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

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

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

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

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

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

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

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

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

438
                // UNRESOLVED & anything    -> ERROR
439

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

471
        default:
UNCOV
472
            throw new AssertionError("Unknown operator for " + node);
×
473
        }
474
    }
475

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

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

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

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

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

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

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

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

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

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

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

554

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

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

576

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

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

601
        JTypeMirror resultMirror = null;
1✔
602

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

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

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

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

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

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

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

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

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

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

670

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

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

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

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