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

Enable error-prone static analysis checks #961

Closed
wants to merge 22 commits into from

Conversation

jbduncan
Copy link
Contributor

@jbduncan jbduncan commented Jul 18, 2017

Overview

Following issue #955, this is a work-in-progress PR to introduce error-prone checks to JUnit 5.

Feedback is welcome!


I hereby agree to the terms of the JUnit Contributor License Agreement.


Definition of Done

@jbduncan
Copy link
Contributor Author

Work on this PR has been postponed until google/error-prone#685 is resolved.

@jbduncan
Copy link
Contributor Author

google/error-prone#685 has been resolved, but it hasn't had time to make it into an error-prone release yet, so I'll see if I can use an earlier version of error-prone.

@smoyer64
Copy link
Contributor

Related to #358 ... and related to the implementation it might actually close that issue.

I don't really care which library is chosen to perform this checking ... at this point FindBugs (and JSR-305 itself) seems dead. Note that JSR-305 has been implemented quite a few times. This is something I intend to ask the JCP executive committee at JavaOne this year.

ErrorProne and SpotBugs both support the JSR-305 proposal, so picking ErrorProne is probably a good decision. There was also a good discussion in JUnit Pioneer about this (junit-pioneer/junit-pioneer#33) and I think that we'll probably just follow the JUnit team's lead.

@talios
Copy link

talios commented Jul 19, 2017

@smoyer64 it seems spotbugs is coming along nicely, and should soon be poised to be a full replacement for findbugs. Findbugs/spotbugs serves a similar, but different purpose as well - so including both might be useful to consider ( along with fb-contrib, find-sec-bugs extensions).

@smoyer64
Copy link
Contributor

@talios - I'm a big fan of find-sec-bugs so I should have specifically mentioned that plugin package. The fb-contrib plugins also look great and I'm thinking I should add these to my parent POM.

@jbduncan
Copy link
Contributor Author

jbduncan commented Jul 19, 2017

@talios I am actually considering Spotbugs as well. I think it or FindBugs would work well with error-prone, since error-prone itself doesn't catch everything that FindBugs does, and vice-versa.

@smoyer64 For null checking, I'll look into the Checker Framework. Its null checking is exhaustive (which is what makes it a formal verification tool as opposed to just a static analysis tool), whereas FindBugs' null checking isn't.

Also, I'm not sure if either of you have seen #961 #955, but there I briefly talk about which tools I want to introduce. Just thought I'd let you both know, FYI. :)

@jbduncan
Copy link
Contributor Author

jbduncan commented Jul 19, 2017

Oh that's a bummer; error-prone doesn't seem to work yet on JDK 9, so Travis is complaining about it.

I'll see if I can find a way in the meantime of disabling error-prone when running on JDK 9.

@jbduncan
Copy link
Contributor Author

@sbrannen @marcphilipp I've made some comments in my recent commits asking about certain things related to error-prone which I'd quite like both your feedback on. Would you mind taking a look?

@jbduncan
Copy link
Contributor Author

@sbrannen @marcphilipp FYI, the reason static was applied to many methods and the parameters in a number of methods were rearranged was to satisfy the checks http://errorprone.info/bugpattern/MethodCanBeStatic and http://errorprone.info/bugpattern/InconsistentOverloads.

If you'd like me to revert these changes because e.g. they're too invasive, please do let me know.

@marcphilipp
Copy link
Member

I'm not a fan of MethodsCanBeStatic to be honest. The description of InconsistentOverloads is very... ehm... concise. What does it try to accomplish?

Which rule caused you to reorder parameters of a lot of methods?

@jbduncan
Copy link
Contributor Author

jbduncan commented Jul 29, 2017

@marcphilipp I agree that the description of InconsistentOverloads is... concise. 😉

IIRC, it reports warnings when two or more methods with the same name (overloads) don't start with the exact same sequence of parameters. For example, this passes without warning because the two overloads' common subset of parameters (a, b, c) have the same names and types, occur at the start of their respective parameter lists, and have the exact same order as each other:

public m(int a, double b, long c) {}
public m(int a, double b, long c, Object d) {}

However this example fails because for the second overload, parameter c is now located after d, breaking at least one of the requirements above:

public m(int a, double b, long c) {}
public m(int a, double b, Object d, long c) {}

So to answer your 2nd question, it was the InconsistentOverloads rule which prompted me to reorder parameters in a lot of methods.

@jbduncan
Copy link
Contributor Author

I'm not a fan of MethodsCanBeStatic to be honest.

Okey dokey! Would you like me to revert the changes I made to satisfy that rule? 😃

@jbduncan
Copy link
Contributor Author

Would you also like me to revert the changes I made for InconsistentOverloads?

@jbduncan
Copy link
Contributor Author

Note, I've just rebased this PR on master and pushed two new commits - one a filename cleanup commit (which was caught by error-prone), the other an attempt to enable error-prone's "Immutable" check.

