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

pmd / pmd / 628

29 Jun 2026 06:59AM UTC coverage: 79.138% (-0.005%) from 79.143%
628

push

github

web-flow
[java] Fix #6740: Fix FP in OptimizableToArrayCall (#6813)

19169 of 25145 branches covered (76.23%)

Branch coverage included in aggregate %.

41548 of 51578 relevant lines covered (80.55%)

0.81 hits per line

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

84.71
/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMethodDeclaration.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.function.Predicate;
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.symbols.JMethodSymbol;
13
import net.sourceforge.pmd.lang.java.types.JMethodSig;
14
import net.sourceforge.pmd.lang.java.types.TypeSystem;
15
import net.sourceforge.pmd.lang.java.types.TypeTestUtil;
16
import net.sourceforge.pmd.lang.rule.xpath.DeprecatedAttribute;
17

18
/**
19
 * A method declaration, in a class or interface declaration. Since 7.0,
20
 * this also represents annotation methods. Annotation methods have a
21
 * much more restricted grammar though, in particular:
22
 * <ul>
23
 * <li>They can't declare a {@linkplain #getThrowsList() throws clause}
24
 * <li>They can't declare {@linkplain #getTypeParameters() type parameters}
25
 * <li>Their {@linkplain #getFormalParameters() formal parameters} must be empty
26
 * <li>They can't be declared void
27
 * <li>They must be abstract
28
 * </ul>
29
 * They can however declare a {@link #getDefaultClause() default value}.
30
 *
31
 * <pre class="grammar">
32
 *
33
 * MethodDeclaration ::= {@link ASTModifierList ModifierList}
34
 *                       {@link ASTTypeParameters TypeParameters}?
35
 *                       {@link ASTType Type}
36
 *                       &lt;IDENTIFIER&gt;
37
 *                       {@link ASTFormalParameters FormalParameters}
38
 *                       {@link ASTArrayDimensions ArrayDimensions}?
39
 *                       {@link ASTThrowsList ThrowsList}?
40
 *                       ({@link ASTBlock Block} | ";" )
41
 *
42
 * </pre>
43
 */
