Skip to content

Commit c530176

Browse files
authored
allow ReplaceRegexStep to function as a Linter, use that to improve RemoveWildcardImports (#2571)
2 parents 88e598b + 7140dcd commit c530176

File tree

9 files changed

+119
-9
lines changed

9 files changed

+119
-9
lines changed

CHANGES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
2424
### Fixed
2525
* Fix `spaceBeforeSeparator` in Jackson formatter. ([#2103](https://github.com/diffplug/spotless/pull/2103))
2626
* `GitPrePushHookInstaller` uses a lock to run gracefully if it is called many times in parallel. ([#2570](https://github.com/diffplug/spotless/pull/2570))
27+
### Added
28+
* Add a `lint` mode to `ReplaceRegexStep` ([#2571](https://github.com/diffplug/spotless/pull/2571))
2729

2830
## [3.3.1] - 2025-07-21
2931
### Fixed

lib/src/main/java/com/diffplug/spotless/Lint.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2022-2024 DiffPlug
2+
* Copyright 2022-2025 DiffPlug
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -91,7 +91,7 @@ public ShortcutException(Lint... lints) {
9191
private final List<Lint> lints;
9292

9393
ShortcutException(Collection<Lint> lints) {
94-
super(lints.iterator().next().detail);
94+
super(lints.iterator().next().toString());
9595
this.lints = List.copyOf(lints);
9696
}
9797

lib/src/main/java/com/diffplug/spotless/generic/ReplaceRegexStep.java

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016 DiffPlug
2+
* Copyright 2016-2025 DiffPlug
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -15,12 +15,16 @@
1515
*/
1616
package com.diffplug.spotless.generic;
1717

18+
import java.io.File;
1819
import java.io.Serializable;
20+
import java.util.ArrayList;
21+
import java.util.List;
1922
import java.util.Objects;
2023
import java.util.regex.Pattern;
2124

2225
import com.diffplug.spotless.FormatterFunc;
2326
import com.diffplug.spotless.FormatterStep;
27+
import com.diffplug.spotless.Lint;
2428

2529
public final class ReplaceRegexStep {
2630
// prevent direct instantiation
@@ -35,6 +39,15 @@ public static FormatterStep create(String name, String regex, String replacement
3539
State::toFormatter);
3640
}
3741

42+
public static FormatterStep lint(String name, String regex, String lintDetail) {
43+
Objects.requireNonNull(name, "name");
44+
Objects.requireNonNull(regex, "regex");
45+
Objects.requireNonNull(lintDetail, "lintDetail");
46+
return FormatterStep.createLazy(name,
47+
() -> new LintState(Pattern.compile(regex, Pattern.UNIX_LINES | Pattern.MULTILINE), name, lintDetail),
48+
LintState::toLinter);
49+
}
50+
3851
private static final class State implements Serializable {
3952
private static final long serialVersionUID = 1L;
4053

@@ -50,4 +63,38 @@ FormatterFunc toFormatter() {
5063
return raw -> regex.matcher(raw).replaceAll(replacement);
5164
}
5265
}
66+
67+
private static final class LintState implements Serializable {
68+
private static final long serialVersionUID = 1L;
69+
70+
private final Pattern regex;
71+
private final String ruleId;
72+
private final String lintDetail;
73+
74+
LintState(Pattern regex, String ruleId, String lintDetail) {
75+
this.regex = regex;
76+
this.ruleId = ruleId;
77+
this.lintDetail = lintDetail;
78+
}
79+
80+
FormatterFunc toLinter() {
81+
return new FormatterFunc() {
82+
@Override
83+
public String apply(String raw) {
84+
return raw;
85+
}
86+
87+
@Override
88+
public List<Lint> lint(String raw, File file) {
89+
List<Lint> lints = new ArrayList<>();
90+
var matcher = regex.matcher(raw);
91+
while (matcher.find()) {
92+
int line = 1 + (int) raw.codePoints().limit(matcher.start()).filter(c -> c == '\n').count();
93+
lints.add(Lint.atLine(line, matcher.group(0), lintDetail));
94+
}
95+
return lints;
96+
}
97+
};
98+
}
99+
}
53100
}

lib/src/main/java/com/diffplug/spotless/java/RemoveWildcardImportsStep.java

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,17 @@
2020

2121
/** Removes any wildcard import statements. */
2222
public final class RemoveWildcardImportsStep {
23+
24+
/**
25+
* Matches lines like 'import foo.*;' or 'import static foo.*;'.
26+
*/
27+
private static final String REGEX = "(?m)^import\\s+(?:static\\s+)?[^;\\n]*\\*;\\R?";
28+
private static final String NAME = "removeWildcardImports";
29+
private static final String ERROR = "Do not use wildcard imports (e.g. java.util.*) - replace with specific class imports (e.g. java.util.List) as 'spotlessApply' cannot auto-fix this";
30+
2331
private RemoveWildcardImportsStep() {}
2432

2533
public static FormatterStep create() {
26-
// Matches lines like 'import foo.*;' or 'import static foo.*;'.
27-
return ReplaceRegexStep.create(
28-
"removeWildcardImports",
29-
"(?m)^import\\s+(?:static\\s+)?[^;\\n]*\\*;\\R?",
30-
"");
34+
return ReplaceRegexStep.lint(NAME, REGEX, ERROR);
3135
}
3236
}

plugin-gradle/src/test/java/com/diffplug/gradle/spotless/JavaDefaultTargetTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ void removeWildCardImports() throws IOException {
9797
"}");
9898

9999
setFile("test.java").toResource("java/removewildcardimports/JavaCodeWildcardsUnformatted.test");
100-
gradleRunner().withArguments("spotlessApply").build();
100+
gradleRunner().withArguments("spotlessApply").buildAndFail();
101101
assertFile("test.java").sameAsResource("java/removewildcardimports/JavaCodeWildcardsFormatted.test");
102102
}
103103

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import java.util.List;
2+
import mylib.Helper;
3+
4+
public class Test {}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import java.util.List;
2+
import mylib.Helper;
3+
4+
public class Test {}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1+
import java.util.*;
2+
import static java.util.Collections.*;
13
import java.util.List;
24
import mylib.Helper;
5+
import io.quarkus.maven.dependency.*;
6+
import static io.quarkus.vertx.web.Route.HttpMethod.*;
7+
import static org.springframework.web.reactive.function.BodyInserters.*;
38

49
public class Test {}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright 2025 DiffPlug
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.diffplug.spotless.generic;
17+
18+
import org.junit.jupiter.api.Test;
19+
20+
import com.diffplug.common.base.StringPrinter;
21+
import com.diffplug.spotless.FormatterStep;
22+
import com.diffplug.spotless.StepHarness;
23+
24+
class ReplaceRegexStepTest {
25+
@Test
26+
void formatter() throws Exception {
27+
FormatterStep step = ReplaceRegexStep.create("replace", "bad", "good");
28+
StepHarness.forStep(step)
29+
.test("bad bad", "good good");
30+
}
31+
32+
@Test
33+
void lint() throws Exception {
34+
FormatterStep step = ReplaceRegexStep.lint("stayPositive", "(bad|awful)", "no negative words");
35+
StepHarness.forStep(step)
36+
.expectLintsOf(StringPrinter.buildStringFromLines(
37+
"bad",
38+
"oh boy",
39+
"x awful y",
40+
"yippeee"))
41+
.toBe("L1 stayPositive(bad) no negative words",
42+
"L3 stayPositive(awful) no negative words");
43+
}
44+
}

0 commit comments

Comments
 (0)