Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
* Bump default `google-java-format` version to latest `1.24.0` -> `1.28.0`. ([#2345](https://github.com/diffplug/spotless/pull/2345))
* Bump default `ktlint` version to latest `1.5.0` -> `1.7.1`. ([#2555](https://github.com/diffplug/spotless/pull/2555))
* Bump default `jackson` version to latest `2.19.2` -> `2.20.0`. ([#2606](https://github.com/diffplug/spotless/pull/2606))
* Promote `RemoveWildCardImports` to linter ([#2611](https://github.com/diffplug/spotless/pull/2611))

## [3.3.1] - 2025-07-21
### Fixed
Expand Down
4 changes: 2 additions & 2 deletions lib/src/main/java/com/diffplug/spotless/Lint.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2022-2024 DiffPlug
* Copyright 2022-2025 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -91,7 +91,7 @@ public ShortcutException(Lint... lints) {
private final List<Lint> lints;

ShortcutException(Collection<Lint> lints) {
super(lints.iterator().next().detail);
super(lints.iterator().next().toString());
this.lints = List.copyOf(lints);
}

Expand Down
79 changes: 79 additions & 0 deletions lib/src/main/java/com/diffplug/spotless/generic/LintRegexStep.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Copyright 2016-2025 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.diffplug.spotless.generic;

import static com.diffplug.spotless.Lint.atLine;

import java.io.File;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.regex.Pattern;

import com.diffplug.spotless.FormatterFunc;
import com.diffplug.spotless.FormatterStep;
import com.diffplug.spotless.Lint;

public final class LintRegexStep {
// prevent direct instantiation
private LintRegexStep() {}

public static FormatterStep lint(String name, String regex, String error) {
Objects.requireNonNull(name, "name");
Objects.requireNonNull(regex, "regex");
Objects.requireNonNull(error, "error");
return FormatterStep.createLazy(name,
() -> new State(Pattern.compile(regex, Pattern.UNIX_LINES | Pattern.MULTILINE), error),
State::toLinter);
}

private static final class State implements Serializable {
private static final long serialVersionUID = 1L;

private final Pattern regex;
private final String replacement;

State(Pattern regex, String replacement) {
this.regex = regex;
this.replacement = replacement;
}

FormatterFunc toFormatter() {
return raw -> regex.matcher(raw).replaceAll(replacement);
}

FormatterFunc toLinter() {
return new FormatterFunc() {
@Override
public String apply(String raw) {
return raw;
}

@Override
public List<Lint> lint(String raw, File file) {
List<Lint> lints = new ArrayList<>();
var matcher = regex.matcher(raw);
while (matcher.find()) {
int line = 1 + (int) raw.codePoints().limit(matcher.start()).filter(c -> c == '\n').count();
lints.add(atLine(line, matcher.group(0), replacement));
}
return lints;
}
};
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,21 @@
package com.diffplug.spotless.java;

import com.diffplug.spotless.FormatterStep;
import com.diffplug.spotless.generic.ReplaceRegexStep;
import com.diffplug.spotless.generic.LintRegexStep;

/** Removes any wildcard import statements. */
public final class RemoveWildcardImportsStep {

/**
* Matches lines like 'import foo.*;' or 'import static foo.*;'.
*/
private static final String REGEX = "(?m)^import\\s+(?:static\\s+)?[^;\\n]*\\*;\\R?";
private static final String NAME = "removeWildcardImports";
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";

private RemoveWildcardImportsStep() {}

public static FormatterStep create() {
// Matches lines like 'import foo.*;' or 'import static foo.*;'.
return ReplaceRegexStep.create(
"removeWildcardImports",
"(?m)^import\\s+(?:static\\s+)?[^;\\n]*\\*;\\R?",
"");
return LintRegexStep.lint(NAME, REGEX, ERROR);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
*/
package com.diffplug.gradle.spotless;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;

import java.io.IOException;

import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -97,8 +100,17 @@ void removeWildCardImports() throws IOException {
"}");

setFile("test.java").toResource("java/removewildcardimports/JavaCodeWildcardsUnformatted.test");
gradleRunner().withArguments("spotlessApply").build();
assertFile("test.java").sameAsResource("java/removewildcardimports/JavaCodeWildcardsFormatted.test");
gradleRunner().withArguments("spotlessApply").buildAndFail();
assertThat(assertThrows(AssertionError.class, () -> assertFile("test.java").sameAsResource("java/removewildcardimports/JavaCodeWildcardsFormatted.test")).getMessage())
.contains("Extra content at line 1:")
.contains("Extra content at line 3:")
.contains("import io.quarkus.maven.dependency.*;")
.contains("import static io.quarkus.vertx.web.Route.HttpMethod.*;")
.contains("import static org.springframework.web.reactive.function.BodyInserters.*;")
.contains("java.util.*")
.contains("java.util.Collections.*")
.doesNotContain("import mylib.Helper;")
.doesNotContain("public class Test {}");
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
*/
package com.diffplug.spotless.maven.java;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;

import org.junit.jupiter.api.Test;

import com.diffplug.spotless.maven.MavenIntegrationHarness;
Expand All @@ -28,6 +31,25 @@ void testRemoveWildcardImports() throws Exception {
String path = "src/main/java/test.java";
setFile(path).toResource("java/removewildcardimports/JavaCodeWildcardsUnformatted.test");
mavenRunner().withArguments("spotless:apply").runNoError();
assertFile(path).sameAsResource("java/removewildcardimports/JavaCodeWildcardsFormatted.test");
assertThat(assertThrows(AssertionError.class, () -> assertFile(path).sameAsResource("java/removewildcardimports/JavaCodeWildcardsFormatted.test")).getMessage())
.contains("Extra content at line 1:")
.contains("Extra content at line 3:")
.contains("import io.quarkus.maven.dependency.*;")
.contains("import static io.quarkus.vertx.web.Route.HttpMethod.*;")
.contains("import static org.springframework.web.reactive.function.BodyInserters.*;")
.contains("java.util.*")
.contains("java.util.Collections.*")
.doesNotContain("import mylib.Helper;")
.doesNotContain("public class Test {}");
}

@Test
void testRemoveWildcardImportsWithNoResult() throws Exception {
writePomWithJavaSteps("<removeWildcardImports/>");

String path = "src/main/java/test.java";
setFile(path).toResource("java/removewildcardimports/JavaCodeNoWildcardsUnformatted.test");
mavenRunner().withArguments("spotless:apply").runNoError();
assertFile(path).sameAsResource("java/removewildcardimports/JavaCodeNoWildcardsFormatted.test");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import java.util.List;
import mylib.Helper;

public class Test {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import java.util.List;
import mylib.Helper;

public class Test {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright 2025 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.diffplug.spotless.generic;

import org.junit.jupiter.api.Test;

import com.diffplug.common.base.StringPrinter;
import com.diffplug.spotless.FormatterStep;
import com.diffplug.spotless.StepHarness;

class LintRegexStepTest {

@Test
void lint() throws Exception {
FormatterStep step = LintRegexStep.lint("regex", "bad", "no bad words");
StepHarness.forStep(step)
.expectLintsOf(StringPrinter.buildStringFromLines(
"bad",
"x bad y",
"ok"))
.toBe("L1 regex(bad) no bad words\nL2 regex(bad) no bad words");
}
}
Loading