@jbduncan
Copy link
Contributor Author

The "Immutable" check fails on JDK 9, just like my earlier attempt with the "Var" check. So I've pushed a new commit which disables all checks that depend on error-prone annotations.

@jbduncan
Copy link
Contributor Author

jbduncan commented Aug 7, 2017

@sbrannen I understand that @marcphilipp is on holiday, so I wondered if you could confirm for me in his stead if I should disable and revert the MethodsCanBeStatic and InconsistentOverloads rules discussed above?

@sbrannen
Copy link
Member

I'm actually also on vacation and therefore haven't had a chance to look at this in detail yet.

But I'll see if I can get to it within the next week or so; otherwise, one of us will get back to you after our vacations. 😉

@jbduncan
Copy link
Contributor Author

@sbrannen

I'm actually also on vacation and therefore haven't had a chance to look at this in detail yet.

Oops! Pardon me. For some reason I thought you never went on holiday or finished early, which was why I asked for your confirmation specifically. 😜

Tell you what, I'll go and disable both of those rules as soon as I have the time, since they don't seem to buy much, and I believe disabling both of them would make reviewing this PR much easier. After all, they can always be re-enabled at a later point in time if desired.

@jbduncan
Copy link
Contributor Author

Okay, those two rules and their respective changes have been reverted.

/* no-op */
}
///CLOVER:ON

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you mind moving all of theses private constructors in Assert* classes to a separate PR?

We can then merge that new PR immediately.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Certainly. Should be done and ready for merging over at #1027.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sbrannen Ah, I remember now why I added these private constructors in this PR rather than a separate one in the first place. It's because error-prone purposely fails to compile whenever classes like Assert* don't have associated private constructors.

The error message that error-prone reports for me is as follows:

