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

pmd / pmd / 23

30 May 2025 04:25PM UTC coverage: 78.377% (-0.2%) from 78.601%
23

push

github

adangel
[core] Add rule to report unnecessary suppression comments/annotations (#5609)

Merge pull request #5609 from oowekyala:new-rule-UnnecessarySuppression

17712 of 23434 branches covered (75.58%)

Branch coverage included in aggregate %.

159 of 328 new or added lines in 22 files covered. (48.48%)

36 existing lines in 4 files now uncovered.

38902 of 48799 relevant lines covered (79.72%)

0.81 hits per line

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

64.66
/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/internal/RuleSets.java
1
/**
2
 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3
 */
4

5
package net.sourceforge.pmd.lang.rule.internal;
6

7
import java.util.ArrayList;
8
import java.util.Collection;
9
import java.util.Collections;
10
import java.util.HashSet;
11
import java.util.Iterator;
12
import java.util.List;
13
import java.util.Set;
14

15
import net.sourceforge.pmd.benchmark.TimeTracker;
16
import net.sourceforge.pmd.benchmark.TimedOperation;
17
import net.sourceforge.pmd.benchmark.TimedOperationCategory;
18
import net.sourceforge.pmd.lang.LanguageProcessorRegistry;
19
import net.sourceforge.pmd.lang.ast.RootNode;
20
import net.sourceforge.pmd.lang.document.TextFile;
21
import net.sourceforge.pmd.lang.rule.InternalApiBridge;
22
import net.sourceforge.pmd.lang.rule.Rule;
23
import net.sourceforge.pmd.lang.rule.RuleReference;
24
import net.sourceforge.pmd.lang.rule.RuleSet;
25
import net.sourceforge.pmd.lang.rule.RuleSet.RuleSetBuilder;
26
import net.sourceforge.pmd.lang.rule.impl.UnnecessaryPmdSuppressionRule;
27
import net.sourceforge.pmd.reporting.FileAnalysisListener;
28
import net.sourceforge.pmd.util.log.PmdReporter;
29

30
/**
31
 * Grouping of Rules per Language in a RuleSet.
32
 *
33
 * @author pieter_van_raemdonck - Application Engineers NV/SA - www.ae.be
34
 */
35
public class RuleSets {
36

37
    private final List<RuleSet> ruleSets;
38

39
    private RuleApplicator ruleApplicator;
40

41
    /**
42
     * Copy constructor. Deep copies RuleSets.
43
     *
44
     * @param ruleSets The RuleSets to copy.
45
     */
46
    public RuleSets(final RuleSets ruleSets) {
1✔
47
        List<RuleSet> rsets = new ArrayList<>();
1✔
48
        for (final RuleSet rs : ruleSets.ruleSets) {
1✔
49
            rsets.add(new RuleSet(rs));
1✔
50
        }
1✔
51
        this.ruleSets = Collections.unmodifiableList(rsets);
1✔
52
    }
1✔
53

54
    public RuleSets(Collection<? extends RuleSet> ruleSets) {
1✔
55
        List<RuleSet> rulesets = new ArrayList<>();
1✔
56
        List<RuleSet> suppressionRules = new ArrayList<>();
1✔
57

58
        /*
59
         Suppression rules are separated because they must be run last.
60
         They are packed into their own rulesets. and added at the end of the ruleset list.
61
         */
62

63
        for (RuleSet ruleSet : ruleSets) {
1✔
64
            RuleSetBuilder noSuppressions = ruleSet.toBuilder();
1✔
65
            RuleSetBuilder onlySuppressions = ruleSet.toBuilder();
1✔
66

67
            noSuppressions.removeIf(rule1 -> followReference(rule1) instanceof UnnecessaryPmdSuppressionRule);
1✔
68
            onlySuppressions.removeIf(rule1 -> !(followReference(rule1) instanceof UnnecessaryPmdSuppressionRule));
1!
69
            rulesets.add(noSuppressions.build());
1✔
70
            suppressionRules.add(onlySuppressions.build());
1✔
71
        }
1✔
72
        rulesets.addAll(suppressionRules);
1✔
73
        this.ruleSets = Collections.unmodifiableList(rulesets);
1✔
74
    }
1✔
75

76
    private static Rule followReference(Rule rule) {
77
        if (rule instanceof RuleReference) {
1!
NEW
78
            return followReference(((RuleReference) rule).getRule());
×
79
        }
80
        return rule;
1✔
81
    }
82

83
    /**
84
     * Public constructor. Add the given rule set.
85
     *
86
     * @param ruleSet the RuleSet
87
     */
88
    public RuleSets(RuleSet ruleSet) {
1✔
89
        this.ruleSets = Collections.singletonList(ruleSet);
1✔
90
    }
1✔
91

92
    public void initializeRules(LanguageProcessorRegistry lpReg, PmdReporter reporter) {
93
        // this is abusing the mutability of RuleSet, will go away eventually.
94
        for (RuleSet rset : ruleSets) {
1✔
95
            for (Iterator<Rule> iterator = rset.getRules().iterator(); iterator.hasNext();) {
1✔
96
                Rule rule = iterator.next();
1✔
97
                try {
98
                    rule.initialize(lpReg.getProcessor(rule.getLanguage()));
1✔
99
                } catch (Exception e) {
1✔
100
                    reporter.errorEx(
1✔
101
                        "Exception while initializing rule " + rule.getName() + ", the rule will not be run", e);
1✔
102
                    iterator.remove();
1✔
103
                }
1✔
104
            }
1✔
105
        }
1✔
106
    }
1✔
107

108
    private RuleApplicator prepareApplicator() {
109
        return RuleApplicator.build(ruleSets.stream().flatMap(it -> it.getRules().stream())::iterator);
1✔
110
    }
111

112
    /**
113
     * Get all the RuleSets.
114
     *
115
     * @return RuleSet[]
116
     */
117
    public RuleSet[] getAllRuleSets() {
118
        return ruleSets.toArray(new RuleSet[0]);
×
119
    }
120

121
    // internal
122
    List<RuleSet> getRuleSetsInternal() {
123
        return ruleSets;
×
124
    }
125

126
    public Iterator<RuleSet> getRuleSetsIterator() {
127
        return ruleSets.iterator();
×
128
    }
129

130
    /**
131
     * Return all rules from all rulesets.
132
     *
133
     * @return Set
134
     */
135
    public Set<Rule> getAllRules() {
136
        Set<Rule> result = new HashSet<>();
×
137
        for (RuleSet r : ruleSets) {
×
138
            result.addAll(r.getRules());
×
139
        }
×
140
        return result;
×
141
    }
142

143
    /**
144
     * Check if a given source file should be checked by rules in this RuleSets.
145
     *
146
     * @param file
147
     *            the source file to check
148
     * @return <code>true</code> if the file should be checked,
149
     *         <code>false</code> otherwise
150
     */
151
    public boolean applies(TextFile file) {
152
        for (RuleSet ruleSet : ruleSets) {
1!
153
            if (InternalApiBridge.ruleSetApplies(ruleSet, file.getFileId())) {
1!
154
                return true;
1✔
155
            }
156
        }
×
157
        return false;
×
158
    }
159

160
    /**
161
     * Apply all applicable rules to the compilation units. Applicable means the
162
     * language of the rules must match the language of the source (@see
163
     * applies).
164
     *
165
     * @param root     the List of compilation units; the type these must have,
166
     *                 depends on the source language
167
     * @param listener Listener that will handle events while analysing.
168
     */
169
    public void apply(RootNode root, FileAnalysisListener listener) {
170
        if (ruleApplicator == null) {
1✔
171
            // initialize here instead of ctor, because some rules properties
172
            // are set after creating the ruleset, and jaxen xpath queries
173
            // initialize their XPath expressions when calling getRuleChainVisits()... fixme
174
            this.ruleApplicator = prepareApplicator();
1✔
175
        }
176

177
        try (TimedOperation ignored = TimeTracker.startOperation(TimedOperationCategory.RULE_AST_INDEXATION)) {
1✔
178
            ruleApplicator.index(root);
1✔
179
        }
180

181
        for (RuleSet ruleSet : ruleSets) {
1✔
182
            if (InternalApiBridge.ruleSetApplies(ruleSet, root.getTextDocument().getFileId())) {
1✔
183
                ruleApplicator.apply(ruleSet.getRules(), listener);
1✔
184
            }
185
        }
1✔
186
    }
1✔
187

188
    /**
189
     * Returns the first Rule found with the given name.
190
     *
191
     * Note: Since we support multiple languages, rule names are not expected to
192
     * be unique within any specific ruleset.
193
     *
194
     * @param ruleName
195
     *            the exact name of the rule to find
196
     * @return the rule or null if not found
197
     */
198
    public Rule getRuleByName(String ruleName) {
199
        Rule rule = null;
×
200
        for (Iterator<RuleSet> i = ruleSets.iterator(); i.hasNext() && rule == null;) {
×
201
            RuleSet ruleSet = i.next();
×
202
            rule = ruleSet.getRuleByName(ruleName);
×
203
        }
×
204
        return rule;
×
205
    }
206

207
    /**
208
     * Determines the total count of rules that are used in all rule sets.
209
     *
210
     * @return the count
211
     */
212
    public int ruleCount() {
213
        int count = 0;
×
214
        for (RuleSet r : ruleSets) {
×
215
            count += r.getRules().size();
×
216
        }
×
217
        return count;
×
218
    }
219

220

221
    /**
222
     * Remove and collect any rules that report problems.
223
     *
224
     * @param collector
225
     */
226
    public void removeDysfunctionalRules(Collection<Rule> collector) {
227
        for (RuleSet ruleSet : ruleSets) {
1✔
228
            ruleSet.removeDysfunctionalRules(collector);
1✔
229
        }
1✔
230
    }
1✔
231

232
    /**
233
     * Retrieves a checksum of the rulesets being used. Any change to any rule
234
     * of any ruleset should trigger a checksum change.
235
     *
236
     * @return The checksum for this ruleset collection.
237
     */
238
    public long getChecksum() {
239
        long checksum = 1;
×
240
        for (final RuleSet ruleSet : ruleSets) {
×
241
            checksum = checksum * 31 + ruleSet.getChecksum();
×
242
        }
×
243
        return checksum;
×
244
    }
245
}
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