44
public final class ASTMethodDeclaration extends AbstractExecutableDeclaration<JMethodSymbol> {
45

46
    /**
47
     * Populated by {@link OverrideResolutionPass}.
48
     */
49
    private JMethodSig overriddenMethod = null;
1✔
50

51
    ASTMethodDeclaration(int id) {
52
        super(id);
1✔
53
    }
1✔
54

55
    @Override
56
    protected <P, R> R acceptVisitor(JavaVisitor<? super P, ? extends R> visitor, P data) {
57
        return visitor.visit(this, data);
1✔
58
    }
59

60
    /**
61
     * Returns true if this method overrides a method in a supertype.
62
     * @since 7.26.0
63
     */
64
    public boolean isOverride() {
65
        return overriddenMethod != null;
1✔
66
    }
67

68
    /**
69
     * @deprecated since 7.26.0. Use {@link #isOverride()} instead.
70
     */
71
    @Deprecated
72
    @DeprecatedAttribute(replaceWith = "@Override")
73
    public boolean isOverridden() {
74
        return isOverride();
×
75
    }
76

77
    /**
78
     * Returns the signature of the method this method overrides in a
79
     * supertype. Note that this method may be implementing several methods
80
     * of super-interfaces at once, in that case, an arbitrary one is returned.
81
     *
82
     * <p>If the method has an {@link Override} annotation, but we couldn't
83
     * resolve any method that is actually implemented, this will return
84
     * {@link TypeSystem#UNRESOLVED_METHOD}.
85
     */
86
    public JMethodSig getOverriddenMethod() {
87
        return overriddenMethod;
1✔
88
    }
89

90
    void setOverriddenMethod(JMethodSig overriddenMethod) {
91
        this.overriddenMethod = overriddenMethod;
1✔
92
    }
1✔
93

94
    /**
95
     * If this method declaration is an explicit record component accessor,
96
     * returns the corresponding record component. Otherwise returns null.
97
     */
98
    public @Nullable ASTRecordComponent getAccessedRecordComponent() {
99
        if (getArity() != 0) {
×
100
            return null;
×
101
        }
102
        ASTRecordComponentList components = getEnclosingType().getRecordComponents();
×
103
        if (components == null) {
×
104
            return null;
×
105
        }
106

107
        return components.toStream().first(it -> it.getVarId().getName().equals(this.getName()));
×
108
    }
109

110

111
    /**
112
     * Returns true if the result type of this method is {@code void}.
113
     */
114
    public boolean isVoid() {
115
        return getResultTypeNode().isVoid();
1✔
116
    }
117

118
    /**
119
     * Returns the default clause, if this is an annotation method declaration
120
     * that features one. Otherwise returns null.
121
     */
122
    public @Nullable ASTDefaultValue getDefaultClause() {
123
        return AstImplUtil.getChildAs(this, getNumChildren() - 1, ASTDefaultValue.class);
1✔
124
    }
125

126
    /**
127
     * Returns the result type node of the method. This may be a {@link ASTVoidType}.
128
     */
129
    public @NonNull ASTType getResultTypeNode() { // TODO rename to getResultType()
130
        return firstChild(ASTType.class);
1✔
131
    }
132

133
    /**
134
     * Returns the extra array dimensions that may be after the
135
     * formal parameters.
136
     */
137
    public @Nullable ASTArrayDimensions getExtraDimensions() {
138
        return children(ASTArrayDimensions.class).first();
1✔
139
    }
140

141
    /**
142
     * Returns whether this is a main method declaration.
143
     *
144
     * @see <a href="https://docs.oracle.com/javase/specs/jls/se25/html/jls-12.html#jls-12.1.4">12.1.4. Invoke a main Method</a>
145
     */
146
    public boolean isMainMethod() {
147
        if (!isMainMethodCandidate()) {
1✔
148
            return false;
1✔
149
        }
150
        if (isStatic()) {
1✔
151
            return getArity() == 1 || hasNoOtherMainMethodSibling(
1✔
152
                m -> m.isStatic() && m.getArity() == 1);
1✔
153
        }
154
        if (getArity() == 1) {
1✔
155
            return hasNoOtherMainMethodSibling(ASTExecutableDeclaration::isStatic);
1✔
156
        }
157
        return hasNoOtherMainMethodSibling(any -> true)
1✔
158
            && !ancestors(ASTClassDeclaration.class).firstOpt()
1✔
159
                    .map(this::hasParametrizedMain).orElse(false);
1✔
160
    }
161

162
    private boolean hasNoOtherMainMethodSibling(Predicate<ASTMethodDeclaration> check) {
163
        return getParent().children(ASTMethodDeclaration.class).toStream().filter(check)
1✔
164
            .noneMatch(m -> m != this && m.isMainMethodCandidate());
1✔
165
    }
166

167
    private boolean isMainMethodCandidate() {
168
        return "main".equals(this.getName())
1✔
169
            && !this.hasModifiers(JModifier.PRIVATE)
1✔
170
            && this.isVoid()
1!
171
            && (this.getArity() == 0
1✔
172
            || this.getArity() == 1 && TypeTestUtil.isExactlyA(String[].class, this.getFormalParameters().get(0)));
1!
173
    }
174

175
    private boolean hasParametrizedMain(ASTClassDeclaration astClassType) {
176
        return astClassType.getTypeMirror().streamMethods(
1✔
177
            m -> "main".equals(m.getSimpleName()) && m.getArity() == 1 && !m.isStatic())
1✔
178
            .anyMatch(m -> TypeTestUtil.isExactlyA(String[].class, m.getFormalParameters().get(0)));
1✔
179
    }
180
}
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