C:\Users\Jonathan\dev\Java\IntelliJ Projects\junit5\junit-jupiter-api\src\main\java\org\junit\jupiter\api\AssertFalse.java:24: warning: [PrivateConstructorForUtilityClass] Utility classes (only static members) are not designed to be instantiated and should be made noninstantiable with a default constructor.
class AssertFalse {
^
    (see http://errorprone.info/bugpattern/PrivateConstructorForUtilityClass)
  Did you mean to remove this line?

Consequently, Travis CI won't go green until #1027 gets merged into master and I can rebase this PR on top of it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#1027 has been merged.

So feel free to rebase on master at your leisure.

build.gradle Outdated
@@ -9,10 +9,13 @@ buildscript {
}
dependencies {
classpath 'com.diffplug.spotless:spotless-plugin-gradle:3.4.1'
classpath 'com.github.ben-manes:gradle-versions-plugin:0.15.0'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just moved that declaration in e6825ea.

So you can rebase on master at your leisure. 😉

build.gradle Outdated
// disable, similarly to how we specify "-Xlint" flags above.
// Do we want to customise exactly which checks error-prone should run? Or are we happy to just
// accept the default checks that each new release brings, explictly excluding the ones we dislike?
if (JavaVersion.current() == JavaVersion.VERSION_1_8 && subproj.name != 'documentation') {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd say, for now, we can "just accept the default checks that each new release brings, explictly excluding the ones we dislike."

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great, thanks for clarifying things for me @sbrannen! I'll work on reverting this comment now.

}
else {
return String.valueOf(obj);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be an unrelated "fix".

Can you please move this to a separate PR outlining the rationale for the change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please move this to a separate PR outlining the rationale for the change?

See https://github.com/junit-team/junit5/pull/961/files#r134096347 for the rationale. :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done! 👍

static boolean floatsAreEqual(float value1, float value2) {
return Float.floatToIntBits(value1) == Float.floatToIntBits(value2);
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you move this method?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved it because, before I added "-Xep:UngroupedOverloads:OFF" to the build.gradle file, error-prone reported that methods like this one were not 'grouped' together with methods of the same name, potentially causing confusion for future maintainers.

I ultimately added "-Xep:UngroupedOverloads:OFF" to the build.gradle because the corresponding rule produced a lot of churn for little gain, but as I was reverting the changes I made initially to satisfy the rule, I thought that moving all the floatsAreEqual() methods together made a great deal of sense, so I decided to keep it.

If you want me to move it back to where it was, or to put this change in a new PR, please let me know!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can leave it "as is" for the time being.

@@ -128,7 +128,7 @@ void failWithNullStringAndThrowable() {
@Test
void failUsableAsAnExpression() {
// @formatter:off
long count = Collections.emptySet().stream()
long count = Stream.empty()
.peek(element -> fail("peek should never be called"))
.filter(element -> fail("filter should never be called", new Throwable("cause")))
.map(element -> fail(new Throwable("map should never be called")))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in db33cc1.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ta! I'll revert this particular change then.

"Too few test descriptors in classpath");
assertThat(engineDescriptor.getDescendants().size()) //
.as("Too few test descriptors in classpath") //
.isGreaterThan(150);

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in db33cc1.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks again! Reverting this change...

@@ -49,7 +49,7 @@ void privateInnerClassEvaluatesToFalse() {

}

//class name must not end with 'Tests', otherwise it would be picked up by the suite
// Class name must not end with 'Tests', otherwise it would be picked up by the suite
class ClassWithInnerClasses {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in db33cc1.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@@ -50,7 +50,7 @@ void privateNestedClassEvaluatesToFalse() {

}

//class name must not end with 'Tests', otherwise it would be picked up by the suite
// Class name must not end with 'Tests', otherwise it would be picked up by the suite
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in db33cc1.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@@ -13,7 +13,7 @@
class FailAfterAllHelper {

static void fail() {
//hack: use this blacklisted exception to fail the build, all others would be swallowed...
// hack: use this blacklisted exception to fail the build, all others would be swallowed...
throw new OutOfMemoryError("a postcondition was violated");
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in db33cc1.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@sbrannen
Copy link
Member

@jbduncan,

Oops! Pardon me. For some reason I thought you never went on holiday or finished early, which was why I asked for your confirmation specifically. 😜

No worries. 😉

Tell you what, I'll go and disable both of those rules as soon as I have the time, since they don't seem to buy much, and I believe disabling both of them would make reviewing this PR much easier. After all, they can always be re-enabled at a later point in time if desired.

Sounds good.

FYI: I also pushed some of your recommended changes in various "Polishing" commits and requested that you move some of your additional unrelated changes to a separate. So the results of those two actions will also make this PR easier to swallow.

@jbduncan
Copy link
Contributor Author

FYI: I also pushed some of your recommended changes in various "Polishing" commits and requested that you move some of your additional unrelated changes to a separate. So the results of those two actions will also make this PR easier to swallow.

Great! Sounds very reasonable to me, so I'll happily create separate PRs for the corresponding comments and do whatever else needs to be done to cleanup this PR.

I intend to process your comments above as soon as I can, but I'm currently going through a busy time IRL, so it might take me a few days to process everything.

@sbrannen
Copy link
Member

I intend to process your comments above as soon as I can, but I'm currently going through a busy time IRL, so it might take me a few days to process everything.

Sure. No worries, no hurry.

But... it'd be nice to have the polishing commits in place before RC3. 😉

@jbduncan
Copy link
Contributor Author

@marcphilipp @JLLeitschuh Thank you very much for your thoughts on this! I've found that making compile[Test]JavaWithErrorProne depend on compile[Test]Kotlin has fixed the problem I was experiencing.

(FYI, I've been busy with a new job, which is why it took me so long to get back to both of you. I hope you'll accept my apologies.)

@jbduncan
Copy link
Contributor Author

...but it seems I have encountered yet another stumbling block.

Travis, AppVeyor and my local setup seem to complain that they can't find any Java 9 related classes when compiling subproject junit-platform-commons-java-9. I have tried surrounding my error-prone config with the following if statement to disable error-prone for that subproject (as in fe8c123), but it doesn't seem to be working, at least locally.

if (!project.name.endsWith('-java-9')
		// error-prone doesn't work with Java 9 sources yet:
		// https://github.com/google/error-prone/issues/448
		&& subproj.name != 'documentation') {

  apply plugin: 'net.ltgt.errorprone-base'

  [...]

}

Do y'all have any further thoughts on this?

@sormuras
Copy link
Member

Wait until errorprone supports Java 9? ;)

Mh, why are checking project.name ... shouldn't it be subproj.name as well?

@jbduncan
Copy link
Contributor Author

Mh, why are checking project.name ... shouldn't it be subproj.name as well?

Good catch @sormuras!

Sadly, it hasn't worked. :,(

Wait until errorprone supports Java 9? ;)

;)

I'll persevere with this PR for a bit longer, methinks. This camel can carry more straws on its back. ;) (I'll just leave it for another day now, as it's getting late here in the UK and my mind is dozing off.)

If you or @marcphilipp have any further thoughts that may allow me to get unstuck again, I would be immensely grateful.

@jbduncan
Copy link
Contributor Author

It's getting prohibitively difficult for me to continuously rebase this PR over master, and the size of this PR has reached a point where I can't easily debug problems, so I'm closing it with the intention of re-opening it in a new PR.

@jbduncan jbduncan closed this Jan 26, 2018
Andrei94 pushed a commit to Andrei94/junit5 that referenced this pull request Jun 23, 2018
Prior to this commit, AssertionUtils.toString(Object) printed an array via
the array's toString() method which resulted in non-user-friendly output.

This commit addresses this issue by printing arrays using Arrays.toString(),
which produces human readable output.

This is a prerequisite for junit-team#961.

Issue: junit-team#1030
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants