diff --git a/lib/src/main/java/com/diffplug/spotless/Lint.java b/lib/src/main/java/com/diffplug/spotless/Lint.java index b48a7bcbd2..b01a072905 100644 --- a/lib/src/main/java/com/diffplug/spotless/Lint.java +++ b/lib/src/main/java/com/diffplug/spotless/Lint.java @@ -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. @@ -91,7 +91,7 @@ public ShortcutException(Lint... lints) { private final List lints; ShortcutException(Collection lints) { - super(lints.iterator().next().detail); + super(lints.iterator().next().toString()); this.lints = List.copyOf(lints); } diff --git a/lib/src/main/java/com/diffplug/spotless/generic/ReplaceRegexStep.java b/lib/src/main/java/com/diffplug/spotless/generic/ReplaceRegexStep.java index 990cf65806..309739b0fc 100644 --- a/lib/src/main/java/com/diffplug/spotless/generic/ReplaceRegexStep.java +++ b/lib/src/main/java/com/diffplug/spotless/generic/ReplaceRegexStep.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 DiffPlug + * 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. @@ -15,12 +15,18 @@ */ 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 ReplaceRegexStep { // prevent direct instantiation @@ -35,6 +41,15 @@ public static FormatterStep create(String name, String regex, String replacement State::toFormatter); } + 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; @@ -49,5 +64,25 @@ private static final class State implements Serializable { 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(String raw, File file) { + List 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; + } + }; + } } } diff --git a/lib/src/main/java/com/diffplug/spotless/java/RemoveWildcardImportsStep.java b/lib/src/main/java/com/diffplug/spotless/java/RemoveWildcardImportsStep.java index ca513b8280..efb6ede93b 100644 --- a/lib/src/main/java/com/diffplug/spotless/java/RemoveWildcardImportsStep.java +++ b/lib/src/main/java/com/diffplug/spotless/java/RemoveWildcardImportsStep.java @@ -20,13 +20,17 @@ /** 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 ReplaceRegexStep.lint(NAME, REGEX, ERROR); } } diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/JavaDefaultTargetTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/JavaDefaultTargetTest.java index 401e8ee330..21e372a822 100644 --- a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/JavaDefaultTargetTest.java +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/JavaDefaultTargetTest.java @@ -97,7 +97,7 @@ void removeWildCardImports() throws IOException { "}"); setFile("test.java").toResource("java/removewildcardimports/JavaCodeWildcardsUnformatted.test"); - gradleRunner().withArguments("spotlessApply").build(); + gradleRunner().withArguments("spotlessApply").buildAndFail(); assertFile("test.java").sameAsResource("java/removewildcardimports/JavaCodeWildcardsFormatted.test"); } diff --git a/testlib/src/main/resources/java/removewildcardimports/JavaCodeNoWildcardsFormatted.test b/testlib/src/main/resources/java/removewildcardimports/JavaCodeNoWildcardsFormatted.test new file mode 100644 index 0000000000..85ee903fc5 --- /dev/null +++ b/testlib/src/main/resources/java/removewildcardimports/JavaCodeNoWildcardsFormatted.test @@ -0,0 +1,4 @@ +import java.util.List; +import mylib.Helper; + +public class Test {} \ No newline at end of file diff --git a/testlib/src/main/resources/java/removewildcardimports/JavaCodeNoWildcardsUnformatted.test b/testlib/src/main/resources/java/removewildcardimports/JavaCodeNoWildcardsUnformatted.test new file mode 100644 index 0000000000..85ee903fc5 --- /dev/null +++ b/testlib/src/main/resources/java/removewildcardimports/JavaCodeNoWildcardsUnformatted.test @@ -0,0 +1,4 @@ +import java.util.List; +import mylib.Helper; + +public class Test {} \ No newline at end of file diff --git a/testlib/src/main/resources/java/removewildcardimports/JavaCodeWildcardsFormatted.test b/testlib/src/main/resources/java/removewildcardimports/JavaCodeWildcardsFormatted.test index 85ee903fc5..74646094ae 100644 --- a/testlib/src/main/resources/java/removewildcardimports/JavaCodeWildcardsFormatted.test +++ b/testlib/src/main/resources/java/removewildcardimports/JavaCodeWildcardsFormatted.test @@ -1,4 +1,9 @@ +import java.util.*; +import static java.util.Collections.*; import java.util.List; import mylib.Helper; +import io.quarkus.maven.dependency.*; +import static io.quarkus.vertx.web.Route.HttpMethod.*; +import static org.springframework.web.reactive.function.BodyInserters.*; public class Test {} \ No newline at end of file diff --git a/testlib/src/test/java/com/diffplug/spotless/generic/ReplaceRegexStepTest.java b/testlib/src/test/java/com/diffplug/spotless/generic/ReplaceRegexStepTest.java new file mode 100644 index 0000000000..909255dfd5 --- /dev/null +++ b/testlib/src/test/java/com/diffplug/spotless/generic/ReplaceRegexStepTest.java @@ -0,0 +1,42 @@ +/* + * 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 ReplaceRegexStepTest { + @Test + void formatter() throws Exception { + FormatterStep step = ReplaceRegexStep.create("replace", "bad", "good"); + StepHarness.forStep(step) + .test("bad bad", "good good"); + } + + @Test + void lint() throws Exception { + FormatterStep step = ReplaceRegexStep.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"); + } +}