habit-hooks-java 1.4.0rc2__tar.gz → 1.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {habit_hooks_java-1.4.0rc2 → habit_hooks_java-1.5.0}/PKG-INFO +1 -1
- {habit_hooks_java-1.4.0rc2 → habit_hooks_java-1.5.0}/docs/java-plugin.spec.md +122 -1
- {habit_hooks_java-1.4.0rc2 → habit_hooks_java-1.5.0}/pyproject.toml +1 -1
- {habit_hooks_java-1.4.0rc2 → habit_hooks_java-1.5.0}/src/habit_hooks_java/sensors/pmd-ruleset.xml +3 -0
- {habit_hooks_java-1.4.0rc2 → habit_hooks_java-1.5.0}/src/habit_hooks_java/sensors/pmd_sensor.py +1 -3
- habit_hooks_java-1.5.0/tests/test_bundled_ruleset_declares_its_contract.py +33 -0
- {habit_hooks_java-1.4.0rc2 → habit_hooks_java-1.5.0}/.gitignore +0 -0
- {habit_hooks_java-1.4.0rc2 → habit_hooks_java-1.5.0}/README.md +0 -0
- {habit_hooks_java-1.4.0rc2 → habit_hooks_java-1.5.0}/src/habit_hooks_java/__init__.py +0 -0
- {habit_hooks_java-1.4.0rc2 → habit_hooks_java-1.5.0}/src/habit_hooks_java/config.toml +0 -0
- {habit_hooks_java-1.4.0rc2 → habit_hooks_java-1.5.0}/src/habit_hooks_java/sensors/pmd.toml +0 -0
- {habit_hooks_java-1.4.0rc2 → habit_hooks_java-1.5.0}/src/habit_hooks_java/sensors/pmd_ruleset.py +0 -0
- {habit_hooks_java-1.4.0rc2 → habit_hooks_java-1.5.0}/tests/conftest.py +0 -0
- {habit_hooks_java-1.4.0rc2 → habit_hooks_java-1.5.0}/tests/test_a_vendored_sensor_finds_its_neighbour.py +0 -0
- {habit_hooks_java-1.4.0rc2 → habit_hooks_java-1.5.0}/tests/test_class_level_metric_violations_are_dropped.py +0 -0
- {habit_hooks_java-1.4.0rc2 → habit_hooks_java-1.5.0}/tests/test_every_ruleset_spelling_wins.py +0 -0
- {habit_hooks_java-1.4.0rc2 → habit_hooks_java-1.5.0}/tests/test_sensor_args_reach_pmd_not_the_file_list.py +0 -0
- {habit_hooks_java-1.4.0rc2 → habit_hooks_java-1.5.0}/tests/test_the_java_files_leave_build_output_alone.py +0 -0
- {habit_hooks_java-1.4.0rc2 → habit_hooks_java-1.5.0}/tests/test_the_sensor_runs_the_pmd_it_is_handed.py +0 -0
|
@@ -66,6 +66,71 @@ habit-sensors --all | jq 'sort_by(.smell)[] | {smell, language, key: (.issues[0]
|
|
|
66
66
|
}
|
|
67
67
|
```
|
|
68
68
|
|
|
69
|
+
## pmd sensor reports the third nested if from the bundled ruleset
|
|
70
|
+
|
|
71
|
+
With no PMD ruleset in the project, the plugin's bundled fallback explicitly
|
|
72
|
+
sets `AvoidDeeplyNestedIfStmts` to report depth 3. The finding points at the
|
|
73
|
+
third `if` and translates the PMD rule to the shared `deep-nesting` smell.
|
|
74
|
+
|
|
75
|
+
📄Nested.java
|
|
76
|
+
```java
|
|
77
|
+
class Nested {
|
|
78
|
+
boolean accepts(int value) {
|
|
79
|
+
if (value > 0) {
|
|
80
|
+
if (value < 10) {
|
|
81
|
+
if (value != 5) {
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
habit-sensors --all | jq '.[] | {smell, language, key: (.issues[0].key | sub(".*/"; "")), line: .issues[0].details.line, source: .issues[0].details.source}'
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
🖥️ ✅
|
|
96
|
+
```json
|
|
97
|
+
{
|
|
98
|
+
"smell": "deep-nesting",
|
|
99
|
+
"language": "java",
|
|
100
|
+
"key": "Nested.java",
|
|
101
|
+
"line": 5,
|
|
102
|
+
"source": "pmd:AvoidDeeplyNestedIfStmts"
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## two nested ifs remain below the bundled threshold
|
|
107
|
+
|
|
108
|
+
The third nested `if` is the first one PMD reports at `problemDepth=3`, so an
|
|
109
|
+
otherwise equivalent two-level chain remains clean.
|
|
110
|
+
|
|
111
|
+
📄Nested.java
|
|
112
|
+
```java
|
|
113
|
+
class Nested {
|
|
114
|
+
boolean accepts(int value) {
|
|
115
|
+
if (value > 0) {
|
|
116
|
+
if (value < 10) {
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
habit-sensors --all | jq '[.[].smell]'
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
🖥️ ✅
|
|
130
|
+
```json
|
|
131
|
+
[]
|
|
132
|
+
```
|
|
133
|
+
|
|
69
134
|
## pmd sensor maps a deeply-branched method to high-complexity
|
|
70
135
|
|
|
71
136
|
A method whose conditions are littered with `||` exceeds PMD's cyclomatic
|
|
@@ -103,7 +168,9 @@ habit-sensors --all | jq '.[] | {smell, language, source: .issues[0].details.sou
|
|
|
103
168
|
PMD never discovers a project ruleset, so the sensor reaches for one only where
|
|
104
169
|
the Java ecosystem conventionally keeps it. A `src/main/resources/pmd/ruleset.xml`
|
|
105
170
|
that lowers the parameter threshold is in force for the run — the bundled
|
|
106
|
-
fallback is only the answer to "this project has none".
|
|
171
|
+
fallback is only the answer to "this project has none". The source also has
|
|
172
|
+
three nested `if`s, but this project ruleset omits `AvoidDeeplyNestedIfStmts`,
|
|
173
|
+
so only the rule the project selected produces a finding.
|
|
107
174
|
|
|
108
175
|
📄src/main/resources/pmd/ruleset.xml
|
|
109
176
|
```xml
|
|
@@ -122,6 +189,13 @@ fallback is only the answer to "this project has none".
|
|
|
122
189
|
```java
|
|
123
190
|
class Project {
|
|
124
191
|
void save(String a, String b) {
|
|
192
|
+
if (a != null) {
|
|
193
|
+
if (b != null) {
|
|
194
|
+
if (!a.equals(b)) {
|
|
195
|
+
System.out.println(a);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
125
199
|
}
|
|
126
200
|
}
|
|
127
201
|
```
|
|
@@ -139,6 +213,53 @@ habit-sensors --all | jq '.[] | {smell, language, key: (.issues[0].key | sub(".*
|
|
|
139
213
|
}
|
|
140
214
|
```
|
|
141
215
|
|
|
216
|
+
## a project ruleset can enable deep nesting at its own threshold
|
|
217
|
+
|
|
218
|
+
A project can opt into the same PMD rule with a different `problemDepth`. Its
|
|
219
|
+
ruleset still replaces the bundled fallback as a whole; the sensor translates
|
|
220
|
+
the enabled rule normally.
|
|
221
|
+
|
|
222
|
+
📄pmd/ruleset.xml
|
|
223
|
+
```xml
|
|
224
|
+
<?xml version="1.0"?>
|
|
225
|
+
<ruleset name="custom" xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
|
|
226
|
+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
227
|
+
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd">
|
|
228
|
+
<description>report the second nested if</description>
|
|
229
|
+
<rule ref="category/java/design.xml/AvoidDeeplyNestedIfStmts">
|
|
230
|
+
<properties><property name="problemDepth" value="2"/></properties>
|
|
231
|
+
</rule>
|
|
232
|
+
</ruleset>
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
📄Nested.java
|
|
236
|
+
```java
|
|
237
|
+
class Nested {
|
|
238
|
+
boolean accepts(int value) {
|
|
239
|
+
if (value > 0) {
|
|
240
|
+
if (value < 10) {
|
|
241
|
+
return true;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
habit-sensors --all | jq '.[] | {smell, language, line: .issues[0].details.line, source: .issues[0].details.source}'
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
🖥️ ✅
|
|
254
|
+
```json
|
|
255
|
+
{
|
|
256
|
+
"smell": "deep-nesting",
|
|
257
|
+
"language": "java",
|
|
258
|
+
"line": 4,
|
|
259
|
+
"source": "pmd:AvoidDeeplyNestedIfStmts"
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
142
263
|
## `[sensors.pmd] args` reaches PMD directly
|
|
143
264
|
|
|
144
265
|
`args` is spliced into the sensor's command as `${args} -- ${files}`, so a real
|
{habit_hooks_java-1.4.0rc2 → habit_hooks_java-1.5.0}/src/habit_hooks_java/sensors/pmd-ruleset.xml
RENAMED
|
@@ -8,6 +8,9 @@
|
|
|
8
8
|
<rule ref="category/java/design.xml/ExcessiveParameterList">
|
|
9
9
|
<properties><property name="minimum" value="4"/></properties>
|
|
10
10
|
</rule>
|
|
11
|
+
<rule ref="category/java/design.xml/AvoidDeeplyNestedIfStmts">
|
|
12
|
+
<properties><property name="problemDepth" value="3"/></properties>
|
|
13
|
+
</rule>
|
|
11
14
|
<rule ref="category/java/design.xml/CyclomaticComplexity">
|
|
12
15
|
<properties><property name="methodReportLevel" value="10"/></properties>
|
|
13
16
|
</rule>
|
{habit_hooks_java-1.4.0rc2 → habit_hooks_java-1.5.0}/src/habit_hooks_java/sensors/pmd_sensor.py
RENAMED
|
@@ -35,6 +35,7 @@ from pathlib import Path
|
|
|
35
35
|
from pmd_ruleset import ruleset_of
|
|
36
36
|
|
|
37
37
|
RULE_SMELLS = {
|
|
38
|
+
"AvoidDeeplyNestedIfStmts": "deep-nesting",
|
|
38
39
|
"ExcessiveParameterList": "too-many-parameters",
|
|
39
40
|
"CyclomaticComplexity": "high-complexity",
|
|
40
41
|
"NcssCount": "oversized-function",
|
|
@@ -131,9 +132,6 @@ def findings(entries: list[dict]) -> list[dict]:
|
|
|
131
132
|
|
|
132
133
|
def main() -> int:
|
|
133
134
|
pmd, argv = sys.argv[1], sys.argv[2:]
|
|
134
|
-
if not argv:
|
|
135
|
-
print("[]")
|
|
136
|
-
return 0
|
|
137
135
|
pmd_args, files = split_argv(argv)
|
|
138
136
|
ruleset, remaining_args = ruleset_of(pmd_args, Path.cwd())
|
|
139
137
|
file_args = [token for file in files for token in ("-d", file)]
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""The bundled PMD fallback pins behavior that PMD's defaults must not move."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import xml.etree.ElementTree as ElementTree
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
RULESET = (
|
|
9
|
+
Path(__file__).resolve().parents[1]
|
|
10
|
+
/ "src"
|
|
11
|
+
/ "habit_hooks_java"
|
|
12
|
+
/ "sensors"
|
|
13
|
+
/ "pmd-ruleset.xml"
|
|
14
|
+
)
|
|
15
|
+
PMD_NAMESPACE = {"pmd": "http://pmd.sourceforge.net/ruleset/2.0.0"}
|
|
16
|
+
DEEP_NESTING_RULE = "category/java/design.xml/AvoidDeeplyNestedIfStmts"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_the_bundled_ruleset_explicitly_reports_the_third_nested_if() -> None:
|
|
20
|
+
root = ElementTree.parse(RULESET).getroot()
|
|
21
|
+
|
|
22
|
+
rules = [
|
|
23
|
+
rule
|
|
24
|
+
for rule in root.findall("pmd:rule", PMD_NAMESPACE)
|
|
25
|
+
if rule.get("ref") == DEEP_NESTING_RULE
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
assert len(rules) == 1
|
|
29
|
+
problem_depth = rules[0].findall(
|
|
30
|
+
"pmd:properties/pmd:property[@name='problemDepth']", PMD_NAMESPACE
|
|
31
|
+
)
|
|
32
|
+
assert len(problem_depth) == 1
|
|
33
|
+
assert problem_depth[0].get("value") == "3"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{habit_hooks_java-1.4.0rc2 → habit_hooks_java-1.5.0}/src/habit_hooks_java/sensors/pmd_ruleset.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{habit_hooks_java-1.4.0rc2 → habit_hooks_java-1.5.0}/tests/test_every_ruleset_spelling_wins.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|