Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Linting - API #2149

Draft
wants to merge 17 commits into
base: feat/prepare-for-lint-take-2
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
9afa6dd
Handy tool for making encoding formats that don't have any forbidden …
nedtwigg May 30, 2024
319fc79
Revert "Handy tool for making encoding formats that don't have any fo…
nedtwigg May 30, 2024
777fd5c
Add a `Lint` class, along with `ShortcutException` for sending them.
nedtwigg May 30, 2024
322d5bf
Add a `lint` method to the core interfaces: `Formatter[Step|Func]`
nedtwigg May 30, 2024
c9f52d5
Pipe linting through the core FormatterStep implementations.
nedtwigg May 30, 2024
b57bf24
Formatter can now capture exceptions per-formatter, rethrows if you d…
nedtwigg May 30, 2024
a94dce9
Pipe the lints through `FenceStep`, preliminary.
nedtwigg May 30, 2024
c224929
Remove all of `FormatExceptionPolicy` except `Strict`, which remains …
nedtwigg May 30, 2024
b01e9e9
Rename `ExceptionPerStep` to `ValuePerStep`, and bring `Formatter` cl…
nedtwigg May 30, 2024
fe92d05
Update `Formatter` and `ValuePerStep` so that something (perhaps null…
nedtwigg May 31, 2024
f3be100
Introduce LintState which efficiently reads lint data from both `form…
nedtwigg May 31, 2024
5c42457
Restore the "legacy" error printing for both `Formatter` and `DirtySt…
nedtwigg May 31, 2024
d467545
Fix spotbugs.
nedtwigg May 31, 2024
700114f
Add Selfie, and configure it to not use triple quote literals.
nedtwigg Jun 4, 2024
ffc911e
Add a way to test for lints, and use that to bring back FenceStepTest…
nedtwigg Jun 4, 2024
c2fe9c6
Merge branch 'feat/prepare-for-lint-take-2' into feat/lint-take-2
nedtwigg Jun 4, 2024
6bbe556
Deal with `DirtyState.Calculation` is gone.
nedtwigg Jun 4, 2024
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 gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,4 @@ VER_JGIT=6.7.0.202309050840-r
VER_JUNIT=5.10.2
VER_ASSERTJ=3.26.0
VER_MOCKITO=5.12.0
VER_SELFIE=2.2.0
17 changes: 12 additions & 5 deletions lib/src/main/java/com/diffplug/spotless/DirtyState.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public boolean didNotConverge() {
return this == didNotConverge;
}

private byte[] canonicalBytes() {
byte[] canonicalBytes() {
if (canonicalBytes == null) {
throw new IllegalStateException("First make sure that {@code !isClean()} and {@code !didNotConverge()}");
}
Expand Down Expand Up @@ -74,7 +74,14 @@ public static DirtyState of(Formatter formatter, File file) throws IOException {
}

public static DirtyState of(Formatter formatter, File file, byte[] rawBytes) {
String raw = new String(rawBytes, formatter.getEncoding());
return of(formatter, file, rawBytes, new String(rawBytes, formatter.getEncoding()));
}

public static DirtyState of(Formatter formatter, File file, byte[] rawBytes, String raw) {
return of(formatter, file, rawBytes, raw, new ValuePerStep<>(formatter));
}

public static DirtyState of(Formatter formatter, File file, byte[] rawBytes, String raw, ValuePerStep<Throwable> exceptionPerStep) {
// check that all characters were encodable
String encodingError = EncodingErrorMsg.msg(raw, rawBytes, formatter.getEncoding());
if (encodingError != null) {
Expand All @@ -84,7 +91,7 @@ public static DirtyState of(Formatter formatter, File file, byte[] rawBytes) {
String rawUnix = LineEnding.toUnix(raw);

// enforce the format
String formattedUnix = formatter.compute(rawUnix, file);
String formattedUnix = formatter.computeWithLint(rawUnix, file, exceptionPerStep);
// convert the line endings if necessary
String formatted = formatter.computeLineEndings(formattedUnix, file);

Expand All @@ -95,13 +102,13 @@ public static DirtyState of(Formatter formatter, File file, byte[] rawBytes) {
}

// F(input) != input, so we'll do a padded check
String doubleFormattedUnix = formatter.compute(formattedUnix, file);
String doubleFormattedUnix = formatter.computeWithLint(formattedUnix, file, exceptionPerStep);
if (doubleFormattedUnix.equals(formattedUnix)) {
// most dirty files are idempotent-dirty, so this is a quick-short circuit for that common case
return new DirtyState(formattedBytes);
}

PaddedCell cell = PaddedCell.check(formatter, file, rawUnix);
PaddedCell cell = PaddedCell.check(formatter, file, rawUnix, exceptionPerStep);
if (!cell.isResolvable()) {
return didNotConverge;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2016-2023 DiffPlug
* Copyright 2016-2024 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -16,8 +16,8 @@
package com.diffplug.spotless;

import java.io.File;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.annotation.Nullable;
Expand All @@ -36,14 +36,24 @@ final class FilterByContentPatternFormatterStep extends DelegateFormatterStep {
public @Nullable String format(String raw, File file) throws Exception {
Objects.requireNonNull(raw, "raw");
Objects.requireNonNull(file, "file");
Matcher matcher = contentPattern.matcher(raw);
if (matcher.find() == (onMatch == OnMatch.INCLUDE)) {
if (contentPattern.matcher(raw).find() == (onMatch == OnMatch.INCLUDE)) {
return delegateStep.format(raw, file);
} else {
return raw;
}
}

@Override
public List<Lint> lint(String raw, File file) throws Exception {
Objects.requireNonNull(raw, "raw");
Objects.requireNonNull(file, "file");
if (contentPattern.matcher(raw).find() == (onMatch == OnMatch.INCLUDE)) {
return delegateStep.lint(raw, file);
} else {
return List.of();
}
}

@Override
public boolean equals(Object o) {
if (this == o) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2016-2022 DiffPlug
* Copyright 2016-2024 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -16,6 +16,7 @@
package com.diffplug.spotless;

import java.io.File;
import java.util.List;
import java.util.Objects;

import javax.annotation.Nullable;
Expand All @@ -39,6 +40,17 @@ final class FilterByFileFormatterStep extends DelegateFormatterStep {
}
}

@Override
public List<Lint> lint(String content, File file) throws Exception {
Objects.requireNonNull(content, "content");
Objects.requireNonNull(file, "file");
if (filter.accept(file)) {
return delegateStep.lint(content, file);
} else {
return List.of();
}
}

@Override
public boolean equals(Object o) {
if (this == o) {
Expand Down
39 changes: 0 additions & 39 deletions lib/src/main/java/com/diffplug/spotless/FormatExceptionPolicy.java

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2016 DiffPlug
* Copyright 2016-2024 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -23,7 +23,7 @@
* A policy for handling exceptions in the format. Any exceptions will
* halt the build except for a specifically excluded path or step.
*/
public class FormatExceptionPolicyStrict extends NoLambda.EqualityBasedOnSerialization implements FormatExceptionPolicy {
public class FormatExceptionPolicyStrict extends NoLambda.EqualityBasedOnSerialization {
private static final long serialVersionUID = 1L;

private final Set<String> excludeSteps = new TreeSet<>();
Expand All @@ -39,18 +39,17 @@ public void excludePath(String relativePath) {
excludePaths.add(Objects.requireNonNull(relativePath));
}

@Override
public void handleError(Throwable e, FormatterStep step, String relativePath) {
Objects.requireNonNull(e, "e");
Objects.requireNonNull(step, "step");
Objects.requireNonNull(relativePath, "relativePath");
if (excludeSteps.contains(step.getName())) {
FormatExceptionPolicyLegacy.warning(e, step, relativePath);
LintPolicy.warning(e, step, relativePath);
} else {
if (excludePaths.contains(relativePath)) {
FormatExceptionPolicyLegacy.warning(e, step, relativePath);
LintPolicy.warning(e, step, relativePath);
} else {
FormatExceptionPolicyLegacy.error(e, step, relativePath);
LintPolicy.error(e, step, relativePath);
throw ThrowingEx.asRuntimeRethrowError(e);
}
}
Expand Down
39 changes: 31 additions & 8 deletions lib/src/main/java/com/diffplug/spotless/Formatter.java
Original file line number Diff line number Diff line change
Expand Up @@ -127,28 +127,51 @@ public String computeLineEndings(String unix, File file) {
* is guaranteed to also have unix line endings.
*/
public String compute(String unix, File file) {
ValuePerStep<Throwable> exceptionPerStep = new ValuePerStep<>(this);
String result = computeWithLint(unix, file, exceptionPerStep);
LintPolicy.legacyBehavior(this, file, exceptionPerStep);
return result;
}

/**
* Returns the result of calling all of the FormatterSteps, while also
* tracking any exceptions which are thrown.
* <p>
* The input must have unix line endings, and the output
* is guaranteed to also have unix line endings.
* <p>
* It doesn't matter what is inside `ValuePerStep`, the value at every index will be overwritten
* when the method returns.
*/
String computeWithLint(String unix, File file, ValuePerStep<Throwable> exceptionPerStep) {
Objects.requireNonNull(unix, "unix");
Objects.requireNonNull(file, "file");

for (FormatterStep step : steps) {
for (int i = 0; i < steps.size(); ++i) {
FormatterStep step = steps.get(i);
Throwable storeForStep;
try {
String formatted = step.format(unix, file);
if (formatted == null) {
// This probably means it was a step that only checks
// for errors and doesn't actually have any fixes.
// No exception was thrown so we can just continue.
storeForStep = LintState.formatStepCausedNoChange();
} else {
// Should already be unix-only, but some steps might misbehave.
unix = LineEnding.toUnix(formatted);
String clean = LineEnding.toUnix(formatted);
if (clean.equals(unix)) {
storeForStep = LintState.formatStepCausedNoChange();
} else {
storeForStep = null;
unix = LineEnding.toUnix(formatted);
}
}
} catch (Throwable e) {
// TODO: this is bad, but it won't matter when add support for linting
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
} else {
throw new RuntimeException(e);
}
// store the exception which was thrown and keep going
storeForStep = e;
}
exceptionPerStep.set(i, storeForStep);
}
return unix;
}
Expand Down
26 changes: 26 additions & 0 deletions lib/src/main/java/com/diffplug/spotless/FormatterFunc.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package com.diffplug.spotless;

import java.io.File;
import java.util.List;
import java.util.Objects;

/**
Expand All @@ -32,6 +33,14 @@ default String apply(String unix, File file) throws Exception {
return apply(unix);
}

/**
* Calculates a list of lints against the given content.
* By default, that's just an throwables thrown by the lint.
*/
default List<Lint> lint(String content, File file) throws Exception {
return List.of();
}

/**
* {@code Function<String, String>} and {@code BiFunction<String, File, String>} whose implementation
* requires a resource which should be released when the function is no longer needed.
Expand Down Expand Up @@ -74,6 +83,14 @@ public String apply(String unix) throws Exception {
@FunctionalInterface
interface ResourceFunc<T extends AutoCloseable> {
String apply(T resource, String unix) throws Exception;

/**
* Calculates a list of lints against the given content.
* By default, that's just an throwables thrown by the lint.
*/
default List<Lint> lint(T resource, String unix) throws Exception {
return List.of();
}
}

/** Creates a {@link FormatterFunc.Closeable} which uses the given resource to execute the format function. */
Expand Down Expand Up @@ -101,6 +118,10 @@ public String apply(String unix) throws Exception {
@FunctionalInterface
interface ResourceFuncNeedsFile<T extends AutoCloseable> {
String apply(T resource, String unix, File file) throws Exception;

default List<Lint> lint(T resource, String content, File file) throws Exception {
return List.of();
}
}

/** Creates a {@link FormatterFunc.Closeable} which uses the given resource to execute the file-dependent format function. */
Expand All @@ -123,6 +144,11 @@ public String apply(String unix, File file) throws Exception {
public String apply(String unix) throws Exception {
return apply(unix, Formatter.NO_FILE_SENTINEL);
}

@Override
public List<Lint> lint(String content, File file) throws Exception {
return function.lint(resource, content, file);
}
};
}
}
Expand Down
17 changes: 17 additions & 0 deletions lib/src/main/java/com/diffplug/spotless/FormatterStep.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import java.io.File;
import java.io.Serializable;
import java.util.List;
import java.util.Objects;

import javax.annotation.Nullable;
Expand Down Expand Up @@ -46,6 +47,22 @@ public interface FormatterStep extends Serializable, AutoCloseable {
@Nullable
String format(String rawUnix, File file) throws Exception;

/**
* Returns a list of lints against the given file content
*
* @param content
* the content to check
* @param file
* the file which {@code content} was obtained from; never null. Pass an empty file using
* {@code new File("")} if and only if no file is actually associated with {@code content}
* @return a list of lints
* @throws Exception if the formatter step experiences a problem
*/
@Nullable
default List<Lint> lint(String content, File file) throws Exception {
return List.of();
}

/**
* Returns a new {@code FormatterStep} which, observing the value of {@code formatIfMatches},
* will only apply, or not, its changes to files which pass the given filter.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import java.io.File;
import java.io.Serializable;
import java.util.Arrays;
import java.util.List;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;

Expand Down Expand Up @@ -48,6 +49,14 @@ public String format(String rawUnix, File file) throws Exception {
return formatter.apply(rawUnix, file);
}

@Override
public List<Lint> lint(String content, File file) throws Exception {
if (formatter == null) {
formatter = stateToFormatter(state());
}
return formatter.lint(content, file);
}

@Override
public boolean equals(Object o) {
if (o == null) {
Expand Down
Loading
Loading