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

pmd / pmd / 835

27 Sep 2026 08:36PM UTC coverage: 79.741% (-0.003%) from 79.744%
835

push

github

web-flow
[java] Fix constant classification of final variable references (#7062)

Co-authored-by: Sören Glimm <git@uncleowen.de>

20274 of 26466 branches covered (76.6%)

Branch coverage included in aggregate %.

31 of 31 new or added lines in 3 files covered. (100.0%)

4 existing lines in 2 files now uncovered.

43601 of 53637 relevant lines covered (81.29%)

0.82 hits per line

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

81.82
/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTExpression.java
1
/*
2
 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3
 */
4

5
package net.sourceforge.pmd.lang.java.ast;
6

7
import java.util.Objects;
8

9
import org.checkerframework.checker.nullness.qual.NonNull;
10
import org.checkerframework.checker.nullness.qual.Nullable;
11

12
import net.sourceforge.pmd.lang.java.types.ast.ExprContext;
13

14
/**
15
 * Represents an expression, in the most general sense.
16
 * This corresponds to the <a href="https://docs.oracle.com/javase/specs/jls/se9/html/jls-15.html#jls-Expression">Expression</a>
17
 * of the JLS.
18
 *
19
 * <p>From 7.0.0 on, this is an interface which all expression nodes
20
 * implement.
21
 *
22
 * <p>Expressions are required to be constant in some parts of the grammar
23
 * (in {@link ASTSwitchLabel SwitchLabel}, {@link ASTAnnotation Annotation},
24
 * {@link ASTDefaultValue DefaultValue}). A <i>constant expression</i> is
25
 * represented as a normal expression subtree, which does not feature any
26
 * {@link ASTMethodReference MethodReference}, {@link ASTLambdaExpression LambdaExpression}
27
 * or {@link ASTAssignmentExpression AssignmentExpression}.
28
 *
29
 *
30
 * <pre class="grammar">
31
 *
32
 * (: In increasing precedence order :)
33
 * Expression ::= {@link ASTAssignmentExpression AssignmentExpression}
34
 *              | {@link ASTConditionalExpression ConditionalExpression}
35
 *              | {@link ASTLambdaExpression LambdaExpression}
36
 *              | {@link ASTInfixExpression InfixExpression}
37
 *              | {@link ASTUnaryExpression PrefixExpression} | {@link ASTCastExpression CastExpression}
38
 *              | {@link ASTUnaryExpression PostfixExpression}
39
 *              | {@link ASTSwitchExpression SwitchExpression}
40
 *              | {@link ASTPrimaryExpression PrimaryExpression}
41
 *
42
 * </pre>
43
 */
44
public interface ASTExpression extends TypeNode, ASTMemberValue, ASTSwitchArrowRHS {
45

46
    /**
47
     * Always returns true. This is to allow XPath queries
48
     * to query like {@code /*[@Expression=true()]} to match
49
     * any expression, but is useless in Java code.
50
     */
51
    default boolean isExpression() {
52
        return true;
×
53
    }
54

55

56
    /**
57
     * Returns the number of parenthesis levels around this expression.
58
     * If this method returns 0, then no parentheses are present.
59
     *
60
     * <p>E.g. the expression {@code (a + b)} is parsed as an AdditiveExpression
61
     * whose parenthesisDepth is 1, and in {@code ((a + b))} it's 2.
62
     *
63
     * <p>This is to avoid the parentheses interfering with analysis.
64
     * Parentheses already influence parsing by breaking the natural
65
     * precedence of operators. It would mostly hide false positives
66
     * to make a ParenthesizedExpr node, because it would make semantically
67
     * equivalent nodes have a very different representation.
68
     *
69
     * <p>On the other hand, when a rule explicitly cares about parentheses,
70
     * then this attribute may be used to find out whether parentheses
71
     * were mentioned, so no information is lost.
72
     */
73
    int getParenthesisDepth();
74

75

76
    /**
77
     * Returns true if this expression has at least one level of parentheses.
78
     * The specific depth can be fetched with {@link #getParenthesisDepth()}.
79
     */
80
    default boolean isParenthesized() {
81
        return getParenthesisDepth() > 0;
1 ✔
82
    }
83

84
    /**
85
     * {@inheritDoc}
86
     *
87
     * <p>Note that even if this is null, the constant value may be known anyway but
88
     * technically not be a CT constant, because it uses non-static (final) fields
89
     * for instance. See {@link #getConstFoldingResult()} and {@link ConstResult} to
90
     * get a constant value in this case.
91
     *
92
     * @see #getConstFoldingResult() for more precise results.
93
     */
94
    @Override
95
    default @Nullable Object getConstValue() {
96
        ConstResult res = getConstFoldingResult();
1 ✔
97
        return res.isCompileTimeConstant() ? res.getValue() : null;
1 ✔
98
    }
99

100
    /**
101
     * Returns the result of constant folding on this expression. This may find a
102
     * constant value for more than strict compile-time constants. See {@link ConstResult}.
103
     *
104
     * @since 7.12.0
105
     */
106
    default @NonNull ConstResult getConstFoldingResult() {
107
        return ConstResult.NO_CONST_VALUE;
1 ✔
108
    }
109

110
    /**
111
     * Returns true if this expression is a compile-time constant, and is inlined.
112
     *
113
     * @see #getConstFoldingResult()
114
     */
115
    default boolean isCompileTimeConstant() {
116
        return getConstFoldingResult().isCompileTimeConstant();
1 ✔
117
    }
118

119
    /**
120
     * Returns the type expected by the context. This type may determine
121
     * an implicit conversion of this value to that type (eg a boxing
122
     * conversion, widening numeric conversion, or widening reference
123
     * conversion).
124
     *
125
     * <p>There are many different cases.
126
     * For example, in {@code arr['c']}, {@link #getTypeMirror()} would
127
     * return {@code char} for the char literal, but the context type
128
     * is {@code int} since it's used as an array index. Hence, a widening
129
     * conversion occurs. Similarly, the context type of an expression
130
     * in a return statement is the return type of the method, etc.
131
     *
132
     * <p>If the context is undefined, then the returned object will answer
133
     * true to {@link ExprContext#isMissing()}. This is completely normal
134
     * and needs to be accounted for by rules. For instance, it occurs
135
     * if this expression is used as a statement.
136
     *
137
     * <p>Note that conversions are a language-level construct only.
138
     * Converting from a type to another may not actually require any
139
     * concrete operation at runtime. For instance, converting a
140
     * {@code char} to an {@code int} is a noop at runtime, because chars
141
     * are anyway treated as ints by the JVM (within stack frames). A
142
     * boxing conversion will however in general translate to a call to
143
     * e.g. {@link Integer#valueOf(int)}.
144
     *
145
     * <p>Not all contexts allow all kinds of conversions. See
146
     * {@link ExprContext}.
147
     */
148
    default @NonNull ExprContext getConversionContext() {
149
        return getRoot().getLazyTypeResolver().getConversionContextForExternalUse(this);
1 ✔
150
    }
151

152
    /**
153
     * Result of constant folding an expression. This can be in one of three states:
154
     * <ul>
155
     * <li>No constant value: constant folding failed, meaning, the value of the expression is not known at compile time.
156
     * <li>Has compile-time constant value: there is a constant value, and it is a compile-time constant in the sense of the JLS.
157
     * Such constants are inlined in class files. References to constant variables are allowed: these are final
158
     * variables of primitive or String type initialized with a constant expression (JLS 4.12.4).
159
     * They may be local variables or fields, including non-static fields.
160
     * <li>Has value, not compile-time constant: we could compute a constant value, but it is not CT-constant in the sense
161
     * of the JLS. For instance, the initializer of a final boxed variable may have a known value,
162
     * but a reference to that variable is not a constant expression.
163
     * </ul>
164
     *
165
     * @since 7.12.0
166
     */
167
    final class ConstResult {
168
        private final boolean isCompileTimeConstant;
169
        private final @Nullable Object value;
170

171
        static final ConstResult NO_CONST_VALUE = new ConstResult(false, null);
1 ✔
172
        static final ConstResult BOOL_TRUE = ctConst(true);
1 ✔
173
        static final ConstResult BOOL_FALSE = ctConst(false);
1 ✔
174

175
        ConstResult(boolean isCompileTimeConstant, @Nullable Object value) {
1 ✔
176
            this.isCompileTimeConstant = isCompileTimeConstant && value != null;
1 ✔
177
            this.value = value;
1 ✔
178
        }
1 ✔
179

180
        static @NonNull ConstResult ctConst(@NonNull Object result) {
181
            return new ConstResult(true, Objects.requireNonNull(result));
1 ✔
182
        }
183

184
        static @NonNull ConstResult ctConstIfNotNull(@Nullable Object result) {
UNCOV
185
            if (result == null) {
×
UNCOV
186
                return NO_CONST_VALUE;
×
187
            }
UNCOV
188
            return new ConstResult(true, Objects.requireNonNull(result));
×
189
        }
190

191
        /**
192
         * If true, this value is a compile-time constant in the sense of the JLS. See class description.
193
         */
194
        public boolean isCompileTimeConstant() {
195
            return isCompileTimeConstant;
1 ✔
196
        }
197

198
        /**
199
         * If true, a constant value could be computed. It is not necessarily a CT-constant. See class description.
200
         */
201
        public boolean hasValue() {
202
            return value != null;
1 ✔
203
        }
204

205
        /**
206
         * Get the value, or null if no value could be computed. The value has one of the primitive wrapper types,
207
         * or String type.
208
         */
209
        public @Nullable Object getValue() {
210
            return value;
1 ✔
211
        }
212
    }
213
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc