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

pmd / pmd / 130

06 Sep 2025 12:33PM UTC coverage: 78.533% (+0.04%) from 78.498%
130

push

github

oowekyala
Fix #4770: [java] UnusedFormalParameter should ignore public constructor as same as method (#5994)

Merge branch 'issue-4770-UnusedFormalParameter-should-ignore-public-constructor-as-same-as-method'

17953 of 23697 branches covered (75.76%)

Branch coverage included in aggregate %.

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

27 existing lines in 6 files now uncovered.

39280 of 49181 relevant lines covered (79.87%)

0.81 hits per line

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

69.34
/pmd-test-schema/src/main/java/net/sourceforge/pmd/test/schema/BaseTestParserImpl.java
1
/*
2
 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3
 */
4

5
package net.sourceforge.pmd.test.schema;
6

7
import java.util.ArrayList;
8
import java.util.Collections;
9
import java.util.HashMap;
10
import java.util.HashSet;
11
import java.util.List;
12
import java.util.Map;
13
import java.util.Properties;
14
import java.util.Set;
15
import java.util.stream.Collectors;
16

17
import org.apache.commons.lang3.StringUtils;
18
import org.w3c.dom.Attr;
19
import org.w3c.dom.Document;
20
import org.w3c.dom.Element;
21
import org.w3c.dom.Node;
22
import org.w3c.dom.NodeList;
23

24
import net.sourceforge.pmd.lang.Language;
25
import net.sourceforge.pmd.lang.LanguageRegistry;
26
import net.sourceforge.pmd.lang.LanguageVersion;
27
import net.sourceforge.pmd.lang.document.Chars;
28
import net.sourceforge.pmd.lang.rule.Rule;
29
import net.sourceforge.pmd.properties.PropertyDescriptor;
30
import net.sourceforge.pmd.properties.PropertySource;
31
import net.sourceforge.pmd.test.schema.TestSchemaParser.PmdXmlReporter;
32
import net.sourceforge.pmd.util.StringUtil;
33

34
import com.github.oowekyala.ooxml.DomUtils;
35
import com.github.oowekyala.ooxml.messages.PositionedXmlDoc;
36
import com.github.oowekyala.ooxml.messages.XmlPosition;
37
import com.github.oowekyala.ooxml.messages.XmlPositioner;
38

39
/**
40
 * @author Clément Fournier
41
 */
