Skip to content

Commit dfca397

Browse files
committed
Rework SpotlessTask to use LintState so that we can know which step is failing and properly use the FormatExceptionPolicyStrict.
1 parent 9af4880 commit dfca397

File tree

8 files changed

+70
-69
lines changed

8 files changed

+70
-69
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ public static DirtyState of(Formatter formatter, File file, byte[] rawBytes, Str
8484
return state;
8585
}
8686

87-
public static DirtyState of(Formatter formatter, File file, byte[] rawBytes, String raw, ValuePerStep<Throwable> exceptionPerStep) {
87+
static DirtyState of(Formatter formatter, File file, byte[] rawBytes, String raw, ValuePerStep<Throwable> exceptionPerStep) {
8888
// check that all characters were encodable
8989
String encodingError = EncodingErrorMsg.msg(raw, rawBytes, formatter.getEncoding());
9090
if (encodingError != null) {

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,16 @@
2727
* to Spotless, then it is by definition.
2828
*/
2929
public final class Lint implements Serializable {
30+
/** Returns a runtime exception which, if thrown, will lint the entire file. */
31+
public static ShortcutException entireFile(String code, String msg) {
32+
return new ShortcutException(Lint.create(code, msg, -1));
33+
}
34+
35+
/** Returns a runtime exception which, if thrown, will lint a specific line. */
36+
public static ShortcutException atLine(int line, String code, String msg) {
37+
return new ShortcutException(Lint.create(code, msg, line));
38+
}
39+
3040
/** Any exception which implements this interface will have its lints extracted and reported cleanly to the user. */
3141
public interface Has {
3242
List<Lint> getLints();

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ public boolean isHasLints() {
4141
return lintsPerStep != null;
4242
}
4343

44+
public boolean isClean() {
45+
return dirtyState.isClean() && !isHasLints();
46+
}
47+
4448
public Map<FormatterStep, List<Lint>> getLints(Formatter formatter) {
4549
if (lintsPerStep == null) {
4650
throw new IllegalStateException("Check `isHasLints` first!");
@@ -137,7 +141,7 @@ public static LintState of(Formatter formatter, File file, byte[] rawBytes) {
137141
List<Lint> lintsForStep;
138142
if (exceptionForLint instanceof Lint.Has) {
139143
lintsForStep = ((Lint.Has) exceptionForLint).getLints();
140-
} else if (exceptionForLint != null) {
144+
} else if (exceptionForLint != null && exceptionForLint != formatStepCausedNoChange()) {
141145
lintsForStep = List.of(Lint.createFromThrowable(step, toLint, exceptionForLint));
142146
} else {
143147
lintsForStep = List.of();
@@ -149,6 +153,13 @@ public static LintState of(Formatter formatter, File file, byte[] rawBytes) {
149153
return new LintState(dirty, lints.indexOfFirstValue() == -1 ? null : lints);
150154
}
151155

156+
/** Returns the DirtyState which corresponds to {@code isClean()}. */
157+
public static LintState clean() {
158+
return isClean;
159+
}
160+
161+
private static final LintState isClean = new LintState(DirtyState.clean(), null);
162+
152163
static Throwable formatStepCausedNoChange() {
153164
return FormatterCausedNoChange.INSTANCE;
154165
}

plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,8 @@ private static void relativizeIfSubdir(List<String> relativePaths, File root, Fi
339339
if (!destPath.startsWith(rootPath)) {
340340
return null;
341341
} else {
342-
return destPath.substring(rootPath.length());
342+
String relativized = destPath.substring(rootPath.length());
343+
return relativized.startsWith("/") ? relativized.substring(1) : relativized;
343344
}
344345
}
345346

plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessTask.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,14 @@ public File getOutputDirectory() {
157157
return outputDirectory;
158158
}
159159

160+
protected File lintDirectory = new File(getProject().getLayout().getBuildDirectory().getAsFile().get(),
161+
"spotless-lint/" + getName());
162+
163+
@OutputDirectory
164+
public File getLintDirectory() {
165+
return lintDirectory;
166+
}
167+
160168
private final ConfigurationCacheHackList stepsInternalRoundtrip = ConfigurationCacheHackList.forRoundtrip();
161169
private final ConfigurationCacheHackList stepsInternalEquality = ConfigurationCacheHackList.forEquality();
162170

plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessTaskImpl.java

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.nio.file.Files;
2121
import java.nio.file.Path;
2222
import java.nio.file.StandardCopyOption;
23+
import java.util.List;
2324

2425
import javax.annotation.Nullable;
2526
import javax.inject.Inject;
@@ -37,8 +38,8 @@
3738

3839
import com.diffplug.common.annotations.VisibleForTesting;
3940
import com.diffplug.common.base.StringPrinter;
40-
import com.diffplug.spotless.DirtyState;
4141
import com.diffplug.spotless.Formatter;
42+
import com.diffplug.spotless.LintState;
4243
import com.diffplug.spotless.extra.GitRatchet;
4344

4445
@CacheableTask
@@ -83,7 +84,7 @@ public void performAction(InputChanges inputs) throws Exception {
8384
for (FileChange fileChange : inputs.getFileChanges(target)) {
8485
File input = fileChange.getFile();
8586
if (fileChange.getChangeType() == ChangeType.REMOVED) {
86-
deletePreviousResult(input);
87+
deletePreviousResults(input);
8788
} else {
8889
if (input.isFile()) {
8990
processInputFile(ratchet, formatter, input);
@@ -95,24 +96,23 @@ public void performAction(InputChanges inputs) throws Exception {
9596

9697
@VisibleForTesting
9798
void processInputFile(@Nullable GitRatchet ratchet, Formatter formatter, File input) throws IOException {
98-
File output = getOutputFile(input);
99+
File output = getOutputFileWithBaseDir(input, outputDirectory);
100+
File lint = getOutputFileWithBaseDir(input, lintDirectory);
99101
getLogger().debug("Applying format to {} and writing to {}", input, output);
100-
DirtyState dirtyState;
102+
LintState lintState;
101103
if (ratchet != null && ratchet.isClean(getProjectDir().get().getAsFile(), getRootTreeSha(), input)) {
102-
dirtyState = DirtyState.clean();
104+
lintState = LintState.clean();
103105
} else {
104106
try {
105-
dirtyState = DirtyState.of(formatter, input);
106-
} catch (IOException e) {
107-
throw new IOException("Issue processing file: " + input, e);
108-
} catch (RuntimeException e) {
107+
lintState = LintState.of(formatter, input);
108+
} catch (Throwable e) {
109109
throw new IllegalArgumentException("Issue processing file: " + input, e);
110110
}
111111
}
112-
if (dirtyState.isClean()) {
112+
if (lintState.getDirtyState().isClean()) {
113113
// Remove previous output if it exists
114114
Files.deleteIfExists(output.toPath());
115-
} else if (dirtyState.didNotConverge()) {
115+
} else if (lintState.getDirtyState().didNotConverge()) {
116116
getLogger().warn("Skipping '{}' because it does not converge. Run {@code spotlessDiagnose} to understand why", input);
117117
} else {
118118
Path parentDir = output.toPath().getParent();
@@ -124,20 +124,32 @@ void processInputFile(@Nullable GitRatchet ratchet, Formatter formatter, File in
124124
Files.copy(input.toPath(), output.toPath(), StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES);
125125

126126
getLogger().info(String.format("Writing clean file: %s", output));
127-
dirtyState.writeCanonicalTo(output);
127+
lintState.getDirtyState().writeCanonicalTo(output);
128+
}
129+
if (lintState.isHasLints()) {
130+
var lints = lintState.getLints(formatter);
131+
var first = lints.entrySet().iterator().next();
132+
getExceptionPolicy().handleError(new Throwable(first.getValue().get(0).toString()), first.getKey(), FormatExtension.relativize(getProjectDir().get().getAsFile(), input));
133+
// Files.createDirectories(lint.toPath().getParent());
134+
// Files.write(lint.toPath(), lintState.asString().getBytes());
135+
} else {
136+
Files.deleteIfExists(lint.toPath());
128137
}
129138
}
130139

131-
private void deletePreviousResult(File input) throws IOException {
132-
File output = getOutputFile(input);
133-
if (output.isDirectory()) {
134-
getFs().delete(d -> d.delete(output));
135-
} else {
136-
Files.deleteIfExists(output.toPath());
140+
private void deletePreviousResults(File input) throws IOException {
141+
File output = getOutputFileWithBaseDir(input, outputDirectory);
142+
File lint = getOutputFileWithBaseDir(input, lintDirectory);
143+
for (File file : List.of(output, lint)) {
144+
if (file.isDirectory()) {
145+
getFs().delete(d -> d.delete(file));
146+
} else {
147+
Files.deleteIfExists(file.toPath());
148+
}
137149
}
138150
}
139151

140-
private File getOutputFile(File input) {
152+
private File getOutputFileWithBaseDir(File input, File baseDir) {
141153
File projectDir = getProjectDir().get().getAsFile();
142154
String outputFileName = FormatExtension.relativize(projectDir, input);
143155
if (outputFileName == null) {
@@ -147,6 +159,6 @@ private File getOutputFile(File input) {
147159
printer.println(" target: " + input.getAbsolutePath());
148160
}));
149161
}
150-
return new File(outputDirectory, outputFileName);
162+
return new File(baseDir, outputFileName);
151163
}
152164
}

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

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,11 @@
2323
import org.assertj.core.api.Assertions;
2424
import org.gradle.testkit.runner.BuildResult;
2525
import org.gradle.testkit.runner.TaskOutcome;
26-
import org.junit.jupiter.api.Disabled;
2726
import org.junit.jupiter.api.Test;
2827

2928
import com.diffplug.common.base.CharMatcher;
3029
import com.diffplug.common.base.Splitter;
3130
import com.diffplug.spotless.LineEnding;
32-
import com.diffplug.spotless.tag.ForLintRefactor;
3331

3432
/** Tests the desired behavior from https://github.com/diffplug/spotless/issues/46. */
3533
class ErrorShouldRethrowTest extends GradleIntegrationHarness {
@@ -45,7 +43,7 @@ private void writeBuild(String... toInsert) throws IOException {
4543
lines.add(" target file('README.md')");
4644
lines.add(" custom 'no swearing', {");
4745
lines.add(" if (it.toLowerCase(Locale.ROOT).contains('fubar')) {");
48-
lines.add(" throw new RuntimeException('No swearing!');");
46+
lines.add(" throw com.diffplug.spotless.Lint.entireFile('swearing', 'No swearing!');");
4947
lines.add(" }");
5048
lines.add(" }");
5149
lines.addAll(Arrays.asList(toInsert));
@@ -62,8 +60,6 @@ void passesIfNoException() throws Exception {
6260
}
6361

6462
@Test
65-
@Disabled
66-
@ForLintRefactor
6763
void anyExceptionShouldFail() throws Exception {
6864
writeBuild(
6965
" } // format",
@@ -72,8 +68,8 @@ void anyExceptionShouldFail() throws Exception {
7268
runWithFailure(
7369
"> Task :spotlessMisc FAILED\n" +
7470
"Step 'no swearing' found problem in 'README.md':\n" +
75-
"No swearing!\n" +
76-
"java.lang.RuntimeException: No swearing!");
71+
"-1: (swearing) No swearing!\n" +
72+
"java.lang.Throwable: -1: (swearing) No swearing!");
7773
}
7874

7975
@Test
@@ -86,9 +82,6 @@ void unlessEnforceCheckIsFalse() throws Exception {
8682
runWithSuccess("> Task :processResources NO-SOURCE");
8783
}
8884

89-
@Disabled
90-
@ForLintRefactor
91-
@Test
9285
void unlessExemptedByStep() throws Exception {
9386
writeBuild(
9487
" ignoreErrorForStep 'no swearing'",
@@ -99,8 +92,6 @@ void unlessExemptedByStep() throws Exception {
9992
"Unable to apply step 'no swearing' to 'README.md'");
10093
}
10194

102-
@Disabled
103-
@ForLintRefactor
10495
@Test
10596
void unlessExemptedByPath() throws Exception {
10697
writeBuild(
@@ -113,8 +104,6 @@ void unlessExemptedByPath() throws Exception {
113104
}
114105

115106
@Test
116-
@Disabled
117-
@ForLintRefactor
118107
void failsIfNeitherStepNorFileExempted() throws Exception {
119108
writeBuild(
120109
" ignoreErrorForStep 'nope'",
@@ -124,8 +113,8 @@ void failsIfNeitherStepNorFileExempted() throws Exception {
124113
setFile("README.md").toContent("This code is fubar.");
125114
runWithFailure("> Task :spotlessMisc FAILED\n" +
126115
"Step 'no swearing' found problem in 'README.md':\n" +
127-
"No swearing!\n" +
128-
"java.lang.RuntimeException: No swearing!");
116+
"-1: (swearing) No swearing!\n" +
117+
"java.lang.Throwable: -1: (swearing) No swearing!");
129118
}
130119

131120
private void runWithSuccess(String expectedToStartWith) throws Exception {

testlib/src/main/java/com/diffplug/spotless/tag/ForLintRefactor.java

Lines changed: 0 additions & 30 deletions
This file was deleted.

0 commit comments

Comments
 (0)