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

pmd / pmd / 814

22 Sep 2026 04:26PM UTC coverage: 79.662% (+0.02%) from 79.645%
814

push

github

web-flow
[java] Prepare deprecation of asCtx in java-codestyle (part of #4814) (#6975)

Rewrite all rule visitors in java-codestyle to...
* cast the "Object data" parameter to RuleContext first
* never touch "data" again
* return null instead of data

20055 of 26194 branches covered (76.56%)

Branch coverage included in aggregate %.

255 of 256 new or added lines in 32 files covered. (99.61%)

43251 of 53274 relevant lines covered (81.19%)

0.82 hits per line

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

97.74
/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryModifierRule.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.rule.codestyle;
6

7
import static net.sourceforge.pmd.lang.java.ast.JModifier.ABSTRACT;
8
import static net.sourceforge.pmd.lang.java.ast.JModifier.FINAL;
9
import static net.sourceforge.pmd.lang.java.ast.JModifier.PRIVATE;
10
import static net.sourceforge.pmd.lang.java.ast.JModifier.PUBLIC;
11
import static net.sourceforge.pmd.lang.java.ast.JModifier.STATIC;
12

13
import java.util.EnumSet;
14
import java.util.Set;
15

16
import org.apache.commons.lang3.StringUtils;
17

18
import net.sourceforge.pmd.lang.java.ast.ASTAnnotationTypeDeclaration;
19
import net.sourceforge.pmd.lang.java.ast.ASTClassDeclaration;
20
import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration;
21
import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration;
22
import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration;
23
import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
24
import net.sourceforge.pmd.lang.java.ast.ASTRecordDeclaration;
25
import net.sourceforge.pmd.lang.java.ast.ASTResource;
26
import net.sourceforge.pmd.lang.java.ast.ASTTypeDeclaration;
27
import net.sourceforge.pmd.lang.java.ast.JModifier;
28
import net.sourceforge.pmd.lang.java.ast.JavaNode;
29
import net.sourceforge.pmd.lang.java.ast.ModifierOwner;
30
import net.sourceforge.pmd.lang.java.ast.internal.PrettyPrintingUtil;
31
import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule;
32
import net.sourceforge.pmd.reporting.RuleContext;
33

34

35
public class UnnecessaryModifierRule extends AbstractJavaRulechainRule {
36

37

38
    public UnnecessaryModifierRule() {
39
        super(ASTTypeDeclaration.class,
1 ✔
40
              ASTMethodDeclaration.class,
41
              ASTResource.class,
42
              ASTFieldDeclaration.class,
43
              ASTConstructorDeclaration.class);
44
    }
1 ✔
45

46

47
    private void reportUnnecessaryModifiers(RuleContext ctx, JavaNode node,
48
                                            JModifier unnecessaryModifier, String explanation) {
49
        reportUnnecessaryModifiers(ctx, node, EnumSet.of(unnecessaryModifier), explanation);
1 ✔
50
    }
1 ✔
51

52

53
    private void reportUnnecessaryModifiers(RuleContext ctx, JavaNode node,
54
                                            Set<JModifier> unnecessaryModifiers, String explanation) {
55
        if (unnecessaryModifiers.isEmpty()) {
1 ✔
56
            return;
1 ✔
57
        }
58
        ctx.addViolation(node, formatUnnecessaryModifiers(unnecessaryModifiers),
1 ✔
59
                         PrettyPrintingUtil.getPrintableNodeKind(node),
1 ✔
60
                         PrettyPrintingUtil.getNodeName(node),
1 ✔
61
                         explanation.isEmpty() ? "" : ": " + explanation);
1 !
62
    }
1 ✔
63

64

65
    private String formatUnnecessaryModifiers(Set<JModifier> set) {
66
        // prints in the standard modifier order (sorted by enum constant ordinal),
67
        // regardless of the actual order in which we checked
68
        return (set.size() > 1 ? "s" : "") + " '" + StringUtils.join(set, " ") + "'";
1 ✔
69
    }
70

71

72
    @Override
73
    public Object visit(ASTEnumDeclaration node, Object data) {
74
        RuleContext ctx = (RuleContext) data;
1 ✔
75
        
76
        if (node.hasExplicitModifiers(PUBLIC)) {
1 ✔
77
            checkDeclarationInInterfaceType(ctx, node, EnumSet.of(PUBLIC));
1 ✔
78
        }
79

80
        if (node.hasExplicitModifiers(STATIC)) {
1 ✔
81
            // a static enum
82
            reportUnnecessaryModifiers(ctx, node, STATIC, "nested enums are implicitly static");
1 ✔
83
        }
84

85
        return null;
1 ✔
86
    }
87

88

89
    @Override
90
    public Object visit(ASTAnnotationTypeDeclaration node, Object data) {
91
        RuleContext ctx = (RuleContext) data;
1 ✔
92

93
        if (node.hasExplicitModifiers(ABSTRACT)) {
1 ✔
94
            // may have several violations, with different explanations
95
            reportUnnecessaryModifiers(ctx, node, ABSTRACT, "annotations types are implicitly abstract");
1 ✔
96

97
        }
98

99
        if (!node.isNested()) {
1 ✔
100
            return null;
1 ✔
101
        }
102

103
        checkDeclarationInInterfaceType(ctx, node, EnumSet.of(PUBLIC));
1 ✔
104

105
        if (node.hasExplicitModifiers(STATIC)) {
1 !
106
            // a static annotation
NEW
107
            reportUnnecessaryModifiers(ctx, node, STATIC, "nested annotation types are implicitly static");
×
108
        }
109

110
        return null;
1 ✔
111
    }
112

113
    // also considers annotations, as should ASTTypeDeclaration do
114
    private boolean isParentInterfaceType(ModifierOwner node) {
115
        ASTTypeDeclaration enclosing = node.getEnclosingType();
1 ✔
116
        return enclosing != null && enclosing.isInterface();
1 ✔
117
    }
118

119

120
    @Override
121
    public Object visit(ASTClassDeclaration node, Object data) {
122
        RuleContext ctx = (RuleContext) data;
1 ✔
123
        
124
        if (node.isInterface() && node.hasExplicitModifiers(ABSTRACT)) {
1 ✔
125
            // an abstract interface
126
            reportUnnecessaryModifiers(ctx, node, ABSTRACT, "interface types are implicitly abstract");
1 ✔
127
        }
128

129
        if (!node.isNested()) {
1 ✔
130
            return null;
1 ✔
131
        }
132

133
        checkDeclarationInInterfaceType(ctx, node, EnumSet.of(PUBLIC, STATIC));
1 ✔
134

135
        if (node.hasExplicitModifiers(STATIC) && node.isInterface() && !isParentInterfaceType(node)) {
1 ✔
136
            // a static interface
137
            reportUnnecessaryModifiers(ctx, node, STATIC, "member interfaces are implicitly static");
1 ✔
138
        }
139

140
        return null;
1 ✔
141
    }
142

143
    @Override
144
    public Object visit(final ASTMethodDeclaration node, Object data) {
145
        RuleContext ctx = (RuleContext) data;
1 ✔
146
        
147
        checkDeclarationInInterfaceType(ctx, node, EnumSet.of(PUBLIC, ABSTRACT));
1 ✔
148

149
        if (node.hasExplicitModifiers(FINAL)) {
1 ✔
150
            // If the method is annotated by @SafeVarargs then it's ok
151
            if (!isSafeVarargs(node)) {
1 ✔
152
                if (node.hasModifiers(PRIVATE)) {
1 ✔
153
                    reportUnnecessaryModifiers(ctx, node, FINAL, "private methods cannot be overridden");
1 ✔
154
                } else {
155
                    final ASTTypeDeclaration n = node.getEnclosingType();
1 ✔
156
                    // A final method of an anonymous class / enum constant. Neither can be extended / overridden
157
                    if (n.isAnonymous()) {
1 ✔
158
                        reportUnnecessaryModifiers(ctx, node, FINAL, "an anonymous class cannot be extended");
1 ✔
159
                    } else if (n.isFinal()) {
1 ✔
160
                        // notice: enum types are implicitly final if no enum constant declares a body
161
                        reportUnnecessaryModifiers(ctx, node, FINAL, "the method is already in a final class");
1 ✔
162
                    }
163
                }
164
            }
165
        }
166

167
        return null;
1 ✔
168
    }
169

170
    @Override
171
    public Object visit(final ASTResource node, final Object data) {
172
        RuleContext ctx = (RuleContext) data;
1 ✔
173
        if (!node.isConciseResource() && node.asLocalVariableDeclaration().hasExplicitModifiers(FINAL)) {
1 ✔
174
            reportUnnecessaryModifiers(ctx, node, FINAL, "resource specifications are implicitly final");
1 ✔
175
        }
176
        return null;
1 ✔
177
    }
178

179
    @Override
180
    public Object visit(ASTFieldDeclaration node, Object data) {
181
        RuleContext ctx = (RuleContext) data;
1 ✔
182
        checkDeclarationInInterfaceType(ctx, node, EnumSet.of(PUBLIC, STATIC, FINAL));
1 ✔
183
        return null;
1 ✔
184
    }
185

186
    @Override
187
    public Object visit(ASTConstructorDeclaration node, Object data) {
188
        RuleContext ctx = (RuleContext) data;
1 ✔
189
        if (node.getEnclosingType().isEnum() && node.hasExplicitModifiers(PRIVATE)) {
1 ✔
190
            reportUnnecessaryModifiers(ctx, node, PRIVATE, "enum constructors are implicitly private");
1 ✔
191
        }
192
        return null;
1 ✔
193
    }
194

195
    @Override
196
    public Object visit(ASTRecordDeclaration node, Object data) {
197
        RuleContext ctx = (RuleContext) data;
1 ✔
198
        if (node.hasExplicitModifiers(STATIC)) {
1 ✔
199
            reportUnnecessaryModifiers(ctx, node, STATIC, "records are implicitly static");
1 ✔
200
        }
201
        if (node.hasExplicitModifiers(FINAL)) {
1 ✔
202
            reportUnnecessaryModifiers(ctx, node, FINAL, "records are implicitly final");
1 ✔
203
        }
204
        return null;
1 ✔
205
    }
206

207

208
    private boolean isSafeVarargs(final ASTMethodDeclaration node) {
209
        return node.isAnnotationPresent(SafeVarargs.class.getName());
1 ✔
210
    }
211

212

213
    private void checkDeclarationInInterfaceType(RuleContext ctx, ModifierOwner member, Set<JModifier> unnecessary) {
214
        // third ancestor could be an AllocationExpression
215
        // if this is a method in an anonymous inner class
216
        ASTTypeDeclaration parent = member.getEnclosingType();
1 ✔
217
        if (isParentInterfaceType(member)) {
1 ✔
218
            unnecessary.removeIf(mod -> !member.hasExplicitModifiers(mod));
1 ✔
219

220
            String explanation = "the " + PrettyPrintingUtil.getPrintableNodeKind(member)
1 ✔
221
                + " is declared in an " + PrettyPrintingUtil.getPrintableNodeKind(parent) + " type";
1 ✔
222
            reportUnnecessaryModifiers(ctx, member, unnecessary, explanation);
1 ✔
223
        }
224
    }
1 ✔
225

226
}
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