42
class BaseTestParserImpl {
1✔
43

44
    static class ParserV1 extends BaseTestParserImpl {
1✔
45

46
    }
47

48
    public RuleTestCollection parseDocument(Rule rule, PositionedXmlDoc positionedXmlDoc, PmdXmlReporter err) {
49
        Document doc = positionedXmlDoc.getDocument();
1✔
50
        Element root = doc.getDocumentElement();
1✔
51

52
        Map<String, Element> codeFragments = parseCodeFragments(err, root);
1✔
53

54
        Set<String> usedFragments = new HashSet<>();
1✔
55
        List<Element> testCodes = DomUtils.childrenNamed(root, "test-code");
1✔
56
        RuleTestCollection result = new RuleTestCollection();
1✔
57
        for (int i = 0; i < testCodes.size(); i++) {
1✔
58
            RuleTestDescriptor descriptor = new RuleTestDescriptor(i, rule.deepCopy());
1✔
59

60
            try (PmdXmlReporter errScope = err.newScope()) {
1✔
61
                parseSingleTest(testCodes.get(i), descriptor, codeFragments, usedFragments, positionedXmlDoc.getPositioner(), errScope);
1✔
62
                if (!errScope.hasError()) {
1✔
63
                    result.addTest(descriptor);
1✔
64
                }
65
            }
66
        }
67

68
        codeFragments.keySet().removeAll(usedFragments);
1✔
69
        codeFragments.forEach((id, node) -> err.at(node).warn("Unused code fragment"));
1✔
70

71
        return result;
1✔
72
    }
73

74
    private Map<String, Element> parseCodeFragments(PmdXmlReporter err, Element root) {
75
        Map<String, Element> codeFragments = new HashMap<>();
1✔
76

77
        for (Element node : DomUtils.childrenNamed(root, "code-fragment")) {
1✔
78
            Attr id = getRequiredAttribute("id", node, err);
1✔
79
            if (id == null) {
1!
80
                continue;
×
81
            }
82

83
            Element prev = codeFragments.put(id.getValue(), node);
1✔
84
            if (prev != null) {
1!
85
                err.at(prev).error("Fragment with duplicate id ''{0}'' is ignored", id.getValue());
×
86
            }
87
        }
1✔
88
        return codeFragments;
1✔
89
    }
90

91
    private void parseSingleTest(Element testCode,
92
                                 RuleTestDescriptor descriptor,
93
                                 Map<String, Element> fragments,
94
                                 Set<String> usedFragments,
95
                                 XmlPositioner xmlPositioner,
96
                                 PmdXmlReporter err) {
97
        {
98
            String description = getSingleChildText(testCode, "description", true, err);
1✔
99
            if (description == null) {
1!
100
                return;
×
101
            }
102
            descriptor.setDescription(description.trim());
1✔
103
        }
104

105
        parseBoolAttribute(testCode, "reinitializeRule", true, err, "Attribute 'reinitializeRule' is deprecated and ignored, assumed true");
1✔
106
        parseBoolAttribute(testCode, "useAuxClasspath", true, err, "Attribute 'useAuxClasspath' is deprecated and ignored, assumed true");
1✔
107

108
        boolean disabled = parseBoolAttribute(testCode, "disabled", false, err, null)
1✔
109
                          | !parseBoolAttribute(testCode, "regressionTest", true, err, "Attribute ''regressionTest'' is deprecated, use ''disabled'' with inverted value");
1✔
110

111
        descriptor.setDisabled(disabled);
1✔
112

113

114
        boolean focused = parseBoolAttribute(testCode, "focused", false, err,
1✔
115
                                             "Attribute focused is used, do not forget to remove it when checking in sources");
116

117
        descriptor.setFocused(focused);
1✔
118

119

120
        Properties properties = parseRuleProperties(testCode, descriptor.getRule(), err);
1✔
121
        descriptor.getProperties().putAll(properties);
1✔
122

123
        parseExpectedProblems(testCode, descriptor, err);
1✔
124
        parseExpectedSuppressions(testCode, descriptor, err);
1✔
125

126
        String code = getTestCode(testCode, fragments, usedFragments, err);
1✔
127
        if (code == null) {
1!
128
            return;
×
129
        }
130
        descriptor.setCode(code);
1✔
131

132

133
        LanguageVersion lversion = parseLanguageVersion(testCode, err);
1✔
134
        if (lversion != null) {
1!
135
            descriptor.setLanguageVersion(lversion);
×
136
        }
137

138
        XmlPosition startPosition = xmlPositioner.startPositionOf(testCode);
1✔
139
        descriptor.setLineNumber(startPosition.getLine());
1✔
140
    }
1✔
141

142
    private void parseExpectedProblems(Element testCode, RuleTestDescriptor descriptor, PmdXmlReporter err) {
143
        Node expectedProblemsNode = getSingleChild(testCode, "expected-problems", true, err);
1✔
144
        if (expectedProblemsNode == null) {
1!
145
            return;
×
146
        }
147
        int expectedProblems = Integer.parseInt(parseTextNode(expectedProblemsNode));
1✔
148

149
        List<String> expectedMessages = Collections.emptyList();
1✔
150
        {
151
            Element messagesNode = getSingleChild(testCode, "expected-messages", false, err);
1✔
152
            if (messagesNode != null) {
1!
153
                expectedMessages = new ArrayList<>();
×
154
                List<Element> messageNodes = DomUtils.childrenNamed(messagesNode, "message");
×
155
                if (messageNodes.size() != expectedProblems) {
×
156
                    err.at(expectedProblemsNode).error("Number of ''expected-messages'' ({0}) does not match", messageNodes.size());
×
157
                    return;
×
158
                }
159

160
                for (Node message : messageNodes) {
×
161
                    expectedMessages.add(parseTextNode(message));
×
162
                }
×
163
            }
164
        }
165

166
        List<Integer> expectedLineNumbers = Collections.emptyList();
1✔
167
        List<Integer> expectedEndLineNumbers = Collections.emptyList();
1✔
168
        {
169
            Element lineNumbers = getSingleChild(testCode, "expected-linenumbers", false, err);
1✔
170
            if (lineNumbers != null) {
1✔
171
                expectedLineNumbers = new ArrayList<>();
1✔
172
                expectedEndLineNumbers = new ArrayList<>();
1✔
173
                String[] linenos = parseTextNode(lineNumbers).split(",");
1✔
174
                if (linenos.length != expectedProblems) {
1!
175
                    err.at(expectedProblemsNode).error("Number of ''expected-linenumbers'' ({0}) does not match", linenos.length);
×
176
                    return;
×
177
                }
178
                for (String num : linenos) {
1✔
179
                    if (num.contains("-")) {
1!
180
                        String[] beginAndEnd = num.split("-", 2);
×
181
                        expectedLineNumbers.add(Integer.valueOf(beginAndEnd[0].trim()));
×
182
                        expectedEndLineNumbers.add(Integer.valueOf(beginAndEnd[1].trim()));
×
183
                    } else {
×
184
                        expectedLineNumbers.add(Integer.valueOf(num.trim()));
1✔
185
                    }
186
                }
187
            }
188
        }
189

190
        descriptor.recordExpectedViolations(
1✔
191
            expectedProblems,
192
            expectedLineNumbers,
193
            expectedEndLineNumbers,
194
            expectedMessages
195
        );
196

197
    }
1✔
198

199
    private void parseExpectedSuppressions(Element testCode, RuleTestDescriptor descriptor, PmdXmlReporter err) {
200
        Node expectedProblemsNode = getSingleChild(testCode, "expected-suppressions", false, err);
1✔
201
        if (expectedProblemsNode == null) {
1✔
202
            return;
1✔
203
        }
204

205
        descriptor.createEmptyExpectedSuppression();
1✔
206
        NodeList childNodes = expectedProblemsNode.getChildNodes();
1✔
207
        for (int i = 0; i < childNodes.getLength(); i++) {
1✔
208
            Node item = childNodes.item(i);
1✔
209
            if (!(item instanceof Element)) {
1✔
210
                continue;
1✔
211
            }
212
            Element itemEl = (Element) item;
1✔
213
            if (!"suppressor".equals(itemEl.getLocalName())) {
1!
214
                err.at(itemEl).error("Unexpected node found: {0} - 'suppressor' expected", item.getLocalName());
×
215
            }
216
            Attr line = getRequiredAttribute("line", itemEl, err);
1✔
217
            String suppressor = parseTextNode(itemEl);
1✔
218

219
            if (line != null) {
1!
220
                descriptor.recordExpectedSuppression(Integer.parseInt(line.getValue()), suppressor);
1✔
221
            }
222
        }
223
    }
1✔
224

225
    private String getTestCode(Element testCode, Map<String, Element> fragments, Set<String> usedFragments, PmdXmlReporter err) {
226
        String code = getSingleChildText(testCode, "code", false, err);
1✔
227
        if (code == null) {
1✔
228
            // Should have a coderef
229
            List<Element> coderefs = DomUtils.childrenNamed(testCode, "code-ref");
1✔
230
            if (coderefs.isEmpty()) {
1!
231
                throw new RuntimeException(
×
232
                    "Required tag is missing from the test-xml. Supply either a code or a code-ref tag");
233
            }
234
            Element coderef = coderefs.get(0);
1✔
235
            Attr id = getRequiredAttribute("id", coderef, err);
1✔
236
            if (id == null) {
1!
237
                return null;
×
238
            }
239
            Element fragment = fragments.get(id.getValue());
1✔
240
            if (fragment == null) {
1!
241
                err.at(id).error("Unknown id, known IDs are {0}", fragments.keySet());
×
242
                return null;
×
243
            }
244
            usedFragments.add(id.getValue());
1✔
245
            code = parseTextNodeNoTrim(fragment);
1✔
246
        }
247
        // first trim empty lines at beginning/end, then trim any indentation
248
        code = StringUtil.trimIndent(Chars.wrap(code).trimBlankLines()).toString();
1✔
249
        return code;
1✔
250
    }
251

252
    private LanguageVersion parseLanguageVersion(Element testCode, PmdXmlReporter err) {
253
        Node sourceTypeNode = getSingleChild(testCode, "source-type", false, err);
1✔
254
        if (sourceTypeNode == null) {
1!
255
            return null;
1✔
256
        }
257
        String languageVersionString = parseTextNode(sourceTypeNode);
×
258
        LanguageVersion languageVersion = parseSourceType(languageVersionString);
×
259
        if (languageVersion != null) {
×
260
            return languageVersion;
×
261
        }
262

263
        err.at(sourceTypeNode).error("Unknown language version ''{0}''", languageVersionString);
×
264
        return null;
×
265
    }
266

267
    /** FIXME this is stupid, the language version may be of a different language than the Rule... */
268
    private static LanguageVersion parseSourceType(String languageIdAndVersion) {
269
        final String version;
270
        final String languageId;
271
        if (languageIdAndVersion.contains(" ")) {
×
272
            version = StringUtils.trimToNull(languageIdAndVersion.substring(languageIdAndVersion.lastIndexOf(' ') + 1));
×
273
            languageId = languageIdAndVersion.substring(0, languageIdAndVersion.lastIndexOf(' '));
×
274
        } else {
275
            version = null;
×
276
            languageId = languageIdAndVersion;
×
277
        }
278
        Language language = LanguageRegistry.PMD.getLanguageById(languageId);
×
279
        if (language != null) {
×
280
            if (version == null) {
×
281
                return language.getDefaultVersion();
×
282
            } else {
283
                return language.getVersion(version);
×
284
            }
285
        }
286
        return null;
×
287
    }
288

289
    private Properties parseRuleProperties(Element testCode, PropertySource knownProps, PmdXmlReporter err) {
290
        Properties properties = new Properties();
1✔
291
        for (Element ruleProperty : DomUtils.childrenNamed(testCode, "rule-property")) {
1✔
292
            Node nameAttr = getRequiredAttribute("name", ruleProperty, err);
1✔
293
            if (nameAttr == null) {
1!
294
                continue;
×
295
            }
296
            String propertyName = nameAttr.getNodeValue();
1✔
297
            if (knownProps.getPropertyDescriptor(propertyName) == null) {
1!
298
                String knownNames = knownProps.getPropertyDescriptors().stream().map(PropertyDescriptor::name)
1✔
299
                        .collect(Collectors.joining(", "));
1✔
300
                err.at(nameAttr).error("Unknown property, known property names are {0}", knownNames);
1✔
301
                continue;
1✔
302
            }
303
            properties.setProperty(propertyName, parseTextNode(ruleProperty));
×
304
        }
×
305
        return properties;
1✔
306
    }
307

308
    private Attr getRequiredAttribute(String name, Element ruleProperty, PmdXmlReporter err) {
309
        Attr nameAttr = (Attr) ruleProperty.getAttributes().getNamedItem(name);
1✔
310
        if (nameAttr == null) {
1!
311
            err.at(ruleProperty).error("Missing ''{0}'' attribute", name);
×
312
            return null;
×
313
        }
314
        return nameAttr;
1✔
315
    }
316

317
    private boolean parseBoolAttribute(Element testCode, String attrName, boolean defaultValue, PmdXmlReporter err, String deprecationMessage) {
318
        Attr attrNode = testCode.getAttributeNode(attrName);
1✔
319
        if (attrNode != null) {
1!
320
            // only consider attributes that are "specified". XML Validation will add the default values of
321
            // the defined attributes implicitly. This would lead to deprecation messages for attributes, that
322
            // are not actually used.
323
            if (deprecationMessage != null && attrNode.getSpecified()) {
1✔
324
                err.at(attrNode).warn(deprecationMessage);
1✔
325
            }
326
            return Boolean.parseBoolean(attrNode.getNodeValue());
1✔
327
        }
UNCOV
328
        return defaultValue;
×
329
    }
330

331

332
    private String getSingleChildText(Element parentElm, String nodeName, boolean required, PmdXmlReporter err) {
333
        Node node = getSingleChild(parentElm, nodeName, required, err);
1✔
334
        if (node == null) {
1✔
335
            return null;
1✔
336
        }
337
        return parseTextNodeNoTrim(node);
1✔
338
    }
339

340
    private Element getSingleChild(Element parentElm, String nodeName, boolean required, PmdXmlReporter err) {
341
        List<Element> nodes = DomUtils.childrenNamed(parentElm, nodeName);
1✔
342
        if (nodes.isEmpty()) {
1✔
343
            if (required) {
1!
UNCOV
344
                err.at(parentElm).error("Required child ''{0}'' is missing", nodeName);
×
345
            }
346
            return null;
1✔
347
        } else if (nodes.size() > 1) {
1!
UNCOV
348
            err.at(nodes.get(1)).error("Duplicate tag ''{0}'' is ignored", nodeName);
×
349
        }
350
        return nodes.get(0);
1✔
351
    }
352

353
    private static String parseTextNode(Node exampleNode) {
354
        return parseTextNodeNoTrim(exampleNode).trim();
1✔
355
    }
356

357
    private static String parseTextNodeNoTrim(Node exampleNode) {
358
        StringBuilder buffer = new StringBuilder();
1✔
359
        for (int i = 0; i < exampleNode.getChildNodes().getLength(); i++) {
1✔
360
            Node node = exampleNode.getChildNodes().item(i);
1✔
361
            if (node.getNodeType() == Node.CDATA_SECTION_NODE || node.getNodeType() == Node.TEXT_NODE) {
1!
362
                buffer.append(node.getNodeValue());
1✔
363
            }
364
        }
365
        return buffer.toString();
1✔
366
    }
367

368

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