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

Add support for linters #1097

Draft
wants to merge 34 commits into
base: feat/cleanup-breaking-changes
Choose a base branch
from
Draft

Conversation

nedtwigg
Copy link
Member

@nedtwigg nedtwigg commented Jan 14, 2022

This is just a proposal, open to other UIs / designs / implementations

This PR adds a Lint pojo, as well as the new method FormatterFunc::lint with the following default implementation:

default List<Lint> lint(String content, File file) throws Exception {
apply(content, file);
return Collections.emptyList();
}

We now split formatting into two parts: the composable String -> String part and the non-composable List<Lint> part. Before this PR, the only way to "lint" was to throw a single exception. In this PR, those exceptions are now caught and transformed into a lint 7a1e72a which has the following implications:

Gradle integration notes

There's an interesting subtlety here specific to the Gradle plugin, but it could also matter for maven if we go further with up-to-date checking. The spotlessJava task generates a folder of "clean" files for each file which is found to be unclean. Here (a25eded) we extend that to also have a folder of "lint" files for each file found to have non-zero lints . However, the lints will be different if the user calls ./gradlew spotlessCheck or instead ./gradlew spotlessApply spotlessCheck. The "clean" content will be the same, but the line numbers and content of the lints will depend on whether apply was called or not, spotlessJava can't know that ahead of time, so for now we just compute the lints twice (89339cd). That's not as expensive as it seems because we can skip the double-compute for clean files, which is almost-always almost-all files.

Open questions

There might be other things to think about too, but these are what is on my mind:

  • should spotlessApply fail if there are any lints?
    • before this PR we failed on the first "lint" / exception, after this PR apply always succeeds
    • I think our goal of "think about formatting less" means that we should sweep lints under the rug until spotlessCheck (we could make applyFailsOnLint = true available as an option)
  • should we support "info/warning/error" levels?
    • I think the answer is no, because that's the job of the step
    • It should be fooLint().level('info').confidence(70), so that each step can handle that
    • At the spotless level, it should just be "lint = failure", suppress with something like dd5165f
spotless {
    suppressLintForPath 'blah.txt'
    suppressLintForStep 'blah'
    suppressLintForCode 'SOME_CODE'
    suppressLintForMsgMatchesRegex 'blah'
    printSuppressedLintsToConsole = false

Moving forward

It would be great to have some draft PRs based on this feat/lint branch that exercise the new lint method. I'm very confident that FormatterStep and FormatterFunc are stable. The uncertain part is what fields Lint will have, and that will be determined by a combination of:

  • what do the linters have to give
  • what do sarif and reviewdog want

This is a big change so it will happen slowly :)

…ly` (the line numbers in a lint will change depending on whether the `apply` already ran or not).
…as formatted as they can be, except for lints.
…as formatted as they can be, except for lints.
…lf, and tries to parse a line number from the error message.
@rougsig
Copy link

rougsig commented Jan 23, 2022

Can be. Or:

May be different tasks can be the Best solution? Like spotlessLint spotlessFormat?

@nedtwigg
Copy link
Member Author

Degrees of freedom are cost (more surface to maintain, more ways for things to go wrong). It's only worth the cost if you get some value out of it.

The magic of spotless imo is that check says why your build failed, and apply fixes that as much as possible, and there's exactly nothing else to say. This simplicity is why I think there should not be any new tasks, and also why I think that Spotless should have no concept of "error/warning/info" - that should be configured per-linter.

@rougsig
Copy link

rougsig commented Feb 9, 2022

What is the plan for this feature? Waiting for me with a detekt PR?

@nedtwigg
Copy link
Member Author

nedtwigg commented Feb 9, 2022

I'm waiting until we have at least two FormatterStep which use this feature, preferably three. Anyone can contribute a step by opening a PR against this feat/lint branch.

@nedtwigg
Copy link
Member Author

A good one to add: https://github.com/openrewrite/rewrite

@nedtwigg
Copy link
Member Author

nedtwigg commented Jan 3, 2023

I have been convinced that this is totally worth doing. There are three reasons:

  1. Surprisingly, it simplifies the existing functionality by making our error handling simpler
  2. It simplifies integration with hybrid fixers/linters like KtLint
  3. It opens up a new class of tool like Spotbugs

I'm not sure if reason 3 is a good idea or not, but reasons 1 and 2 alone make it worthwhile.

In terms of disruption to our stakeholders:

  • end users -> no disruption at all
  • contributors to FormatterSteps -> no change, new abilities if they want, only real problem is merge conflicts from Breaking changes to Spotless' internal testing infrastructure testlib #1443 which needs to happen whether we do lint or not
  • contributors to build plugins -> a little bit of change
  • contributors to IDE plugins -> potentially a fair bit of change

spotless-lib is currently on version 2.x. I'm planning to roll this out in three stages:

The existing PR's have all gone stale (a year old), but I'll be reusing them by force-pushing.

@nedtwigg nedtwigg marked this pull request as draft January 3, 2023 02:39
@jbduncan
Copy link
Member

jbduncan commented Jan 3, 2023

@nedtwigg Haha, I've only just seen your request for me to review this January last year, so sorry about that. Let me know when you're finished with this PR, and I'd be more than happy to review it. :)

@nedtwigg
Copy link
Member Author

nedtwigg commented Jan 3, 2023

Thanks! It'll probably be a week or two before it's ready :)

@jbduncan
Copy link
Member

jbduncan commented Jan 3, 2023

On the subject of OpenRewrite, I've actually used it a bit on a small project with some success. But IIRC it focuses purely on "solving" rather than "yelling", not to mention it already has a Gradle plugin, so it's not clear to me how Spotless linting would help. I don't suppose you can remember why you mentioned it now, can you?

But perhaps an advantage of incorporating OpenRewrite into Spotless would be the speed gained from Spotless's caching mechanism (at least on the Gradle side).

@blacelle
Copy link
Contributor

for now, the limitation from Spotless is that the linter must do source-only analysis

I did not dig into the PR, but maybe this limitation is slightly stricter for now: 'the linter must do source-only analysis, one a per-file basis'. i.e. I suspect the first iteration will not be able to receive a whole Set of source-files in a single batch.

A good one to add: https://github.com/openrewrite/rewrite

OpenRewrite behave (much) better when called on a source-set, than on each individual files.

But IIRC it focuses purely on "solving" rather than "yelling",

I agree. (I integrated it as an underlying engine of CleanThat, which is focused on solving than yelling)

enum FailOnLint { CHECK, CHECK_AND_APPLY, NOFAIL }

About CHECK_AND_APPLY, I feel people are more interested in a APPLY_AND_CHECK (i.e. try fixing, and please yell is there is still outstanding issues).

@nedtwigg nedtwigg mentioned this pull request Mar 11, 2023
@nedtwigg nedtwigg added the pr-archive PRs which are still valid but have gotten stuck for some reason label Feb 12, 2024
@ztlr
Copy link

ztlr commented Mar 20, 2024

Is this PR still relevant for a future release although it was labeled as "pr-archive"?
We are highly interested in this since it will fix #287.

@nedtwigg
Copy link
Member Author

This PR is absolutely still relevant to a future release, but it will be a while.

@WillHolbrook
Copy link

Hello I'd be interested in having this feature implemented and released. How would I be able to help contribute to getting this merged? I haven't contributed to open source before so sorry if this is a newbie Q

@nedtwigg
Copy link
Member Author

@WillHolbrook which linter would you like to use? Merging this is blocked on me, but I could use help testing the API with specific steps that need the linter.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
pr-archive PRs which are still valid but have gotten stuck for some reason
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants